diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6012bb07f..d9c0581ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ name: Build on: push: branches: [mega] - tags-ignore: '**' + tags-ignore: ['**'] paths-ignore: - 'dist/Release_notes.txt' - 'docs/**' @@ -37,7 +37,7 @@ jobs: make html cd .. zip -r -qq ESPEasy_docs.zip docs/build/* - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: Documentation path: ESPEasy_docs.zip @@ -88,7 +88,7 @@ jobs: uses: Entepotenz/change-string-case-action-min-dependencies@v1 with: string: ${{ matrix.chip }} - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: # FIXME Workaround to (temporarily) not use # in the artifact name, see https://github.com/actions/upload-artifact/issues/473 name: Bin-${{ steps.string.outputs.uppercase }}-${{ matrix.env }}-${{ steps.date.outputs.builddate }}_PR_${{ github.event.number }}_${{ github.run_id }} # Sort by ESP type @@ -110,7 +110,7 @@ jobs: run: | echo "builddate=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT - name: Download all artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: artifacts/Binaries/bin/ pattern: Bin-* @@ -119,7 +119,7 @@ jobs: run: | cd artifacts/Binaries/ ls -R - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: # FIXME Workaround to (temporarily) not use # in the artifact name, see https://github.com/actions/upload-artifact/issues/473 name: ESPEasy-all-Binaries-${{ steps.date.outputs.builddate }}_PR_${{ github.event.number }}_${{ github.run_id }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8a70cdf9..e900cde4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,8 @@ name: Release on: push: - branches-ignore: '**' - tags: 'mega-*' + branches-ignore: ['**'] + tags: ['mega-*'] permissions: contents: read @@ -58,7 +58,7 @@ jobs: uses: Entepotenz/change-string-case-action-min-dependencies@v1 with: string: ${{ matrix.chip }} - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: Bin-${{ steps.string.outputs.uppercase }}-${{ matrix.env }} path: | @@ -87,7 +87,7 @@ jobs: cd dist zip -r -qq ../ESPEasy_dist.zip * cd .. - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: name: Distribution path: | @@ -131,13 +131,13 @@ jobs: run: | echo "builddate=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT - name: Download all successfully compiled artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: artifacts/bin/ pattern: Bin-* merge-multiple: true - name: Download dist and docs zip files - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: distribution/ name: Distribution diff --git a/.gitignore b/.gitignore index 08840c545..138e1916b 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,5 @@ src/src/CustomBuild/CompiletimeDefines_generated.h sdkconfig.normal_ESP32solo1_4M316k + +src/Custom_.h diff --git a/README.md b/README.md index 15dca94db..2618dd9e7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,20 @@ For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above. + +# ESPEasy, Automate (using) Common Sense, No AI. + +The main objective for ESPEasy is to make people realize they can easily control appliances and act on sensor data. + +Anyone can use it and you don't need AI. +Only requirement is common sense and appreciate the satisfaction of seeing things just work. + +If you can't build it yourself, you don't 'own' it. +So let's make sure YOU control it. + +We are more than willing to assist via our [forum](https://www.letscontrolit.com/forum). + + # ESPEasy (development branch) diff --git a/boards/esp32c3cdc-16M.json b/boards/esp32c3cdc-16M.json index 338909ca0..293149efd 100644 --- a/boards/esp32c3cdc-16M.json +++ b/boards/esp32c3cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", diff --git a/boards/esp32c3cdc.json b/boards/esp32c3cdc.json index 206367fb9..73fc6ca36 100644 --- a/boards/esp32c3cdc.json +++ b/boards/esp32c3cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", @@ -29,7 +29,6 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, - "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", diff --git a/boards/esp32c5cdc-8M.json b/boards/esp32c5cdc-8M.json index 42ae430a3..ebe4c7fe2 100644 --- a/boards/esp32c5cdc-8M.json +++ b/boards/esp32c5cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c5cdc.json b/boards/esp32c5cdc.json index 312d32556..4459f683f 100644 --- a/boards/esp32c5cdc.json +++ b/boards/esp32c5cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c61cdc-8M.json b/boards/esp32c61cdc-8M.json index be0ed214d..06ff0f88c 100644 --- a/boards/esp32c61cdc-8M.json +++ b/boards/esp32c61cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c61_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-16M.json b/boards/esp32c6cdc-16M.json index efa16120c..1e8c84f17 100644 --- a/boards/esp32c6cdc-16M.json +++ b/boards/esp32c6cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-8M.json b/boards/esp32c6cdc-8M.json index 9bfc4e34c..d33326421 100644 --- a/boards/esp32c6cdc-8M.json +++ b/boards/esp32c6cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc.json b/boards/esp32c6cdc.json index b6615cba4..d5e8e29dd 100644 --- a/boards/esp32c6cdc.json +++ b/boards/esp32c6cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi-16M.json b/boards/esp32s3cdc-qio_opi-16M.json index 22afb4bb5..a66c537fa 100644 --- a/boards/esp32s3cdc-qio_opi-16M.json +++ b/boards/esp32s3cdc-qio_opi-16M.json @@ -9,7 +9,7 @@ "f_flash": "80000000L", "boot": "opi", "flash_mode": "qio", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "hwids": [ [ "0x303A", diff --git a/boards/esp32s3cdc-qio_opi-8M.json b/boards/esp32s3cdc-qio_opi-8M.json index 064cb4019..b24582f91 100644 --- a/boards/esp32s3cdc-qio_opi-8M.json +++ b/boards/esp32s3cdc-qio_opi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi.json b/boards/esp32s3cdc-qio_opi.json index 03943d071..e586ed6a5 100644 --- a/boards/esp32s3cdc-qio_opi.json +++ b/boards/esp32s3cdc-qio_opi.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-16M.json b/boards/esp32s3cdc-qio_qspi-16M.json index e924d3dbd..5b65241ff 100644 --- a/boards/esp32s3cdc-qio_qspi-16M.json +++ b/boards/esp32s3cdc-qio_qspi-16M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-8M.json b/boards/esp32s3cdc-qio_qspi-8M.json index 669a50bb5..62735f885 100644 --- a/boards/esp32s3cdc-qio_qspi-8M.json +++ b/boards/esp32s3cdc-qio_qspi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi.json b/boards/esp32s3cdc-qio_qspi.json index e1ecb7910..293db5750 100644 --- a/boards/esp32s3cdc-qio_qspi.json +++ b/boards/esp32s3cdc-qio_qspi.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/docs/builds_overview.py b/docs/builds_overview.py index a0898d8ac..0f36316bb 100644 --- a/docs/builds_overview.py +++ b/docs/builds_overview.py @@ -4,14 +4,15 @@ # ############################################################################################################# # This script parses all documentation substitution files to determine in what builds a plugin is available -# Collection A..G, Display, Energy and Neopixel, IR and IRext get Normal plugins injected -# Collection, Notify and Network plugins are also injected into Collection A..G +# Collection A..H, Display A..B, Climate A..B, Energy and Neopixel, IR and IRext get Normal plugins injected +# Collection, Notify and Network plugins are also injected into Collection A..H # All plugins get injected into MAX build set # Some build sets have exceptions for plugins not available # The output generation order is determined by how they are ordered in list 'buildColors' # When adding or removing a build set, this script may need adjustments! # Changelog: +# 2026-03-12 tonhuisman: Separate status for ESP32 and ESP8266 (_lb suffix) # 2025-10-05 tonhuisman: Adjustments for Display A and Display B split # 2025-10-01 tonhuisman: Include Notify and Network plugins, ignore not available files, parse NWxxx also # 2024-05-04 tonhuisman: Working and documented @@ -26,6 +27,7 @@ basePath = "source/_templates/" # Gather data allBuilds = {} +allBuilds_lb = {} # Not mentioned as a build in documentation, implicit appendBuilds = {'MAX'} @@ -46,9 +48,10 @@ excludePlugins = { 'CLIMATE A': {'P007', 'P008', 'P009', 'P017', 'P022', 'P027', 'P030', 'P035', 'P040', 'P041', 'P042', 'P045'}, 'CLIMATE B': {'P007', 'P008', 'P009', 'P017', 'P022', 'P027', 'P030', 'P035', 'P040', 'P041', 'P042', 'P045'}, 'DISPLAY A': {'P038', 'P041', 'P042', 'P070'}, - 'DISPLAY B': {'P038', 'P041', 'P042', 'P057', 'P070', 'P075', 'P104'}, + 'DISPLAY B': {'P012', 'P023', 'P038', 'P041', 'P042', 'P057', 'P070', 'P075', 'P104'}, # 'MAX': {''}, # 'NEOPIXEL': {''}, + # 'ENERGY': {''}, 'NORMAL': {'P016', 'P035'}, } @@ -75,13 +78,17 @@ buildColors = { } # Add/update a single plugin in the list -def addOnePlugin(build, plugin, pluginName): +def addOnePlugin(build, plugin, pluginName, with_lb = False): if not build in allBuilds: allBuilds[build] = {} allBuilds[build].update({plugin: pluginName}) + if not build in allBuilds_lb: + allBuilds_lb[build] = {} + if with_lb: + allBuilds_lb[build].update({plugin: pluginName}) # Add a plugin to all builds it should go in -def addToAllBuilds(plugin, pluginName, builds:dict): +def addToAllBuilds(plugin, pluginName, builds:dict, builds_lb:dict): for b in appendBuilds: if not b in builds: builds += {b} @@ -96,7 +103,7 @@ def addToAllBuilds(plugin, pluginName, builds:dict): if plugin in excludePlugins[b]: includeIt = False if includeIt: - addOnePlugin(b, plugin, pluginName) + addOnePlugin(b, plugin, pluginName, b in builds_lb) # Add in other builds too? if b in appendAlso: for n in appendAlso[b]: @@ -105,7 +112,7 @@ def addToAllBuilds(plugin, pluginName, builds:dict): includeIt = False # Except when not to be included if includeIt: - addOnePlugin(n, plugin, pluginName) + addOnePlugin(n, plugin, pluginName, b in builds_lb) # Parse a single substitution file def parseSingleSubstitutionFile(fileName): @@ -118,25 +125,28 @@ def parseSingleSubstitutionFile(fileName): plugin = "" pluginName = "" builds = [] + builds_lb = [] while True: line = pfile.readline() if not line: break - # Parse into label, plugin ID, description and up to 4 separate builds (current max.), + # Parse into label, plugin ID, description and up to 5 separate builds (current max.), # append "(?:[^`]+`([^`]+)`)?" to regex for an extra build, if needed - m = re.search(r"[^|]\|((?:NW|[PCN])(\d{3}))([^\|]+)\|[^`]+`([^`]+)`(?:[^`]+`([^`]+)`)?(?:[^`]+`([^`]+)`)?(?:[^`]+`([^`]+)`)?", line) + m = re.search(r"[^|]\|((?:NW|[PCN])(\d{3}))([^\|]+)\|[^`]+`([^`]+)`(?:[^`]+`([^`]+)`)?(?:[^`]+`([^`]+)`)?(?:[^`]+`([^`]+)`)?(?:[^`]+`([^`]+)`)?", line) if m: if m.group(3) == "_typename": # the typename substitution should be before _status... if plugin != "" and plugin != m.group(1): # Changed plugin ID, store current - addToAllBuilds(plugin, pluginName, builds) + addToAllBuilds(plugin, pluginName, builds, builds_lb) plugin = m.group(1) pluginName = m.group(4) if m.group(3) == "_status": - builds = [m.group(4), m.group(5), m.group(6), m.group(7)] + builds = [m.group(4), m.group(5), m.group(6), m.group(7), m.group(8)] + if m.group(3) == "_status_lb": + builds_lb = [m.group(4), m.group(5), m.group(6), m.group(7), m.group(8)] pfile.close() if plugin != "": # Store last one too - addToAllBuilds(plugin, pluginName, builds) + addToAllBuilds(plugin, pluginName, builds, builds_lb) # Parse all .. include :: files def parseSubstitutionFiles(rootFile): @@ -175,11 +185,11 @@ def generateBuildOverview(fileName): output.write('.. collapse:: Details...\n') output.write('\n') output.write(' .. csv-table::\n') - output.write(' :header: "Plugin name", "Plugin number"\n') - output.write(' :widths: 10, 5\n') + output.write(' :header: "Plugin name", "ESP32", "ESP8266", "Plugin number"\n') + output.write(' :widths: 10, 3, 3, 3\n') output.write('\n') for p in sorted(allBuilds[b], key=sortPluginsBeforeControllers): - output.write(' ":ref:`' + p + '_page`","' + p + '"\n') + output.write(' ":ref:`' + p + '_page`", "✓", "' + ('✓' if p in allBuilds_lb[b] else '') + '", "' + p + '"\n') output.write('\n') output.close() diff --git a/docs/source/Controller/C001.rst b/docs/source/Controller/C001.rst index 1f9c6892d..588bb428a 100644 --- a/docs/source/Controller/C001.rst +++ b/docs/source/Controller/C001.rst @@ -14,7 +14,9 @@ Type: |C001_type| Name: |C001_name| -Status: |C001_status| +Status ESP32: |C001_status| + +Status ESP8266: |C001_status_lb| GitHub: |C001_github|_ diff --git a/docs/source/Controller/C002.rst b/docs/source/Controller/C002.rst index 11439039b..493e87d65 100644 --- a/docs/source/Controller/C002.rst +++ b/docs/source/Controller/C002.rst @@ -14,7 +14,9 @@ Type: |C002_type| Name: |C002_name| -Status: |C002_status| +Status ESP32: |C002_status| + +Status ESP8266: |C002_status_lb| GitHub: |C002_github|_ diff --git a/docs/source/Controller/C003.rst b/docs/source/Controller/C003.rst index 16e3275fa..a7f530050 100644 --- a/docs/source/Controller/C003.rst +++ b/docs/source/Controller/C003.rst @@ -14,7 +14,9 @@ Type: |C003_type| Name: |C003_name| -Status: |C003_status| +Status ESP32: |C003_status| + +Status ESP8266: |C003_status_lb| GitHub: |C003_github|_ diff --git a/docs/source/Controller/C004.rst b/docs/source/Controller/C004.rst index 1b47087df..5ac966299 100644 --- a/docs/source/Controller/C004.rst +++ b/docs/source/Controller/C004.rst @@ -14,7 +14,9 @@ Type: |C004_type| Name: |C004_name| -Status: |C004_status| +Status ESP32: |C004_status| + +Status ESP8266: |C004_status_lb| GitHub: |C004_github|_ diff --git a/docs/source/Controller/C005.rst b/docs/source/Controller/C005.rst index 70aa7e465..b02b8fd05 100644 --- a/docs/source/Controller/C005.rst +++ b/docs/source/Controller/C005.rst @@ -14,7 +14,9 @@ Type: |C005_type| Name: |C005_name| -Status: |C005_status| +Status ESP32: |C005_status| + +Status ESP8266: |C005_status_lb| GitHub: |C005_github|_ diff --git a/docs/source/Controller/C006.rst b/docs/source/Controller/C006.rst index 81810f0eb..126f0d1ba 100644 --- a/docs/source/Controller/C006.rst +++ b/docs/source/Controller/C006.rst @@ -14,7 +14,9 @@ Type: |C006_type| Name: |C006_name| -Status: |C006_status| +Status ESP32: |C006_status| + +Status ESP8266: |C006_status_lb| GitHub: |C006_github|_ diff --git a/docs/source/Controller/C007.rst b/docs/source/Controller/C007.rst index 2ed9463b7..d805b1ffe 100644 --- a/docs/source/Controller/C007.rst +++ b/docs/source/Controller/C007.rst @@ -14,7 +14,9 @@ Type: |C007_type| Name: |C007_name| -Status: |C007_status| +Status ESP32: |C007_status| + +Status ESP8266: |C007_status_lb| GitHub: |C007_github|_ diff --git a/docs/source/Controller/C008.rst b/docs/source/Controller/C008.rst index 7dfeb1a84..221b57940 100644 --- a/docs/source/Controller/C008.rst +++ b/docs/source/Controller/C008.rst @@ -14,7 +14,9 @@ Type: |C008_type| Name: |C008_name| -Status: |C008_status| +Status ESP32: |C008_status| + +Status ESP8266: |C008_status_lb| GitHub: |C008_github|_ diff --git a/docs/source/Controller/C009.rst b/docs/source/Controller/C009.rst index 40f73fd35..e9947f454 100644 --- a/docs/source/Controller/C009.rst +++ b/docs/source/Controller/C009.rst @@ -14,7 +14,9 @@ Type: |C009_type| Name: |C009_name| -Status: |C009_status| +Status ESP32: |C009_status| + +Status ESP8266: |C009_status_lb| GitHub: |C009_github|_ diff --git a/docs/source/Controller/C010.rst b/docs/source/Controller/C010.rst index c55472107..e90482ce6 100644 --- a/docs/source/Controller/C010.rst +++ b/docs/source/Controller/C010.rst @@ -14,7 +14,9 @@ Type: |C010_type| Name: |C010_name| -Status: |C010_status| +Status ESP32: |C010_status| + +Status ESP8266: |C010_status_lb| GitHub: |C010_github|_ diff --git a/docs/source/Controller/C011.rst b/docs/source/Controller/C011.rst index 79d78022a..c8f62ee7e 100644 --- a/docs/source/Controller/C011.rst +++ b/docs/source/Controller/C011.rst @@ -14,7 +14,9 @@ Type: |C011_type| Name: |C011_name| -Status: |C011_status| +Status ESP32: |C011_status| + +Status ESP8266: |C011_status_lb| GitHub: |C011_github|_ diff --git a/docs/source/Controller/C012.rst b/docs/source/Controller/C012.rst index 8c380d7ac..208b85fac 100644 --- a/docs/source/Controller/C012.rst +++ b/docs/source/Controller/C012.rst @@ -14,7 +14,9 @@ Type: |C012_type| Name: |C012_name| -Status: |C012_status| +Status ESP32: |C012_status| + +Status ESP8266: |C012_status_lb| GitHub: |C012_github|_ diff --git a/docs/source/Controller/C013.rst b/docs/source/Controller/C013.rst index 190ecb424..b919112f9 100644 --- a/docs/source/Controller/C013.rst +++ b/docs/source/Controller/C013.rst @@ -14,7 +14,9 @@ Type: |C013_type| Name: |C013_name| -Status: |C013_status| +Status ESP32: |C013_status| + +Status ESP8266: |C013_status_lb| GitHub: |C013_github|_ diff --git a/docs/source/Controller/C014.rst b/docs/source/Controller/C014.rst index d32bbebd7..a40039a8e 100644 --- a/docs/source/Controller/C014.rst +++ b/docs/source/Controller/C014.rst @@ -14,7 +14,9 @@ Type: |C014_type| Name: |C014_name| -Status: |C014_status| +Status ESP32: |C014_status| + +Status ESP8266: |C014_status_lb| GitHub: |C014_github|_ diff --git a/docs/source/Controller/C016.rst b/docs/source/Controller/C016.rst index 40eb6484c..3d4d25368 100644 --- a/docs/source/Controller/C016.rst +++ b/docs/source/Controller/C016.rst @@ -14,7 +14,9 @@ Type: |C016_type| Name: |C016_name| -Status: |C016_status| +Status ESP32: |C016_status| + +Status ESP8266: |C016_status_lb| GitHub: |C016_github|_ diff --git a/docs/source/Controller/C017.rst b/docs/source/Controller/C017.rst index 5d21b1014..b2873d1bf 100644 --- a/docs/source/Controller/C017.rst +++ b/docs/source/Controller/C017.rst @@ -14,7 +14,9 @@ Type: |C017_type| Name: |C017_name| -Status: |C017_status| +Status ESP32: |C017_status| + +Status ESP8266: |C017_status_lb| GitHub: |C017_github|_ diff --git a/docs/source/Controller/C018.rst b/docs/source/Controller/C018.rst index 87834d15c..d53483b64 100644 --- a/docs/source/Controller/C018.rst +++ b/docs/source/Controller/C018.rst @@ -14,7 +14,9 @@ Type: |C018_type| Name: |C018_name| -Status: |C018_status| +Status ESP32: |C018_status| + +Status ESP8266: |C018_status_lb| GitHub: |C018_github|_ diff --git a/docs/source/Controller/_Controller.rst b/docs/source/Controller/_Controller.rst index 05f1d5251..c2fed540a 100644 --- a/docs/source/Controller/_Controller.rst +++ b/docs/source/Controller/_Controller.rst @@ -18,26 +18,26 @@ Controller Plugins ================== .. csv-table:: - :header: "Plugin name", "Plugin status", "Plugin number" - :widths: 10, 8, 5 + :header: "Plugin name", "ESP32 Plugin status", "ESP8266 Plugin status", "Plugin number" + :widths: 9, 6, 6, 3 - ":ref:`C001_page`","|C001_status|","C001" - ":ref:`C002_page`","|C002_status|","C002" - ":ref:`C003_page`","|C003_status|","C003" - ":ref:`C004_page`","|C004_status|","C004" - ":ref:`C005_page`","|C005_status|","C005" - ":ref:`C006_page`","|C006_status|","C006" - ":ref:`C007_page`","|C007_status|","C007" - ":ref:`C008_page`","|C008_status|","C008" - ":ref:`C009_page`","|C009_status|","C009" - ":ref:`C010_page`","|C010_status|","C010" - ":ref:`C011_page`","|C011_status|","C011" - ":ref:`C012_page`","|C012_status|","C012" - ":ref:`C013_page`","|C013_status|","C013" - ":ref:`C014_page`","|C014_status|","C014" - ":ref:`C016_page`","|C016_status|","C016" - ":ref:`C017_page`","|C017_status|","C017" - ":ref:`C018_page`","|C018_status|","C018" + ":ref:`C001_page`","|C001_status|","|C001_status_lb|","C001" + ":ref:`C002_page`","|C002_status|","|C002_status_lb|","C002" + ":ref:`C003_page`","|C003_status|","|C003_status_lb|","C003" + ":ref:`C004_page`","|C004_status|","|C004_status_lb|","C004" + ":ref:`C005_page`","|C005_status|","|C005_status_lb|","C005" + ":ref:`C006_page`","|C006_status|","|C006_status_lb|","C006" + ":ref:`C007_page`","|C007_status|","|C007_status_lb|","C007" + ":ref:`C008_page`","|C008_status|","|C008_status_lb|","C008" + ":ref:`C009_page`","|C009_status|","|C009_status_lb|","C009" + ":ref:`C010_page`","|C010_status|","|C010_status_lb|","C010" + ":ref:`C011_page`","|C011_status|","|C011_status_lb|","C011" + ":ref:`C012_page`","|C012_status|","|C012_status_lb|","C012" + ":ref:`C013_page`","|C013_status|","|C013_status_lb|","C013" + ":ref:`C014_page`","|C014_status|","|C014_status_lb|","C014" + ":ref:`C016_page`","|C016_status|","|C016_status_lb|","C016" + ":ref:`C017_page`","|C017_status|","|C017_status_lb|","C017" + ":ref:`C018_page`","|C018_status|","|C018_status_lb|","C018" Controller Parameters diff --git a/docs/source/Controller/_controller_substitutions.repl b/docs/source/Controller/_controller_substitutions.repl index 0683e988a..49f743ec4 100644 --- a/docs/source/Controller/_controller_substitutions.repl +++ b/docs/source/Controller/_controller_substitutions.repl @@ -5,6 +5,7 @@ .. |C001_type| replace:: :cyan:`Controller` .. |C001_typename| replace:: :cyan:`Controller - Domoticz HTTP` .. |C001_status| replace:: :green:`NORMAL` +.. |C001_status_lb| replace:: :green:`NORMAL` .. |C001_github| replace:: C001.cpp .. _C001_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C001.cpp .. |C001_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |C002_type| replace:: :cyan:`Controller` .. |C002_typename| replace:: :cyan:`Controller - Domoticz MQTT` .. |C002_status| replace:: :green:`NORMAL` +.. |C002_status_lb| replace:: :green:`NORMAL` .. |C002_github| replace:: C002.cpp .. _C002_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C002.cpp .. |C002_usedby| replace:: `.` @@ -27,6 +29,7 @@ .. |C003_type| replace:: :cyan:`Controller` .. |C003_typename| replace:: :cyan:`Controller - Nodo Telnet` .. |C003_status| replace:: :green:`NORMAL` +.. |C003_status_lb| replace:: :green:`NORMAL` .. |C003_github| replace:: C003.cpp .. _C003_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C003.cpp .. |C003_usedby| replace:: `.` @@ -38,6 +41,7 @@ .. |C004_type| replace:: :cyan:`Controller` .. |C004_typename| replace:: :cyan:`Controller - ThingSpeak` .. |C004_status| replace:: :green:`NORMAL` +.. |C004_status_lb| replace:: :green:`NORMAL` .. |C004_github| replace:: C004.cpp .. _C004_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C004.cpp .. |C004_usedby| replace:: `.` @@ -49,6 +53,7 @@ .. |C005_type| replace:: :cyan:`Controller` .. |C005_typename| replace:: :cyan:`Controller - Home Assistant (openHAB) MQTT` .. |C005_status| replace:: :green:`NORMAL` +.. |C005_status_lb| replace:: :green:`NORMAL` .. |C005_github| replace:: C005.cpp .. _C005_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C005.cpp .. |C005_usedby| replace:: `.` @@ -60,6 +65,7 @@ .. |C006_type| replace:: :cyan:`Controller` .. |C006_typename| replace:: :cyan:`Controller - PiDome MQTT` .. |C006_status| replace:: :green:`NORMAL` +.. |C006_status_lb| replace:: :green:`NORMAL` .. |C006_github| replace:: C006.cpp .. _C006_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C006.cpp .. |C006_usedby| replace:: `.` @@ -71,6 +77,7 @@ .. |C007_type| replace:: :cyan:`Controller` .. |C007_typename| replace:: :cyan:`Controller - Emoncms` .. |C007_status| replace:: :green:`NORMAL` +.. |C007_status_lb| replace:: :green:`NORMAL` .. |C007_github| replace:: C007.cpp .. _C007_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C007.cpp .. |C007_usedby| replace:: `.` @@ -82,6 +89,7 @@ .. |C008_type| replace:: :cyan:`Controller` .. |C008_typename| replace:: :cyan:`Controller - Generic HTTP` .. |C008_status| replace:: :green:`NORMAL` +.. |C008_status_lb| replace:: :green:`NORMAL` .. |C008_github| replace:: C008.cpp .. _C008_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C008.cpp .. |C008_usedby| replace:: `.` @@ -93,6 +101,7 @@ .. |C009_type| replace:: :cyan:`Controller` .. |C009_typename| replace:: :cyan:`Controller - FHEM HTTP` .. |C009_status| replace:: :green:`NORMAL` +.. |C009_status_lb| replace:: :green:`NORMAL` .. |C009_github| replace:: C009.cpp .. _C009_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C009.cpp .. |C009_usedby| replace:: `.` @@ -104,6 +113,7 @@ .. |C010_type| replace:: :cyan:`Controller` .. |C010_typename| replace:: :cyan:`Controller - Generic UDP` .. |C010_status| replace:: :green:`NORMAL` +.. |C010_status_lb| replace:: :green:`NORMAL` .. |C010_github| replace:: C010.cpp .. _C010_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C010.cpp .. |C010_usedby| replace:: `.` @@ -115,6 +125,7 @@ .. |C011_type| replace:: :cyan:`Controller` .. |C011_typename| replace:: :cyan:`Controller - Generic HTTP Advanced` .. |C011_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` +.. |C011_status_lb| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` .. |C011_github| replace:: C011.cpp .. _C011_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C011.cpp .. |C011_usedby| replace:: `.` @@ -126,6 +137,7 @@ .. |C012_type| replace:: :cyan:`Controller` .. |C012_typename| replace:: :cyan:`Controller - Blynk` .. |C012_status| replace:: :yellow:`COLLECTION` +.. |C012_status_lb| replace:: :yellow:`COLLECTION` .. |C012_github| replace:: C012.cpp .. _C012_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C012.cpp .. |C012_usedby| replace:: `.` @@ -137,6 +149,7 @@ .. |C013_type| replace:: :cyan:`Controller` .. |C013_typename| replace:: :cyan:`Controller - ESPEasy P2P Networking` .. |C013_status| replace:: :green:`NORMAL` +.. |C013_status_lb| replace:: :green:`NORMAL` .. |C013_github| replace:: C013.cpp .. _C013_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C013.cpp .. |C013_usedby| replace:: `.` @@ -148,6 +161,7 @@ .. |C014_type| replace:: :cyan:`Controller` .. |C014_typename| replace:: :cyan:`Controller - Homie MQTT convention` .. |C014_status| replace:: :yellow:`COLLECTION` +.. |C014_status_lb| replace:: :yellow:`COLLECTION` .. |C014_github| replace:: C014.cpp .. _C014_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C014.cpp .. |C014_usedby| replace:: `.` @@ -158,7 +172,8 @@ .. |C016_name| replace:: :cyan:`Cache` .. |C016_type| replace:: :cyan:`Controller` .. |C016_typename| replace:: :cyan:`Controller - Cache` -.. |C016_status| replace:: :red:`DEVELOPMENT` +.. |C016_status| replace:: :yellow:`MAX` +.. |C016_status_lb| replace:: `.` .. |C016_github| replace:: C016.cpp .. _C016_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C016.cpp .. |C016_usedby| replace:: `.` @@ -170,6 +185,7 @@ .. |C017_type| replace:: :cyan:`Controller` .. |C017_typename| replace:: :cyan:`Controller - Zabbix` .. |C017_status| replace:: :yellow:`COLLECTION` +.. |C017_status_lb| replace:: :yellow:`COLLECTION` .. |C017_github| replace:: C017.cpp .. _C017_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C017.cpp .. |C017_usedby| replace:: `.` @@ -181,6 +197,7 @@ .. |C018_type| replace:: :cyan:`Controller` .. |C018_typename| replace:: :cyan:`Controller - LoRa TTN - RN2483/RN2903` .. |C018_status| replace:: :yellow:`COLLECTION` +.. |C018_status_lb| replace:: :yellow:`COLLECTION` .. |C018_github| replace:: C018.cpp .. _C018_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_C018.cpp .. |C018_usedby| replace:: `.` diff --git a/docs/source/ESPEasy/ESPchips.rst b/docs/source/ESPEasy/ESPchips.rst index 044744085..c8381d63d 100644 --- a/docs/source/ESPEasy/ESPchips.rst +++ b/docs/source/ESPEasy/ESPchips.rst @@ -34,10 +34,14 @@ ESPEasy does support a number of variants of the processors manufactured by Espr * **ESP32-S3** Support added: 2023-05-03 * **ESP32-C2 / ESP8684** Support added: 2023-11-10 * **ESP32-C3 / ESP8685** Support added: 2023-05-03 -* **ESP32-C5** Not yet supported +* **ESP32-C5** Preliminary support added: 2026-01-09 * **ESP32-C6** Support added: 2023-11-10 +* **ESP32-C61** Preliminary support added: 2026-01-09 +* **ESP32-E22** Not yet supported * **ESP32-H2** Not yet supported -* **ESP32-P4** Not yet supported +* **ESP32-H21** Not yet supported +* **ESP32-H4** Not yet supported +* **ESP32-P4** Support added: 2026-01-08 .. list-table:: Espressif platforms @@ -92,7 +96,7 @@ ESPEasy does support a number of variants of the processors manufactured by Espr - 160 - 120 - 96 - - 400 + - 360 (v1.x) / 400 (v3.x) * - Voltage (V) - 2.5 ~ 3.6 - 3.0 ~ 3.6 @@ -115,8 +119,8 @@ ESPEasy does support a number of variants of the processors manufactured by Espr - - Nov-23 - - - - - + - n/a + - Jan-26 * - Introduction - 2014 - 2016 @@ -124,23 +128,23 @@ ESPEasy does support a number of variants of the processors manufactured by Espr - 2021 - 2022 - 2020 - - + - 2022 - 2021 - - + - 2024 - 2021 - - - * - Status (2024/12) + - 2023 + * - Status (2026/03) - End-Of-Life - Mass Production (solo1: NRND) - NRND - Mass Production - Mass Production - Mass Production - - Sample - Mass Production - - Sample - Mass Production - - Sample + - Mass Production + - Mass Production + - Mass Production * - Wi-Fi - IEEE 802.11 b/g/n; 2.4 GHz; HT20; up to 72 Mbps - IEEE 802.11 b/g/n; 2.4 GHz; HT20/40; up to 150 Mbps diff --git a/docs/source/Hardware/Hardware.rst b/docs/source/Hardware/Hardware.rst index 8ae97b475..1b290f245 100644 --- a/docs/source/Hardware/Hardware.rst +++ b/docs/source/Hardware/Hardware.rst @@ -33,153 +33,6 @@ This feature can be useful in a development/laboratory environment, for when the .. image:: Hardware_Resetpin.png -------------- -I2C Bus -------------- - -When using devices that use the I2C bus (Inter-integrated circuit, also known as IIC, and mostly compatible with SM Bus) (`Wikipedia: I2C `_) some pins have to be configured, and initialized during boot, for the SDA and SCL connections. This can be any unused pair of pins on the ESP board. - -The pins can be configured here, and will have default values initially (ESP8266: SDA: GPIO-4 (D2), SCL: GPIO-5 (D3), ESP32: No defaults). When I2C is not used, these can be set to `- None -`, so the pins are available for other purposes. - -When having an I2C Priority task configured, the I2C GPIO pins (for that bus, when multiple buses are available) can no longer be changed, as that could disable the hardware, thus blocking the device. The configured GPIO pins will be display-only. - -The default bus clock speed can also be set here. If only devices supporting 100 kHz are connected (Old/Slow devices), then the value can be set to 100 kHz, by default 400 kHz is configured, that is supported by newer devices, though there are many devices supporting higher frequencies. ESP8266 is able to achieve ca. 400 kHz, while ESP32 allows higher speeds. - -ESPEasy has a separate setting for Slow I2C devices, and per I2C device this slow clock speed can be selected in the Device configuration page. This value is by default set to 100 kHz, but can be set lower or higher if desired. - -*I2C ClockStretchLimit* - -- `I2C-bus.org - Clock Stretching `_ -- `ESPeasy wiki - Basics: The I2C Bus `_ - -This setting is only available for the first I2C Bus, as not many devices (should) need this set, and the devices that do need this should then be connected to I2C Bus 0. - -.. image:: Hardware_I2CBus.png - -*Device specific Force Slow I2C speed selection:* - -.. image:: Device_I2COptionsShort.png - -Added: 2023-11-23 - -A device flag has been added for specific devices to have **Force Slow I2C speed** set by default. After adding the device this option will be checked, but can still be unchecked to use (try) Fast I2C speed (400 kHz). - -Added: 2025-02-02 - -Multiple I2C Buses can be configured on ESP32 builds. This aids in connecting all on-board sensors and devices when multiple GPIO pin-pairs are used for I2C devices. By default, 2 I2C Buses are made available, but via compile-time options, a 3rd I2C Bus can be enabled, if required. - -.. image:: Hardware_I2CBus2.png - -The available options are the same as for the first I2C Bus. - -If a second (or third) I2C Bus are not needed, then leave the GPIO settings on ``- None -``, and the interface won't be initialized, and not shown in the configuration options. - -NB: The I2C Buses should of course not be configured for the same GPIO pins as any other I2C Bus. - -NB2: Some boards require that in the Serial Console Settings (Tools/Advanced), the ``Fall-back to Serial 0`` option is disabled, to free the GPIO pins for I2C use. - -When multiple I2C Buses are configured (so, ``SDA`` and ``SCL`` GPIO-pins configured), each task configured with an I2C device will show a selection for the I2C Bus to use. As expected, the first I2C Bus is selected by default, and another interface can be selected as required. - -*Device specific I2C Bus selection:* - -.. image:: Device_I2CBusSelection.png - -NB: If a multiplexer is configured for 1 of the I2C Buses (but *not* for all interfaces), the I2C Bus selector will save & reload the page to show/hide the multiplexer options, below. - -.. image:: Device_I2CBusSelectionReload.png - -This screenshot shows the reload icon, to indicate that changing the selection will reload the page. - -.. image:: Device_I2CBusSelection3.png - -And an example for when 3 I2C Buses are available (compile-time option!) and configured. - ---------------- -I2C Multiplexer ---------------- - -Since build 20110, there is the option of using an I2C multiplexer. This option is not available in all builds, because of the size of the code. It is usually available in the normal, testing and custom builds, but ommitted from minimal, IR and hardware-specific builds. - -Possible use-cases for an I2C multiplexer are: - -* Connect multiple devices that have fixed or limited I2C addresses (For example, some OLED devices have a single fixed address but you need to connect 2 or more, or connect more than 3 TLS2561 devices, that support only 3 different addresses). -* Connect different devices that have the same I2C address (For example connecting a TSL2561 light/lux sensor and an APDS9960 proximity sensor). -* Connect slow and fast devices, where the speed of the fast device prohibits proper working of the slow device. - -.. |br| raw:: html - -
- -.. note:: - If devices with conflicting I2C addresses are to be used, then *none* of them can be connected to the ESP main I2C bus, but they should each be connected to a separate channel of the multiplexer. |br| - Devices that do not conflict with other devices *can* be connected to the ESP main I2C bus, this might improve the performance/responsiveness of these devices. - -.. note:: - When using an I2C Multiplexer, make sure there is no address conflict with any of the devices you intend to connect, f.e. when connecting BME280 sensors, don't set the address of the multiplexer to 0x76 or 0x77. - -There are a couple of I2C multiplexer chips available, currently there is support for: - -* TCA9548a (8 channels, multiple channel-connections, 8 I2C addresses, with reset) -* TCA9546a (4 channels, multiple channel-connections, 8 I2C addresses, with reset, also TCA9545a can be used, but no support for the Interrupt function though) -* TCA9543a (2 channels, multiple channel-connections, 4 I2C addresses, with reset) -* PCA9540 (2 channels, fixed I2C address, no reset, experimental support) - -The TCA9548a, TCA9546a and TCA9543a support connecting multiple channels to the main I2C channel. This can be configured on the Device edit page for I2C devices, once the I2C Multiplexer configuration is enabled by selecting a multiplexer type and an I2C address for the multiplexer. - -Also, the TCA9548a, TCA9546a and TCA9543a chips have a connection for a reset signal available. This allows the chip to be reset if it gets stuck by some 'less compatible' or 'badly behaving' devices. Once connected and configured, the multiplexer can be reset from the software, if desired or required. This feature is not yet used in any I2C device plugin. - -A TCA9543a board has the advantage of being quite a bit smaller than either TCA9546a or TCA9548a, while being digitally compatible. (But with less channels and only 4 I2C addresses). - -All these chips/boards can be found at Adafruit, Aliexpress, Banggood, EBay, etc. - -.. image:: Hardware_I2CMultiplexerNone.png - -*Available multiplexer types:* - -.. image:: Hardware_I2CMultiplexer_Type.png - -*Select the I2C Address for the multiplexer:* - -.. image:: Hardware_I2CMultiplexer_Address.png - - -Added: 2025-02-02 - -With the introduction of multiple I2C Buses, it is also plausible to configure an I2C Multiplexer on the second (or third, when included in the build) I2C Bus. - -.. image:: Hardware_I2CMultiplexer2.png - -This allows the same configuration options as shown above for the first I2C Bus, as all I2C Buses are completely independent from each other. - -Device configuration -^^^^^^^^^^^^^^^^^^^^ - -If an I2C multiplexer is configured for the selected I2C Bus, the Device edit page for I2C devices will show extra options to select the multiplexer channel the device is connected on. - -There is the default option of Single channel, or, when a TCA9548a, TCA9546a or TCA9543a is configured, Multiple channels. - -*Example: A multiplexer is configured, but the device is connected directly on the ESP board I2C channel:* - -.. image:: Device_I2COptionsMultiplexerNone.png - -*Configure a (single) multiplexer channel the device is connected on:* - -.. image:: Device_I2COptionsMultiplexerSelectSingleChannel.png - -NB: Only acceptable channels (0-7/0-3/0-1) will be available in the dropdown list, depending on the Multiplexer type configured. - -*Select Single channel or Multiple channels:* - -.. image:: Device_I2COptionsMultiplexerSelect.png - -*Configure multiple channels for a device, 8 channel multiplexer configured* - -.. image:: Device_I2COptionsMultiplexerMultipleSelect.png - -Above configuration results in channels 0, 4, 5, 6 and 7 being connected to the ESP board I2C bus when this sensor is active via I2C. - -NB: Only acceptable channel checkboxes (0-7/0-3/0-1) will be shown, depending on the Multiplexer type configured. - -------------------- PCF & MCP Direct I/O @@ -198,41 +51,6 @@ When using multiple PCF and/or MCP GPIO extenders, they must all be connected to NB: If only 1 I2C Bus is configured, this section isn't shown. -------------- -SPI Interface -------------- - -When using devices that are connected via the SPI interface (`Wikipedia: SPI `_), the interface must be initialized during boot. This can be enabled here. For ESP32 there is the option to select the HSPI (often called Hardware SPI) interface, the VSPI (often called Virtual SPI) interface, or select User-defined GPIO pins for the ``SCLK``, ``MISO`` and ``MOSI`` signals. - -The common SPI pins are shown here. - -Other SPI pins to be used are device specific, and need to be configured from the corresponding Device edit page. - -*For ESP8266:* - -.. image:: Hardware_SPIInterfaceESP8266.png - -*For ESP32, disabled:* - -.. image:: Hardware_SPIInterfaceESP32.png - -*For ESP32, select the desired configuration:* - -.. image:: Hardware_SPIInterfaceESP32_Select.png - -NB: When using the VSPI configuration and also the I2C interface is used, another pin has to be selected for I2C GPIO -> SCL, as its configuration is fixed for the VSPI setting. - -When selecting the *User-defined* options, 3 extra input fields are displayed, where the ``SCLK``, ``MISO`` and ``MOSI`` GPIO pins have to be selected. Nearly all pins can be used, but for the output signals ``SCLK`` and ``MOSI`` **no** *input-only* pins should be selected! - -.. image:: Hardware_SPIInterfaceESP32_UserDefined.png - -.. warning:: - When changing the setting for **Init SPI**, or changing any of the User-defined GPIO pins, the ESP32 unit needs a hardware reset. This can be achieved by pressing the reset button (when available, sometimes labelled ``EN`` or ``RST``), or by completely removing the power for ~30 seconds. Also take into account here that units with a backup battery (f.e. some LilyGo and Waveshare ESP32 models) may keep power on the unit, so specific measures may have to be taken! - -NB: When selecting the *User-defined* option, **all 3 GPIO pins should be set**, or an error message will be displayed when the page is saved, and the SPI interface will not be enabled at the next boot. - -.. image:: Hardware_SPIpinsError.png - ------- SD Card ------- @@ -241,202 +59,6 @@ When the compile-time option for SD-card support is enabled, the ``CS`` pin for .. image:: Hardware_SDCard.png --------- -Ethernet --------- - -On builds including ``_eth`` in their build name, it is also possible to use ethernet instead of WiFi. -Currently this is only present for ESP32 builds and no plans currently exist to support it for ESP82xx. - -N.B. This is still in testing phase, so not all kinds of network communications work right now. - -Preferred network medium -^^^^^^^^^^^^^^^^^^^^^^^^ - -Allows to switch between: - -* WiFi (default) -* Ethernet - -To activate a new configuration, a reboot is needed. - -Ethernet PHY type -^^^^^^^^^^^^^^^^^ - -ESP boards can be equiped with Ethernet. -This is more stable and reliable compared to WiFi and allows for better responsiveness. - -Actual transfer speed (at least when using RMII) is also higher than can be achieved via WiFi, but this is less of a concern for typical use cases where ESPEasy is used. - -Ethernet chips/boards for ESP32-variant boards exist with 2 types of interfaces to the ESP. - -* RMII interface - Faster actual transfer speeds possible, uses more GPIO pins, only supported on ESP32-classic (and upcoming ESP32-P4). -* SPI interface - (Added: 2024/02) Supported on all ESP32-variants (not all tested) on builds based on ESP-IDF 5.1 - SPI Ethernet adapters do obviously require the SPI interface to be configured. - -N.B. Only ESP32-variant builds with LittleFS support SPI Ethernet. (starting February 2024) - -Supported Ethernet chips: - -* RMII Interface: - * LAN8710 (LAN8720 is also supported, but none of the newer features are supported) - * TLK110 - * RTL8201 (since ESP32 IDF 4.4) - * JL1101 (since ESP32 IDF 4.4) - * DP83848 (since ESP32 IDF 4.4) - * KSZ8041 (since ESP32 IDF 4.4) - * KSZ8081 (since ESP32 IDF 4.4) - -* SPI Interface: (since ESP32 IDF 5.1) - * DM9051 - * W5500 - * KSZ8851 - -.. note:: The LAN8710 and LAN8720 are also available with an "A" suffix. - These are the same chips, only produced after the brand SMSC was taken over by Microchip Technology. - -Ethernet PHY Address -^^^^^^^^^^^^^^^^^^^^ - -The PHY address depends on the hardware and the PHY configuration. -On some chips, like the LAN8720, the board designer may set this address by pulling some pins either high or low at power on. -In theory, one could use multiple PHY adapters on the same RMII/SPI bus, but this is (currently) not supported by ESPEasy. - -* Espressif's Ethernet board with TLK110 PHY use PHY address 31. -* Common Waveshare LAN8720 PHY breakout board (and clones) use PHY address 1. -* Olimex ESP32 EVB REV B IoT LAN8710 PHY Board with CAN use PHY address 0. -* Other LAN8720 breakouts often use PHY address 0. -* ETH01-EVO (ESP32-C3 based board) uses PHY address 1. - -If the PHY address is incorrect then the EMAC will initialise but all attempts to read/write configuration registers on the PHY will fail. - -N.B. There is support for an auto detect of this PHY address, by setting it to -1, but at least on the LAN8720 this does not seem to work. - -RMII Ethernet -^^^^^^^^^^^^^ - -As mentioned above, the RMII interface is only present on ESP32-classic (and is mentioned on the announced ESP32-P4). - -RMII PHY SMI Wiring -""""""""""""""""""" - -Most PHY boards have documented their *RMII PHY SMI Wiring* pins: - -* **MDC** Output to PHY, usually pin 23 -* **MDIO** Bidirectional, usually pin 18 - -Clock sync -"""""""""" - -The PHY and the ESP need to keep a clock in sync. -This can either be done via an external crystal, which is connected to a GPIO pin. -Another option is to let the ESP provide the clock to the PHY. - -* External crystal oscillator -* 50MHz APLL Output on GPIO0 -* 50MHz APLL Output on GPIO16 -* 50MHz APLL Inverted Output on GPIO17 - -.. note:: When using an external crystal oscillator, this is connected to GPIO-0. Make sure this crystal is not active, or connected to GPIO-0, during boot or else the ESP32 may boot into flash mode. - -Power pin -""""""""" - -On almost all PHY boards, or ESP boards equiped with an ethernet PHY, it is possible to turn the PHY on or off. -Either to save energy, or to make sure the external clock is not affecting the ESP boot mode when it restarts. - -For example the Olimex ESP32-EVB does have the *external crystal oscillator* connected to GPIO-0, which could boot the ESP32 randomly into UART flash mode. -Most boards use a specific GPIO pin to control the power to the PHY. -The Olimex ESP32-EVB does have a specific delay circuit to only allow power to the PHY after boot and therefore does not need to control the PHY power. - -For other boards, the default is often GPIO-17, but this may change per board. - -(Changed: 2022-01-20) - -If the power pin is defined, ESPEasy will toggle the ethernet module off and on at boot. -Some Ethernet modules, like the LAN8720, may sometimes get stuck and need to be reset to get it to work again. - - -RMII PHY Wiring -""""""""""""""" - -Apart from these GPIO pins, there is a number of other pins reserved on the ESP32 for RMII PHY Wiring. - -Since these GPIO pin assignments cannot be changed, it is also not needed to configure them. -However, they also cannot be used when *RMII PHY* is used. - -.. include:: ../Reference/RMII_Ethernet_PHY_ESP32.rst - -RMII Ethernet ESP32 Boards -"""""""""""""""""""""""""" - -.. include:: ../Reference/RMII_Ethernet_ESP32_boards.rst - - -SPI Ethernet -^^^^^^^^^^^^ - -(Added: 2024/02) - -As mentioned above, these SPI based Ethernet interfaces require the SPI interface to be configured. - -The SPI bus can be shared, but the SPI Ethernet chips are a bit specific about the used frequency. -Currently the default SPI frequency of 20 MHz is used, so not all other SPI devices may work together with SPI Ethernet. - -Some boards like the ETH01-EVO (ESP32-C3 based) do not even have the SPI bus pins made accesible. - -.. note:: Switching to ECO mode can sometimes result in an unreachable node when using SPI Ethernet. After a reboot the node works just fine again (in ECO mode). This is currently being investigated. - - -GPIO Configuration -"""""""""""""""""" - -* CS pin: Just as any SPI device, it needs a CS pin to tell the device it is being addressed. -* IRQ/INT pin: Allows the Ethernet chip to signal the ESP about new data. (Optional for W5500) -* RST pin: ESP will try to reset the Ethernet adapter during boot. Not all boards may have this wired. - - -SPI Ethernet ESP32 Boards -""""""""""""""""""""""""" - -.. include:: ../Reference/SPI_Ethernet_ESP32_boards.rst - - - -Ethernet with PoE -^^^^^^^^^^^^^^^^^ - -Some ethernet boards support Power over Ethernet (PoE), so only a single (ethernet) cable to the ESP is needed, and the ESP (and any sensors) will be powered via an onboard converter. - -For Olimex boards in the ESP32-POE range, the supplier has documented this warning: - -.. warning:: - **Important notice**: Olimex ESP32-PoE has **no galvanic isolation** from Ethernet's power supply, when you program the board via the micro USB connector the Ethernet cable should be disconnected (if you have power over the Ethernet cable)! - - Consider using Olimex USB-ISO to protect your computer and board from accidental short circuit. Also consider instead using Olimex ESP32-PoE-ISO board, which *is* insulated. - -Most likely, this warning is applicable to other brands as well. - - -Ethernet Isolation -^^^^^^^^^^^^^^^^^^ - -Most Ethernet RJ45 phy (the connector on the PCB) have isolation transformers in them. -This does isolate the TX/RX pins to make sure there is no direct connection between the long cables and the Ethernet controller chip. -The isolation does protect the Ethernet chip from picked up high voltage spikes and ESD surges when inserting the Ethernet cable. -However not all ESP boards with Ethernet have these installed. - -Apart from the isolation of the TX/RX pins the metallic enclosure of the phy should also be isolated from the rest of the circuit of the ESP board. -Typically this is done by connecting the metal enclosure of the phy via a capacitor to GND of the rest of the circuit. -A lot of boards with Ethernet have these directly connected to GND, which may impose a problem when connecting the ESP board to your PC. - -.. warning:: - **Important notice**: For ESP boards with Ethernet which need debugging, never use Ethernet cables with metal shielding on the Ethernet connector. - -Using shielded Ethernet cable will connect the metal shield of the RJ45 phy to the ground of the switch and this may be connected to other appliances which may be badly grounded. -This will add a significant voltage offset between the ESP board and your PC while debugging. -Such a high voltage is very likely to destroy electronics. - ------------------- GPIO boot states diff --git a/docs/source/Hardware/Device_I2CBusSelection.png b/docs/source/Interfaces/Device_I2CBusSelection.png similarity index 100% rename from docs/source/Hardware/Device_I2CBusSelection.png rename to docs/source/Interfaces/Device_I2CBusSelection.png diff --git a/docs/source/Hardware/Device_I2CBusSelection3.png b/docs/source/Interfaces/Device_I2CBusSelection3.png similarity index 100% rename from docs/source/Hardware/Device_I2CBusSelection3.png rename to docs/source/Interfaces/Device_I2CBusSelection3.png diff --git a/docs/source/Hardware/Device_I2CBusSelectionReload.png b/docs/source/Interfaces/Device_I2CBusSelectionReload.png similarity index 100% rename from docs/source/Hardware/Device_I2CBusSelectionReload.png rename to docs/source/Interfaces/Device_I2CBusSelectionReload.png diff --git a/docs/source/Hardware/Device_I2COptionsMultiplexerMultipleSelect.png b/docs/source/Interfaces/Device_I2COptionsMultiplexerMultipleSelect.png similarity index 100% rename from docs/source/Hardware/Device_I2COptionsMultiplexerMultipleSelect.png rename to docs/source/Interfaces/Device_I2COptionsMultiplexerMultipleSelect.png diff --git a/docs/source/Hardware/Device_I2COptionsMultiplexerNone.png b/docs/source/Interfaces/Device_I2COptionsMultiplexerNone.png similarity index 100% rename from docs/source/Hardware/Device_I2COptionsMultiplexerNone.png rename to docs/source/Interfaces/Device_I2COptionsMultiplexerNone.png diff --git a/docs/source/Hardware/Device_I2COptionsMultiplexerSelect.png b/docs/source/Interfaces/Device_I2COptionsMultiplexerSelect.png similarity index 100% rename from docs/source/Hardware/Device_I2COptionsMultiplexerSelect.png rename to docs/source/Interfaces/Device_I2COptionsMultiplexerSelect.png diff --git a/docs/source/Hardware/Device_I2COptionsMultiplexerSelectSingleChannel.png b/docs/source/Interfaces/Device_I2COptionsMultiplexerSelectSingleChannel.png similarity index 100% rename from docs/source/Hardware/Device_I2COptionsMultiplexerSelectSingleChannel.png rename to docs/source/Interfaces/Device_I2COptionsMultiplexerSelectSingleChannel.png diff --git a/docs/source/Hardware/Device_I2COptionsShort.png b/docs/source/Interfaces/Device_I2COptionsShort.png similarity index 100% rename from docs/source/Hardware/Device_I2COptionsShort.png rename to docs/source/Interfaces/Device_I2COptionsShort.png diff --git a/docs/source/Hardware/Hardware_I2CBus.png b/docs/source/Interfaces/Hardware_I2CBus.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CBus.png rename to docs/source/Interfaces/Hardware_I2CBus.png diff --git a/docs/source/Hardware/Hardware_I2CBus2.png b/docs/source/Interfaces/Hardware_I2CBus2.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CBus2.png rename to docs/source/Interfaces/Hardware_I2CBus2.png diff --git a/docs/source/Hardware/Hardware_I2CMultiplexer2.png b/docs/source/Interfaces/Hardware_I2CMultiplexer2.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CMultiplexer2.png rename to docs/source/Interfaces/Hardware_I2CMultiplexer2.png diff --git a/docs/source/Hardware/Hardware_I2CMultiplexerNone.png b/docs/source/Interfaces/Hardware_I2CMultiplexerNone.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CMultiplexerNone.png rename to docs/source/Interfaces/Hardware_I2CMultiplexerNone.png diff --git a/docs/source/Hardware/Hardware_I2CMultiplexer_Address.png b/docs/source/Interfaces/Hardware_I2CMultiplexer_Address.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CMultiplexer_Address.png rename to docs/source/Interfaces/Hardware_I2CMultiplexer_Address.png diff --git a/docs/source/Hardware/Hardware_I2CMultiplexer_Type.png b/docs/source/Interfaces/Hardware_I2CMultiplexer_Type.png similarity index 100% rename from docs/source/Hardware/Hardware_I2CMultiplexer_Type.png rename to docs/source/Interfaces/Hardware_I2CMultiplexer_Type.png diff --git a/docs/source/Hardware/Hardware_SPIInterfaceESP32.png b/docs/source/Interfaces/Hardware_SPIInterfaceESP32.png similarity index 100% rename from docs/source/Hardware/Hardware_SPIInterfaceESP32.png rename to docs/source/Interfaces/Hardware_SPIInterfaceESP32.png diff --git a/docs/source/Hardware/Hardware_SPIInterfaceESP32_Select.png b/docs/source/Interfaces/Hardware_SPIInterfaceESP32_Select.png similarity index 100% rename from docs/source/Hardware/Hardware_SPIInterfaceESP32_Select.png rename to docs/source/Interfaces/Hardware_SPIInterfaceESP32_Select.png diff --git a/docs/source/Hardware/Hardware_SPIInterfaceESP32_UserDefined.png b/docs/source/Interfaces/Hardware_SPIInterfaceESP32_UserDefined.png similarity index 100% rename from docs/source/Hardware/Hardware_SPIInterfaceESP32_UserDefined.png rename to docs/source/Interfaces/Hardware_SPIInterfaceESP32_UserDefined.png diff --git a/docs/source/Hardware/Hardware_SPIInterfaceESP8266.png b/docs/source/Interfaces/Hardware_SPIInterfaceESP8266.png similarity index 100% rename from docs/source/Hardware/Hardware_SPIInterfaceESP8266.png rename to docs/source/Interfaces/Hardware_SPIInterfaceESP8266.png diff --git a/docs/source/Hardware/Hardware_SPIpinsError.png b/docs/source/Interfaces/Hardware_SPIpinsError.png similarity index 100% rename from docs/source/Hardware/Hardware_SPIpinsError.png rename to docs/source/Interfaces/Hardware_SPIpinsError.png diff --git a/docs/source/Interfaces/Interfaces.rst b/docs/source/Interfaces/Interfaces.rst new file mode 100644 index 000000000..87e9084f1 --- /dev/null +++ b/docs/source/Interfaces/Interfaces.rst @@ -0,0 +1,209 @@ +.. _Interfaces_page: + +Interfaces page +*************** + +To configure the available (included in the build) device interfaces there are tabs for each type of interface bus. These tabs allow to set up the GPIO pins, clock speeds and other needed parameters for each interface. + +* :ref:`i2c-bus` +* :ref:`spi-bus` +* :ref:`modbus` + +.. _i2c-bus: + +------- +I2C Bus +------- + +When using devices that use the I2C bus (Inter-integrated circuit, also known as IIC, and mostly compatible with SM Bus) (`Wikipedia: I2C `_) some pins have to be configured, and initialized during boot, for the SDA and SCL connections. This can be any unused pair of pins on the ESP board. + +The pins can be configured here, and will have default values initially (ESP8266: SDA: GPIO-4 (D2), SCL: GPIO-5 (D3), ESP32: No defaults). When I2C is not used, these can be set to `- None -`, so the pins are available for other purposes. + +When having an I2C Priority task configured, the I2C GPIO pins (for that bus, when multiple buses are available) can no longer be changed, as that could disable the hardware, thus blocking the device. The configured GPIO pins will be display-only. + +The default bus clock speed can also be set here. If only devices supporting 100 kHz are connected (Old/Slow devices), then the value can be set to 100 kHz, by default 400 kHz is configured, that is supported by newer devices, though there are many devices supporting higher frequencies. ESP8266 is able to achieve ca. 400 kHz, while ESP32 allows higher speeds. + +ESPEasy has a separate setting for Slow I2C devices, and per I2C device this slow clock speed can be selected in the Device configuration page. This value is by default set to 100 kHz, but can be set lower or higher if desired. + +*I2C ClockStretchLimit* + +- `I2C-bus.org - Clock Stretching `_ +- `ESPeasy wiki - Basics: The I2C Bus `_ + +This setting is only available for the first I2C Bus, as not many devices (should) need this set, and the devices that do need this should then be connected to I2C Bus 0. + +.. image:: Hardware_I2CBus.png + +*Device specific Force Slow I2C speed selection:* + +.. image:: Device_I2COptionsShort.png + +Added: 2023-11-23 + +A device flag has been added for specific devices to have **Force Slow I2C speed** set by default. After adding the device this option will be checked, but can still be unchecked to use (try) Fast I2C speed (400 kHz). + +Added: 2025-02-02 + +Multiple I2C Buses can be configured on ESP32 builds. This aids in connecting all on-board sensors and devices when multiple GPIO pin-pairs are used for I2C devices. By default, 2 I2C Buses are made available, but via compile-time options, a 3rd I2C Bus can be enabled, if required. + +.. image:: Hardware_I2CBus2.png + +The available options are the same as for the first I2C Bus. + +If a second (or third) I2C Bus are not needed, then leave the GPIO settings on ``- None -``, and the interface won't be initialized, and not shown in the configuration options. + +NB: The I2C Buses should of course not be configured for the same GPIO pins as any other I2C Bus. + +NB2: Some boards require that in the Serial Console Settings (Tools/Advanced), the ``Fall-back to Serial 0`` option is disabled, to free the GPIO pins for I2C use. + +When multiple I2C Buses are configured (so, ``SDA`` and ``SCL`` GPIO-pins configured), each task configured with an I2C device will show a selection for the I2C Bus to use. As expected, the first I2C Bus is selected by default, and another interface can be selected as required. + +*Device specific I2C Bus selection:* + +.. image:: Device_I2CBusSelection.png + +NB: If a multiplexer is configured for 1 of the I2C Buses (but *not* for all interfaces), the I2C Bus selector will save & reload the page to show/hide the multiplexer options, below. + +.. image:: Device_I2CBusSelectionReload.png + +This screenshot shows the reload icon, to indicate that changing the selection will reload the page. + +.. image:: Device_I2CBusSelection3.png + +And an example for when 3 I2C Buses are available (compile-time option!) and configured. + +--------------- +I2C Multiplexer +--------------- + +Since build 20110, there is the option of using an I2C multiplexer. This option is not available in all builds, because of the size of the code. It is usually available in the normal, testing and custom builds, but ommitted from minimal, IR and hardware-specific builds. + +Possible use-cases for an I2C multiplexer are: + +* Connect multiple devices that have fixed or limited I2C addresses (For example, some OLED devices have a single fixed address but you need to connect 2 or more, or connect more than 3 TLS2561 devices, that support only 3 different addresses). +* Connect different devices that have the same I2C address (For example connecting a TSL2561 light/lux sensor and an APDS9960 proximity sensor). +* Connect slow and fast devices, where the speed of the fast device prohibits proper working of the slow device. + +.. |br| raw:: html + +
+ +.. note:: + If devices with conflicting I2C addresses are to be used, then *none* of them can be connected to the ESP main I2C bus, but they should each be connected to a separate channel of the multiplexer. |br| + Devices that do not conflict with other devices *can* be connected to the ESP main I2C bus, this might improve the performance/responsiveness of these devices. + +.. note:: + When using an I2C Multiplexer, make sure there is no address conflict with any of the devices you intend to connect, f.e. when connecting BME280 sensors, don't set the address of the multiplexer to 0x76 or 0x77. + +There are a couple of I2C multiplexer chips available, currently there is support for: + +* TCA9548a (8 channels, multiple channel-connections, 8 I2C addresses, with reset) +* TCA9546a (4 channels, multiple channel-connections, 8 I2C addresses, with reset, also TCA9545a can be used, but no support for the Interrupt function though) +* TCA9543a (2 channels, multiple channel-connections, 4 I2C addresses, with reset) +* PCA9540 (2 channels, fixed I2C address, no reset, experimental support) + +The TCA9548a, TCA9546a and TCA9543a support connecting multiple channels to the main I2C channel. This can be configured on the Device edit page for I2C devices, once the I2C Multiplexer configuration is enabled by selecting a multiplexer type and an I2C address for the multiplexer. + +Also, the TCA9548a, TCA9546a and TCA9543a chips have a connection for a reset signal available. This allows the chip to be reset if it gets stuck by some 'less compatible' or 'badly behaving' devices. Once connected and configured, the multiplexer can be reset from the software, if desired or required. This feature is not yet used in any I2C device plugin. + +A TCA9543a board has the advantage of being quite a bit smaller than either TCA9546a or TCA9548a, while being digitally compatible. (But with less channels and only 4 I2C addresses). + +All these chips/boards can be found at Adafruit, Aliexpress, Banggood, EBay, etc. + +.. image:: Hardware_I2CMultiplexerNone.png + +*Available multiplexer types:* + +.. image:: Hardware_I2CMultiplexer_Type.png + +*Select the I2C Address for the multiplexer:* + +.. image:: Hardware_I2CMultiplexer_Address.png + + +Added: 2025-02-02 + +With the introduction of multiple I2C Buses, it is also plausible to configure an I2C Multiplexer on the second (or third, when included in the build) I2C Bus. + +.. image:: Hardware_I2CMultiplexer2.png + +This allows the same configuration options as shown above for the first I2C Bus, as all I2C Buses are completely independent from each other. + +Device configuration +^^^^^^^^^^^^^^^^^^^^ + +If an I2C multiplexer is configured for the selected I2C Bus, the Device edit page for I2C devices will show extra options to select the multiplexer channel the device is connected on. + +There is the default option of Single channel, or, when a TCA9548a, TCA9546a or TCA9543a is configured, Multiple channels. + +*Example: A multiplexer is configured, but the device is connected directly on the ESP board I2C channel:* + +.. image:: Device_I2COptionsMultiplexerNone.png + +*Configure a (single) multiplexer channel the device is connected on:* + +.. image:: Device_I2COptionsMultiplexerSelectSingleChannel.png + +NB: Only acceptable channels (0-7/0-3/0-1) will be available in the dropdown list, depending on the Multiplexer type configured. + +*Select Single channel or Multiple channels:* + +.. image:: Device_I2COptionsMultiplexerSelect.png + +*Configure multiple channels for a device, 8 channel multiplexer configured* + +.. image:: Device_I2COptionsMultiplexerMultipleSelect.png + +Above configuration results in channels 0, 4, 5, 6 and 7 being connected to the ESP board I2C bus when this sensor is active via I2C. + +NB: Only acceptable channel checkboxes (0-7/0-3/0-1) will be shown, depending on the Multiplexer type configured. + +| + +.. _spi-bus: + +------- +SPI Bus +------- + +When using devices that are connected via the SPI interface (`Wikipedia: SPI `_), the interface must be initialized during boot. This can be enabled here. For ESP32 there is the option to select the HSPI (often called Hardware SPI) interface, the VSPI (often called Virtual SPI) interface, or select User-defined GPIO pins for the ``SCLK``, ``MISO`` and ``MOSI`` signals. + +The common SPI pins are shown here. + +Other SPI pins to be used are device specific, and need to be configured from the corresponding Device edit page. + +*For ESP8266:* + +.. image:: Hardware_SPIInterfaceESP8266.png + +*For ESP32, disabled:* + +.. image:: Hardware_SPIInterfaceESP32.png + +*For ESP32, select the desired configuration:* + +.. image:: Hardware_SPIInterfaceESP32_Select.png + +NB: When using the VSPI configuration and also the I2C interface is used, another pin has to be selected for I2C GPIO -> SCL, as its configuration is fixed for the VSPI setting. + +When selecting the *User-defined* options, 3 extra input fields are displayed, where the ``SCLK``, ``MISO`` and ``MOSI`` GPIO pins have to be selected. Nearly all pins can be used, but for the output signals ``SCLK`` and ``MOSI`` **no** *input-only* pins should be selected! + +.. image:: Hardware_SPIInterfaceESP32_UserDefined.png + +.. warning:: + When changing the setting for **Init SPI**, or changing any of the User-defined GPIO pins, the ESP32 unit needs a hardware reset. This can be achieved by pressing the reset button (when available, sometimes labelled ``EN`` or ``RST``), or by completely removing the power for ~30 seconds. Also take into account here that units with a backup battery (f.e. some LilyGo and Waveshare ESP32 models) may keep power on the unit, so specific measures may have to be taken! + +NB: When selecting the *User-defined* option, **all 3 GPIO pins should be set**, or an error message will be displayed when the page is saved, and the SPI interface will not be enabled at the next boot. + +.. image:: Hardware_SPIpinsError.png + +| + +.. _modbus: + +------ +Modbus +------ + +TODO Modbus configuration and documentation to be added. + diff --git a/docs/source/Network/Ethernet_generic.repl b/docs/source/Network/Ethernet_generic.repl new file mode 100644 index 000000000..897112e2a --- /dev/null +++ b/docs/source/Network/Ethernet_generic.repl @@ -0,0 +1,58 @@ +-------- +Ethernet +-------- + +On nearly all ESP32 builds (exception: ESP32-C2), it is also possible to use ethernet instead of WiFi. +Currently this is only present for ESP32 builds and no plans currently exist to support it for ESP82xx. + +Ethernet PHY type +^^^^^^^^^^^^^^^^^ + +ESP boards can be equiped with Ethernet. +This is more stable and reliable compared to WiFi and allows for better responsiveness. + +Actual transfer speed (at least when using RMII) is also higher than can be achieved via WiFi, but this is less of a concern for typical use cases where ESPEasy is used. + +Ethernet chips/boards for ESP32-variant boards exist with 2 types of interfaces to the ESP. + +* RMII interface - Faster actual transfer speeds possible, uses more GPIO pins, only supported on ESP32-classic and ESP32-P4. +* SPI interface - (Added: 2024/02) Supported on all ESP32-variants (not all tested) on builds based on ESP-IDF 5.1 and later. + SPI Ethernet adapters do obviously require the SPI interface to be configured. + +Supported Ethernet chips: + +* RMII Interface: + * LAN8710 (LAN8720 is also supported, but none of the newer features are supported) + * LAN867X (since ESP32 IDF 5.5) + * TLK110 + * RTL8201 (since ESP32 IDF 4.4) + * JL1101 (since ESP32 IDF 4.4) + * DP83848 (since ESP32 IDF 4.4) + * KSZ8041 (since ESP32 IDF 4.4) + * KSZ8081 (since ESP32 IDF 4.4) + +* SPI Interface: (since ESP32 IDF 5.1) + * DM9051 + * W5500 + * KSZ8851 + +.. note:: The LAN8710 and LAN8720 are also available with an "A" suffix. + These are the same chips, only produced after the brand SMSC was taken over by Microchip Technology. + +Ethernet PHY Address +^^^^^^^^^^^^^^^^^^^^ + +The PHY address depends on the hardware and the PHY configuration. +On some chips, like the LAN8720, the board designer may set this address by pulling some pins either high or low at power on. +In theory, one could use multiple PHY adapters on the same RMII/SPI bus, but this is (currently) not supported by ESPEasy. + +* Espressif's Ethernet board with TLK110 PHY use PHY address 31. +* Common Waveshare LAN8720 PHY breakout board (and clones) use PHY address 1. +* Olimex ESP32 EVB REV B IoT LAN8710 PHY Board with CAN use PHY address 0. +* Other LAN8720 breakouts often use PHY address 0. +* ETH01-EVO (ESP32-C3 based board) uses PHY address 1. + +If the PHY address is incorrect then the EMAC will initialise but all attempts to read/write configuration registers on the PHY will fail. + +N.B. There is support for an auto detect of this PHY address, by setting it to -1, but at least on the LAN8720 this does not seem to work. + diff --git a/docs/source/Network/Ethernet_poe_iso.repl b/docs/source/Network/Ethernet_poe_iso.repl new file mode 100644 index 000000000..478abe82e --- /dev/null +++ b/docs/source/Network/Ethernet_poe_iso.repl @@ -0,0 +1,35 @@ + +Ethernet with PoE +^^^^^^^^^^^^^^^^^ + +Some ethernet boards support Power over Ethernet (PoE), so only a single (ethernet) cable to the ESP is needed, and the ESP (and any sensors) will be powered via an onboard converter. + +For Olimex boards in the ESP32-POE range, the supplier has documented this warning: + +.. warning:: + **Important notice**: Olimex ESP32-PoE has **no galvanic isolation** from Ethernet's power supply, when you program the board via the micro USB connector the Ethernet cable should be disconnected (if you have power over the Ethernet cable)! + + Consider using Olimex USB-ISO to protect your computer and board from accidental short circuit. Also consider instead using Olimex ESP32-PoE-ISO board, which *is* insulated. + +Most likely, this warning is applicable to other brands as well. + + +Ethernet Isolation +^^^^^^^^^^^^^^^^^^ + +Most Ethernet RJ45 phy (the connector on the PCB) have isolation transformers in them. +This does isolate the TX/RX pins to make sure there is no direct connection between the long cables and the Ethernet controller chip. +The isolation does protect the Ethernet chip from picked up high voltage spikes and ESD surges when inserting the Ethernet cable. +However not all ESP boards with Ethernet have these installed. + +Apart from the isolation of the TX/RX pins the metallic enclosure of the phy should also be isolated from the rest of the circuit of the ESP board. +Typically this is done by connecting the metal enclosure of the phy via a capacitor to GND of the rest of the circuit. +A lot of boards with Ethernet have these directly connected to GND, which may impose a problem when connecting the ESP board to your PC. + +.. warning:: + **Important notice**: For ESP boards with Ethernet which need debugging, never use Ethernet cables with metal shielding on the Ethernet connector. + +Using shielded Ethernet cable will connect the metal shield of the RJ45 phy to the ground of the switch and this may be connected to other appliances which may be badly grounded. +This will add a significant voltage offset between the ESP board and your PC while debugging. +Such a high voltage is very likely to destroy electronics. + diff --git a/docs/source/Network/Ethernet_rmii.repl b/docs/source/Network/Ethernet_rmii.repl new file mode 100644 index 000000000..62bc122d9 --- /dev/null +++ b/docs/source/Network/Ethernet_rmii.repl @@ -0,0 +1,60 @@ +RMII Ethernet +^^^^^^^^^^^^^ + +As mentioned above, the RMII interface is only present on ESP32-classic and ESP32-P4. + +RMII PHY SMI Wiring +""""""""""""""""""" + +Most PHY boards have documented their *RMII PHY SMI Wiring* pins: + +* **MDC** Output to PHY, usually pin 23 +* **MDIO** Bidirectional, usually pin 18 + +Clock sync +"""""""""" + +The PHY and the ESP need to keep a clock in sync. +This can either be done via an external crystal, which is connected to a GPIO pin. +Another option is to let the ESP provide the clock to the PHY. + +* External crystal oscillator +* 50MHz APLL Output on GPIO0 +* 50MHz APLL Output on GPIO16 +* 50MHz APLL Inverted Output on GPIO17 + +.. note:: When using an external crystal oscillator, this is connected to GPIO-0. Make sure this crystal is not active, or connected to GPIO-0, during boot or else the ESP32 may boot into flash mode. + +Power pin +""""""""" + +On almost all PHY boards, or ESP boards equiped with an ethernet PHY, it is possible to turn the PHY on or off. +Either to save energy, or to make sure the external clock is not affecting the ESP boot mode when it restarts. + +For example the Olimex ESP32-EVB does have the *external crystal oscillator* connected to GPIO-0, which could boot the ESP32 randomly into UART flash mode. +Most boards use a specific GPIO pin to control the power to the PHY. +The Olimex ESP32-EVB does have a specific delay circuit to only allow power to the PHY after boot and therefore does not need to control the PHY power. + +For other boards, the default is often GPIO-17, but this may change per board. + +(Changed: 2022-01-20) + +If the power pin is defined, ESPEasy will toggle the ethernet module off and on at boot. +Some Ethernet modules, like the LAN8720, may sometimes get stuck and need to be reset to get it to work again. + + +RMII PHY Wiring +""""""""""""""" + +Apart from these GPIO pins, there is a number of other pins reserved on the ESP32 for RMII PHY Wiring. + +Since these GPIO pin assignments cannot be changed, it is also not needed to configure them. +However, they also cannot be used when *RMII PHY* is used. + +.. include:: ../Reference/RMII_Ethernet_PHY_ESP32.rst + +RMII Ethernet ESP32 Boards +"""""""""""""""""""""""""" + +.. include:: ../Reference/RMII_Ethernet_ESP32_boards.rst + diff --git a/docs/source/Network/Ethernet_spi.repl b/docs/source/Network/Ethernet_spi.repl new file mode 100644 index 000000000..5518151e2 --- /dev/null +++ b/docs/source/Network/Ethernet_spi.repl @@ -0,0 +1,29 @@ + +SPI Ethernet +^^^^^^^^^^^^ + +(Added: 2024/02) + +As mentioned above, these SPI based Ethernet interfaces require the SPI interface to be configured. + +The SPI bus can be shared, but the SPI Ethernet chips are a bit specific about the used frequency. +Currently the default SPI frequency of 20 MHz is used, so not all other SPI devices may work together with SPI Ethernet. + +Some boards like the ETH01-EVO (ESP32-C3 based) do not even have the SPI bus pins made accesible. + +.. note:: Switching to ECO mode can sometimes result in an unreachable node when using SPI Ethernet. After a reboot the node works just fine again (in ECO mode). This is currently being investigated. + + +GPIO Configuration +"""""""""""""""""" + +* CS pin: Just as any SPI device, it needs a CS pin to tell the device it is being addressed. +* IRQ/INT pin: Allows the Ethernet chip to signal the ESP about new data. (Optional for W5500) +* RST pin: ESP will try to reset the Ethernet adapter during boot. Not all boards may have this wired. + + +SPI Ethernet ESP32 Boards +""""""""""""""""""""""""" + +.. include:: ../Reference/SPI_Ethernet_ESP32_boards.rst + diff --git a/docs/source/Network/NW001.rst b/docs/source/Network/NW001.rst index 0d0f6cd1b..89e113ca1 100644 --- a/docs/source/Network/NW001.rst +++ b/docs/source/Network/NW001.rst @@ -14,7 +14,9 @@ Type: |NW001_type| Name: |NW001_name| -Status: |NW001_status| +Status ESP32: |NW001_status| + +Status ESP8266: |NW001_status_lb| GitHub: |NW001_github|_ diff --git a/docs/source/Network/NW002.rst b/docs/source/Network/NW002.rst index ca451d1c3..d312a7e72 100644 --- a/docs/source/Network/NW002.rst +++ b/docs/source/Network/NW002.rst @@ -14,7 +14,9 @@ Type: |NW002_type| Name: |NW002_name| -Status: |NW002_status| +Status ESP32: |NW002_status| + +Status ESP8266: |NW002_status_lb| GitHub: |NW002_github|_ diff --git a/docs/source/Network/NW003.rst b/docs/source/Network/NW003.rst index 3be1023fe..d9d8e6a19 100644 --- a/docs/source/Network/NW003.rst +++ b/docs/source/Network/NW003.rst @@ -14,12 +14,20 @@ Type: |NW003_type| Name: |NW003_name| -Status: |NW003_status| +Status ESP32: |NW003_status| + +Status ESP8266: |NW003_status_lb| GitHub: |NW003_github|_ Maintainer: |NW003_maintainer| +.. include:: Ethernet_generic.repl + +.. include:: Ethernet_rmii.repl + +.. include:: Ethernet_poe_iso.repl + Change log ---------- diff --git a/docs/source/Network/NW004.rst b/docs/source/Network/NW004.rst index 9a4885546..558102ccb 100644 --- a/docs/source/Network/NW004.rst +++ b/docs/source/Network/NW004.rst @@ -14,12 +14,20 @@ Type: |NW004_type| Name: |NW004_name| -Status: |NW004_status| +Status ESP32: |NW004_status| + +Status ESP8266: |NW004_status_lb| GitHub: |NW004_github|_ Maintainer: |NW004_maintainer| +.. include:: Ethernet_generic.repl + +.. include:: Ethernet_spi.repl + +.. include:: Ethernet_poe_iso.repl + Change log ---------- diff --git a/docs/source/Network/NW005.rst b/docs/source/Network/NW005.rst index 5cf45a5de..2d88ede0f 100644 --- a/docs/source/Network/NW005.rst +++ b/docs/source/Network/NW005.rst @@ -14,7 +14,9 @@ Type: |NW005_type| Name: |NW005_name| -Status: |NW005_status| +Status ESP32: |NW005_status| + +Status ESP8266: |NW005_status_lb| GitHub: |NW005_github|_ diff --git a/docs/source/Network/_Network.rst b/docs/source/Network/_Network.rst index 807f8d1b7..d0b586a74 100644 --- a/docs/source/Network/_Network.rst +++ b/docs/source/Network/_Network.rst @@ -3,7 +3,7 @@ Network ******* -A Network plugin is compable with a 'driver' for a network adapter. +A Network plugin is comparable with a 'driver' for a network adapter. The first 2 entries will be the same for every ESPEasy setup and these cannot be removed. @@ -11,21 +11,107 @@ The first 2 entries will be the same for every ESPEasy setup and these cannot be - Wi-Fi AP: To let the ESP board act as an access point. +.. note:: + The network code of ESPEasy has been rewritten in 2025/2026. + Older builds of ESPEasy do have a different organisation of network related parameters. + Most of these were accessible via the ``Tools->Advanced`` page. + + .. _Network Plugins: Network Plugins ================== -.. csv-table:: - :header: "Plugin name", "Plugin status", "Plugin number" - :widths: 10, 8, 5 +.. note:: + ESP32 builds do have a lot more networking capabilities compared to ESP8266. + For ESP8266 we only have support for WiFi and no other network interfaces will be added for ESP8266. - ":ref:`NW001_page`","|NW001_status|","NW001" - ":ref:`NW002_page`","|NW002_status|","NW002" - ":ref:`NW003_page`","|NW003_status|","NW003" - ":ref:`NW004_page`","|NW004_status|","NW004" - ":ref:`NW005_page`","|NW005_status|","NW005" +.. csv-table:: + :header: "Plugin name", "ESP32 Plugin status", "ESP8266 Plugin status", "Plugin number" + :widths: 9, 6, 6, 3 + + ":ref:`NW001_page`","|NW001_status|","|NW001_status_lb|","NW001" + ":ref:`NW002_page`","|NW002_status|","|NW002_status_lb|","NW002" + ":ref:`NW003_page`","|NW003_status|","|NW003_status_lb|","NW003" + ":ref:`NW004_page`","|NW004_status|","|NW004_status_lb|","NW004" + ":ref:`NW005_page`","|NW005_status|","|NW005_status_lb|","NW005" Network Parameters -===================== +================== + +Route Priority +-------------- + +(ESP32 only) + +When using multiple network interfaces, like WiFi, Ethernet or PPP LTE Modem, it must be made clear which interface should be used for new connections initiated from ESPEasy to some other host. + +The connected network interface with the highest Route Priority is considered to be the default route. + +Default Route Priority values are: + +* WiFi STA = 100 +* Ethernet = 50 +* PPP (LTE modem) = 20 +* WiFi AP = 10 + + +Fallback Interface +------------------ + +(ESP32 only) + +.. note:: + The concept of a Fallback Interface is available on ESP8266, but only for WiFi AP. + This is a special case, which is discussed here: :ref:`NW002_page` . + +A network interface can be marked as "Fallback Interface". + +A fallback interface will only be scheduled to start when: + +* A non-fallback interface failed to connect. +* Route Priority changed to a value which is less than the set priority for the fallback interface. + +The scheduled delay to start the fallback interface is set as "Delay Startup" (see below). + +A fallback interface will be stopped when there is a default route with a route priority higher than the set Route Priority of the fallback interface. + +It is possible to set multiple interfaces as Fallback Interface. +The fallback order can be tweaked using the Delay Startup and Route Priority. + +N.B. Network interfaces which should be started at boot, should not be marked as Fallback Interface. + +Delay Startup +------------- + +For various reasons, it can be useful to not immediately start a network interface at boot. +For example to reduce the power consumption as most network interfaces may draw significant more power for a short time when starting. + +Another use case can be to check some sensor value before deciding to either start the network interface or enter deep sleep again. + +The set value (in msec) is the delay from boot before starting the network interface. + +Delay Startup for Fallback Interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A Fallback Interface is not started at boot. +The set Delay Startup is then used as delay to schedule starting the network interface. +See "Fallback Interface" for more information. + + +Block Web Access +---------------- + +When checked, the ESPEasy web interface cannot be accessed via the IP-range of the network interface. + +.. note:: + The PPP Network Interface (ESP32-only) will have this checked by default, since you typically can't access devices from the network of the mobile provider. + If this is possible for some odd reason, you very likely would never want to allow this. + +Enable IPv6 +----------- + +(ESP32 only) + +Checking this checkbox, will allow the network device to use IPv6. diff --git a/docs/source/Network/_network_substitutions.repl b/docs/source/Network/_network_substitutions.repl index d65e951fe..bf6b27278 100644 --- a/docs/source/Network/_network_substitutions.repl +++ b/docs/source/Network/_network_substitutions.repl @@ -5,6 +5,7 @@ .. |NW001_type| replace:: :cyan:`Network` .. |NW001_typename| replace:: :cyan:`Network - WiFi Station` .. |NW001_status| replace:: :green:`NORMAL` +.. |NW001_status_lb| replace:: :green:`NORMAL` .. |NW001_github| replace:: _NW001_WiFi_STA.cpp .. _NW001_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/ESPEasy/net/_NW001_WiFi_STA.cpp .. |NW001_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |NW002_type| replace:: :cyan:`Network` .. |NW002_typename| replace:: :cyan:`Network - WiFi AP` .. |NW002_status| replace:: :green:`NORMAL` +.. |NW002_status_lb| replace:: :green:`NORMAL` .. |NW002_github| replace:: _NW002_WiFi_AP.cpp .. _NW002_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/ESPEasy/net/_NW002_WiFi_AP.cpp .. |NW002_usedby| replace:: `.` @@ -27,6 +29,7 @@ .. |NW003_type| replace:: :cyan:`Network` .. |NW003_typename| replace:: :cyan:`Network - Ethernet (RMII)` .. |NW003_status| replace:: :green:`NORMAL` +.. |NW003_status_lb| replace:: `.` .. |NW003_github| replace:: _NW003_ETH_RMII.cpp .. _NW003_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/ESPEasy/net/_NW003_ETH_RMII.cpp .. |NW003_usedby| replace:: `.` @@ -38,6 +41,7 @@ .. |NW004_type| replace:: :cyan:`Network` .. |NW004_typename| replace:: :cyan:`Network - Ethernet (SPI)` .. |NW004_status| replace:: :green:`NORMAL` +.. |NW004_status_lb| replace:: `.` .. |NW004_github| replace:: _NW004_ETH_SPI.cpp .. _NW004_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/ESPEasy/net/_NW004_ETH_SPI.cpp .. |NW004_usedby| replace:: `.` @@ -49,6 +53,7 @@ .. |NW005_type| replace:: :cyan:`Network` .. |NW005_typename| replace:: :cyan:`Network - PPP Modem` .. |NW005_status| replace:: :green:`NORMAL` +.. |NW005_status_lb| replace:: `.` .. |NW005_github| replace:: _NW005_PPP_modem.cpp .. _NW005_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/ESPEasy/net/_NW005_PPP_modem.cpp .. |NW005_usedby| replace:: `.` diff --git a/docs/source/Notify/N001.rst b/docs/source/Notify/N001.rst index e73cef858..576665ad8 100644 --- a/docs/source/Notify/N001.rst +++ b/docs/source/Notify/N001.rst @@ -11,7 +11,9 @@ Type: |N001_type| Name: |N001_name| -Status: |N001_status| +Status ESP32: |N001_status| + +Status ESP8266: |N001_status_lb| GitHub: |N001_github|_ diff --git a/docs/source/Notify/N002.rst b/docs/source/Notify/N002.rst index 4cbcb51a5..daca423ba 100644 --- a/docs/source/Notify/N002.rst +++ b/docs/source/Notify/N002.rst @@ -12,7 +12,9 @@ Type: |N002_type| Name: |N002_name| -Status: |N002_status| +Status ESP32: |N002_status| + +Status ESP8266: |N002_status_lb| GitHub: |N002_github|_ diff --git a/docs/source/Notify/_Notifications.rst b/docs/source/Notify/_Notifications.rst index 817453133..31f397504 100644 --- a/docs/source/Notify/_Notifications.rst +++ b/docs/source/Notify/_Notifications.rst @@ -16,8 +16,8 @@ Notifications Plugins ====================== .. csv-table:: - :header: "Name", "Status", "Notify Number" - :widths: 10, 8, 5 + :header: "Plugin name", "ESP32 Plugin status", "ESP8266 Plugin status", "Plugin Number" + :widths: 9, 6, 6, 3 - ":ref:`N001_page`","|N001_status|","N001" - ":ref:`N002_page`","|N002_status|","N002" + ":ref:`N001_page`","|N001_status|","|N001_status_lb|","N001" + ":ref:`N002_page`","|N002_status|","|N002_status_lb|","N002" diff --git a/docs/source/Notify/_notify_substitutions_n00x.repl b/docs/source/Notify/_notify_substitutions_n00x.repl index b6c9702e1..e6fbbf10a 100644 --- a/docs/source/Notify/_notify_substitutions_n00x.repl +++ b/docs/source/Notify/_notify_substitutions_n00x.repl @@ -3,6 +3,7 @@ .. |N001_typename| replace:: :cyan:`Notifications - Email (SMTP)` .. |N001_porttype| replace:: `.` .. |N001_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`COLLECTION` :yellow:`SONOFF` :yellow:`SHELLY` +.. |N001_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`COLLECTION` :yellow:`SONOFF` :yellow:`SHELLY` .. |N001_github| replace:: N001_Email.cpp .. _N001_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_N001_Email.cpp .. |N001_maintainer| replace:: `.` @@ -15,6 +16,7 @@ .. |N002_typename| replace:: :cyan:`Notifications - Buzzer` .. |N002_porttype| replace:: `.` .. |N002_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`COLLECTION` :yellow:`SONOFF` :yellow:`SHELLY` +.. |N002_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`COLLECTION` :yellow:`SONOFF` :yellow:`SHELLY` .. |N002_github| replace:: N002_Buzzer.cpp .. _N002_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_N002_Buzzer.cpp .. |N002_maintainer| replace:: `.` diff --git a/docs/source/Participate/PlatformIO.rst b/docs/source/Participate/PlatformIO.rst index 3b39bae42..b84541f8d 100644 --- a/docs/source/Participate/PlatformIO.rst +++ b/docs/source/Participate/PlatformIO.rst @@ -3,14 +3,14 @@ PlatformIO ********** -ESP easy can be built using the Arduino IDE or PlatformIO (PIO). -Arduino IDE is not being used during development, so it may take some more effort to get it setup for building ESPeasy. +ESP easy can be built using the PlatformIO/pioarduino IDE (PIO). +Arduino IDE is no longer supported for development though it's technically possible to be used. It may take considerable effort to get it setup for building ESPeasy. -We *strongly* advise to use PlatformIO as build environment. +We *strongly* advise to use PIO as build environment. -PlatformIO is just the build, test and upload environment for many micro controllers like the ESP8266 and ESP32 we use. +PIO is just the build, test and upload environment for many micro controllers like the ESP8266 and ESP32 we use. -On top of that you need to use an editor, or so called IDE in which PlatformIO will be used. +On top of that you need to use an editor, or so called IDE in which PIO will be used. The current choice is: @@ -21,10 +21,10 @@ VSCode is free to use and available for Windows, MacOS and Linux. Apart from VSCode, there are more available, like Eclipse, MS Visual Studio (IDE) and probably more. -PlatformIO Prerequisites -======================== +PlatformIO/pioarduino Prerequisites +=================================== -PlatformIO does need at least the following: +PIO does need at least the following: * Python * Git command line tools (`download `_) @@ -58,17 +58,18 @@ install Pyton in Windows for PlatformIO. These steps have been explained in this Youtube video: https://youtu.be/ArqwMcYSMsU though you might want to use the latest version of both the Git tools and Python. -PlatformIO with VSCode -====================== +PlatformIO/pioarduino with VSCode +================================= Install ------- For development of ESPEasy, a number of extensions has to be installed in VS-Code: -* PlatformIO IDE (by PlatformIO) -* C/C++ IntelliSense (by Microsoft) +* pioarduino IDE (by pioarduino) +* C/C++ (by Microsoft) * C/C++ Extension Pack (by Microsoft) +* C/C++ Themes (by Microsoft) * Uncrustify (by Zachary Flower, originally by Laurent Tréguier) Optional, but highly recommended: @@ -79,6 +80,7 @@ Optional, but highly recommended: * All Autocomplete (by Atishay Jain) * Excel Viewer (by GrapeCity) * Esbonio - An extension for editing sphinx projects (by Swyddfa) +* reStructuredText (by LeXtudio) * reStructuredText Syntax highlighting (by Trond Snekvik) * Extension pack for reStructuredText (by LeXtudio Inc.) * Markdown All in One (by Yu Zhang) @@ -109,17 +111,19 @@ After setting it as the default formatter, the hotkey Alt-Shift-F (Cmd-Shift-F o There used to be a reference to the Atom editor, but both Atom, and the PlatformIO plugin for Atom, are no longer maintained by their owners, so it was removed from this documentation. -Load a project using PlatformIO -------------------------------- +Load a project using PlatformIO/pioarduino +------------------------------------------ + +.. note:: PlatformIO has been superceded by the Community-supported **pioarduino** initiative. For simplicity, in most places the abbreviation PIO is throughout this section of the documentation. If you have PIO installed and the source tree cloned to your hard drive, then you can open the main dir of the repository. The main directory of the repository is the level with the file platformio.ini in it. -Then in a few moments after opening the directory, on the left there will appear an alien logo, the logo of PlatformIO. +Then in a few moments after opening the directory, on the left there will appear an chip-package logo, the logo of pioarduino. If you click that one, you will get a tree with lots and lots of project tasks and environments. -It is important to note that PlatformIO does everything based on environments, which are defined in the platformio.ini file. -In the PlatformIO menu (on the left) everything is grouped per environment. +It is important to note that PIO does everything based on environments, which are defined in the platformio.ini file. +In the PIO menu (on the left) everything is grouped per environment. An environment entry has several tasks, like: @@ -130,8 +134,9 @@ An environment entry has several tasks, like: * Clean * ... many more. -Some of these options only are available when you have registered with PlatformIO and some are only for paid subscriptions. -At least the basic ones used for almost any user are available with the free account. +.. .. Some of these options only are available when you have registered with PlatformIO and some are only for paid subscriptions. + +.. .. At least the basic ones used for almost any user are available with the free account. The environment definitions all have at least the used micro controller in the name and the amount of flash memory used. @@ -144,8 +149,8 @@ For example: * ..._ESP32_16M8M_LittleFS -> ESP32 with 16 MB flash and a 4 MB partition for the sketch. (8MB LittleFS) * ..._ESP32_16M1M_ETH -> ESP32 with 16 MB flash and a 4 MB partition for the sketch. (1MB SPIFFS, Wired ethernet support) -Make a custom build using PlatformIO ------------------------------------- +Make a custom build using PIO +----------------------------- The easiest is to go for the environment ``custom_ESP8266_4M1M`` and unfold that one. Then select ``Build`` to see if it will start building. @@ -213,7 +218,7 @@ Now that Linux is installed, it's strongly advised to get it up to date with the NB: The ``sudo`` command will ask for your current account password once, (and maybe later again after some time has passed). When asked for confirmation, confirm the installation of any needed updates, or add the ``-y`` parameter to the ``upgrade`` command to continue without questions. -Additionally some tools need to be installed so PlatformIO can be properly installed later, and we can use the Uncrustify plugin in VS-Code to format the code nicely: +Additionally some tools need to be installed so PIO can be properly installed later, and we can use the Uncrustify plugin in VS-Code to format the code nicely: .. highlight::sh @@ -307,7 +312,7 @@ Upload to ESP Linux ----- -For Linux, you may need to install 99-platformio-udev.rules to make PlatformIO upload tools work in vscode. +For Linux, you may need to install 99-platformio-udev.rules to make PIO upload tools work in vscode. .. highlight::sh @@ -317,12 +322,12 @@ Starter guide for (local) development on ESPEasy For those with less development experience, or less experience in using Github, this chapter is intended as a **How To** guide to get started with development on ESPEasy. -It tries to help setting up Visual Studio Code (VSCode) with the PlatformIO development environment and additional VSCode plugins that aid in easier working on code and documentation. +It tries to help setting up Visual Studio Code (VSCode) with the PIO development environment and additional VSCode plugins that aid in easier working on code and documentation. The global steps described here are: - Creating a private copy on Github -- Getting VSCode and PlatformIO set up +- Getting VSCode and PIO set up - Getting the source code from Github onto your system - Compiling the source code - Creating a branch to make your changes @@ -366,12 +371,12 @@ After this completes, you can view the fork in your Github dashboard at https:// (You have to replace [your_github_handle] with the name you selected during the Github sign-up procedure) -Install VSCode and PlatformIO +Install VSCode and PIO ------------------------------ -Earlier on this page, a complete description has been given on how to install **PlatformIO with VSCode** with the required and advised optional extensions and the git command-line tools. +Earlier on this page, a complete description has been given on how to install **PlatformIO/pioarduino with VSCode** with the required and advised optional extensions and the git command-line tools. -NB: PlatformIO is often shortened to PIO. +NB: PlatformIO/pioarduino is shortened to PIO. Clone your forked repository to your computer --------------------------------------------- @@ -422,7 +427,7 @@ Depending on your usual workflow, the current VSCode environment can be saved as Compile an ESPEasy PIO environment ---------------------------------- -ESPEasy supports several different configurations of ESP units, ESP8266, ESP8285 and ESP32, and also some predefined hardware configurations and sets of plugins & controllers. This has been turned into several different PlatformIO environments, to make managing the different builds as easy as possible. +ESPEasy supports several different configurations of ESP units, ESP8266, ESP8285 and ESP32, and also some predefined hardware configurations and sets of plugins & controllers. This has been turned into several different PIO environments, to make managing the different builds as easy as possible. To compile such 'environment' (PIO terminology), select the PIO button (it looks like an alien) in VSCode: @@ -492,10 +497,10 @@ Especially for new plugins, it is highly recommended to write documentation, as Using external libraries -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: - Since November 2022, the PlatformIO configuration for ESPEasy was changed to *require* all libraries to be locally available, to a) prevent unexpected 'surprises' when an external library is updated, and b) greatly improve build output stability and quality. + Since November 2022, the PIO configuration for ESPEasy was changed to *require* all libraries to be locally available, to a) prevent unexpected 'surprises' when an external library is updated, and b) greatly improve build output stability and quality. While developing a plugin or some other feature, often you use an existing library to re-use that (assumably) proven and tested functionality. To include such library, there is a prerequisite, and some generic steps to take: @@ -516,7 +521,7 @@ Writing documentation Updating, or adding if it does not yet exist, the documentation is a useful activity that should be part of changing or adding to the ESPEasy code. Some of the optional VSCode extensions are specifically aimed at that task. -The documentation is created in the reStructuredText format, using mostly a ``.rst`` extension, and can be built locally by installing the sphinx tool. This can be installed manually by opening a PlatformIO Terminal window in VSCode (an already open PIO Terminal can also be used, when using WSL2 a PlatformIO Terminal is *required* to execute in the correct Python Virtual Environment (venv)) and issuing these commands: +The documentation is created in the reStructuredText format, using mostly a ``.rst`` extension, and can be built locally by installing the sphinx tool. This can be installed manually by opening a PIO Terminal window in VSCode (an already open PIO Terminal can also be used, when using WSL2 a PIO Terminal is *required* to execute in the correct Python Virtual Environment (venv)) and issuing these commands: .. code-block:: diff --git a/docs/source/Plugin/P000.rst b/docs/source/Plugin/P000.rst index d6cd32986..f32f7d88a 100644 --- a/docs/source/Plugin/P000.rst +++ b/docs/source/Plugin/P000.rst @@ -13,7 +13,9 @@ Type: |P000_type| Name: |P000_name| -Status: |P000_status| +Status ESP32: |P000_status| + +Status ESP8266: |P000_status_lb| GitHub: |P000_github|_ diff --git a/docs/source/Plugin/P000_commands.repl b/docs/source/Plugin/P000_commands.repl index 438bebfb1..46c458ecc 100644 --- a/docs/source/Plugin/P000_commands.repl +++ b/docs/source/Plugin/P000_commands.repl @@ -549,37 +549,49 @@ :green:`Rules`"," Like ``PostToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " - ProvisionConfig"," + Provision,Config"," :red:`Internal`"," Fetch ``config.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionConfig``" + ``Provision,Config``" " - ProvisionSecurity"," + Provision,Security"," :red:`Internal`"," Fetch ``security.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionSecurity``" + ``Provision,Security``" " - ProvisionNotification"," + Provision,DevSecurity"," + :red:`Internal`"," + Fetch ``devsecurity.dat`` as configured in the Provisioning configuration (see Settings Archive) + + ``Provision,DevSecurity``" + " + Provision,Notification"," :red:`Internal`"," Fetch ``notification.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionNotification``" + ``Provision,Notification``" " - ProvisionProvision"," + Provision,Provision"," :red:`Internal`"," Fetch ``provisioning.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionProvision``" + ``Provision,Provision``" " - ProvisionRules"," + Provision,Rules"," :red:`Internal`"," Fetch ``rulesN.txt`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionRules,1`` to fetch ``rules1.txt`` " + ``Provision,Rules,1`` to fetch ``rules1.txt`` " + " + Provision,Firmware"," + :red:`Internal`"," + Fetch a new firmware binary, and flash it on the unit, as configured in the Provisioning configuration (see Settings Archive) + + ``Provision,Firmware,`` to fetch and install the specified .bin file from the specified url." " Publish"," :green:`Rules`"," @@ -656,7 +668,7 @@ :green:`Rules`"," Like ``PutToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " Reboot"," :red:`Internal`"," @@ -736,7 +748,7 @@ :green:`Rules`"," Like ``SendToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " SendToUDP"," :green:`Rules`"," diff --git a/docs/source/Plugin/P001.rst b/docs/source/Plugin/P001.rst index fdfe7c2bf..c31aa7641 100644 --- a/docs/source/Plugin/P001.rst +++ b/docs/source/Plugin/P001.rst @@ -13,7 +13,9 @@ Type: |P001_type| Name: |P001_name| -Status: |P001_status| +Status ESP32: |P001_status| + +Status ESP8266: |P001_status_lb| GitHub: |P001_github|_ diff --git a/docs/source/Plugin/P002.rst b/docs/source/Plugin/P002.rst index 84c3f0c20..da8ce0d30 100644 --- a/docs/source/Plugin/P002.rst +++ b/docs/source/Plugin/P002.rst @@ -13,7 +13,9 @@ Type: |P002_type| Name: |P002_name| -Status: |P002_status| +Status ESP32: |P002_status| + +Status ESP8266: |P002_status_lb| GitHub: |P002_github|_ diff --git a/docs/source/Plugin/P003.rst b/docs/source/Plugin/P003.rst index 3f16506c3..fe94b2775 100644 --- a/docs/source/Plugin/P003.rst +++ b/docs/source/Plugin/P003.rst @@ -13,7 +13,9 @@ Type: |P003_type| Name: |P003_name| -Status: |P003_status| +Status ESP32: |P003_status| + +Status ESP8266: |P003_status_lb| GitHub: |P003_github|_ diff --git a/docs/source/Plugin/P004.rst b/docs/source/Plugin/P004.rst index 732e2dfc8..c1c07d232 100644 --- a/docs/source/Plugin/P004.rst +++ b/docs/source/Plugin/P004.rst @@ -13,7 +13,9 @@ Type: |P004_type| Name: |P004_name| -Status: |P004_status| +Status ESP32: |P004_status| + +Status ESP8266: |P004_status_lb| GitHub: |P004_github|_ diff --git a/docs/source/Plugin/P005.rst b/docs/source/Plugin/P005.rst index d1a004bdc..24f597d72 100644 --- a/docs/source/Plugin/P005.rst +++ b/docs/source/Plugin/P005.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p00x.repl +.. include:: ../Plugin/_plugin_substitutions_p00x.repl .. _P005_page: |P005_typename| @@ -13,7 +13,9 @@ Type: |P005_type| Name: |P005_name| -Status: |P005_status| +Status ESP32: |P005_status| + +Status ESP8266: |P005_status_lb| GitHub: |P005_github|_ diff --git a/docs/source/Plugin/P006.rst b/docs/source/Plugin/P006.rst index 77d369d2a..4d19f524c 100644 --- a/docs/source/Plugin/P006.rst +++ b/docs/source/Plugin/P006.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p00x.repl +.. include:: ../Plugin/_plugin_substitutions_p00x.repl .. _P006_page: |P006_typename| @@ -13,7 +13,9 @@ Type: |P006_type| Name: |P006_name| -Status: |P006_status| +Status ESP32: |P006_status| + +Status ESP8266: |P006_status_lb| GitHub: |P006_github|_ diff --git a/docs/source/Plugin/P007.rst b/docs/source/Plugin/P007.rst index 7d6c21fd3..7b6761614 100644 --- a/docs/source/Plugin/P007.rst +++ b/docs/source/Plugin/P007.rst @@ -13,7 +13,9 @@ Type: |P007_type| Name: |P007_name| -Status: |P007_status| +Status ESP32: |P007_status| + +Status ESP8266: |P007_status_lb| GitHub: |P007_github|_ diff --git a/docs/source/Plugin/P008.rst b/docs/source/Plugin/P008.rst index d26e0dd7a..fd419ca05 100644 --- a/docs/source/Plugin/P008.rst +++ b/docs/source/Plugin/P008.rst @@ -13,7 +13,9 @@ Type: |P008_type| Name: |P008_name| -Status: |P008_status| +Status ESP32: |P008_status| + +Status ESP8266: |P008_status_lb| GitHub: |P008_github|_ diff --git a/docs/source/Plugin/P009.rst b/docs/source/Plugin/P009.rst index 17795e354..3f6fa5c76 100644 --- a/docs/source/Plugin/P009.rst +++ b/docs/source/Plugin/P009.rst @@ -13,7 +13,9 @@ Type: |P009_type| Name: |P009_name| -Status: |P009_status| +Status ESP32: |P009_status| + +Status ESP8266: |P009_status_lb| GitHub: |P009_github|_ diff --git a/docs/source/Plugin/P010.rst b/docs/source/Plugin/P010.rst index d6e85d8f1..5cf71a314 100644 --- a/docs/source/Plugin/P010.rst +++ b/docs/source/Plugin/P010.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p01x.repl +.. include:: ../Plugin/_plugin_substitutions_p01x.repl .. _P010_page: |P010_typename| @@ -13,7 +13,9 @@ Type: |P010_type| Name: |P010_name| -Status: |P010_status| +Status ESP32: |P010_status| + +Status ESP8266: |P010_status_lb| GitHub: |P010_github|_ diff --git a/docs/source/Plugin/P011.rst b/docs/source/Plugin/P011.rst index 49d87eb1d..224c25176 100644 --- a/docs/source/Plugin/P011.rst +++ b/docs/source/Plugin/P011.rst @@ -13,7 +13,9 @@ Type: |P011_type| Name: |P011_name| -Status: |P011_status| +Status ESP32: |P011_status| + +Status ESP8266: |P011_status_lb| GitHub: |P011_github|_ diff --git a/docs/source/Plugin/P012.rst b/docs/source/Plugin/P012.rst index 0ef6a14e5..ad0b175e4 100644 --- a/docs/source/Plugin/P012.rst +++ b/docs/source/Plugin/P012.rst @@ -13,7 +13,9 @@ Type: |P012_type| Name: |P012_name| -Status: |P012_status| +Status ESP32: |P012_status| + +Status ESP8266: |P012_status_lb| GitHub: |P012_github|_ diff --git a/docs/source/Plugin/P013.rst b/docs/source/Plugin/P013.rst index 823e2f2d6..272bc3ed8 100644 --- a/docs/source/Plugin/P013.rst +++ b/docs/source/Plugin/P013.rst @@ -13,7 +13,9 @@ Type: |P013_type| Name: |P013_name| -Status: |P013_status| +Status ESP32: |P013_status| + +Status ESP8266: |P013_status_lb| GitHub: |P013_github|_ diff --git a/docs/source/Plugin/P014.rst b/docs/source/Plugin/P014.rst index a87b8b042..0b33d6197 100644 --- a/docs/source/Plugin/P014.rst +++ b/docs/source/Plugin/P014.rst @@ -13,7 +13,9 @@ Type: |P014_type| Name: |P014_name| -Status: |P014_status| +Status ESP32: |P014_status| + +Status ESP8266: |P014_status_lb| GitHub: |P014_github|_ diff --git a/docs/source/Plugin/P015.rst b/docs/source/Plugin/P015.rst index 23b650ba8..0bafb71ab 100644 --- a/docs/source/Plugin/P015.rst +++ b/docs/source/Plugin/P015.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p01x.repl +.. include:: ../Plugin/_plugin_substitutions_p01x.repl .. _P015_page: |P015_typename| @@ -13,7 +13,9 @@ Type: |P015_type| Name: |P015_name| -Status: |P015_status| +Status ESP32: |P015_status| + +Status ESP8266: |P015_status_lb| GitHub: |P015_github|_ diff --git a/docs/source/Plugin/P016.rst b/docs/source/Plugin/P016.rst index 1a8df528b..0bd891512 100644 --- a/docs/source/Plugin/P016.rst +++ b/docs/source/Plugin/P016.rst @@ -13,7 +13,9 @@ Type: |P016_type| Name: |P016_name| -Status: |P016_status| +Status ESP32: |P016_status| + +Status ESP8266: |P016_status_lb| GitHub: |P016_github|_ diff --git a/docs/source/Plugin/P017.rst b/docs/source/Plugin/P017.rst index 882afbc72..00ec5baf3 100644 --- a/docs/source/Plugin/P017.rst +++ b/docs/source/Plugin/P017.rst @@ -13,7 +13,9 @@ Type: |P017_type| Name: |P017_name| -Status: |P017_status| +Status ESP32: |P017_status| + +Status ESP8266: |P017_status_lb| GitHub: |P017_github|_ diff --git a/docs/source/Plugin/P018.rst b/docs/source/Plugin/P018.rst index 1c30edcee..6a8d8f93e 100644 --- a/docs/source/Plugin/P018.rst +++ b/docs/source/Plugin/P018.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p01x.repl +.. include:: ../Plugin/_plugin_substitutions_p01x.repl .. _P018_page: |P018_typename| @@ -13,7 +13,9 @@ Type: |P018_type| Name: |P018_name| -Status: |P018_status| +Status ESP32: |P018_status| + +Status ESP8266: |P018_status_lb| GitHub: |P018_github|_ diff --git a/docs/source/Plugin/P019.rst b/docs/source/Plugin/P019.rst index 11ee24a0e..bb7ee7efa 100644 --- a/docs/source/Plugin/P019.rst +++ b/docs/source/Plugin/P019.rst @@ -13,7 +13,9 @@ Type: |P019_type| Name: |P019_name| -Status: |P019_status| +Status ESP32: |P019_status| + +Status ESP8266: |P019_status_lb| GitHub: |P019_github|_ diff --git a/docs/source/Plugin/P020.rst b/docs/source/Plugin/P020.rst index 5d9732003..dbbe4acbe 100644 --- a/docs/source/Plugin/P020.rst +++ b/docs/source/Plugin/P020.rst @@ -13,7 +13,9 @@ Type: |P020_type| Name: |P020_name| -Status: |P020_status| +Status ESP32: |P020_status| + +Status ESP8266: |P020_status_lb| GitHub: |P020_github|_ diff --git a/docs/source/Plugin/P021.rst b/docs/source/Plugin/P021.rst index 851c622e0..be8658435 100644 --- a/docs/source/Plugin/P021.rst +++ b/docs/source/Plugin/P021.rst @@ -13,7 +13,9 @@ Type: |P021_type| Name: |P021_name| -Status: |P021_status| +Status ESP32: |P021_status| + +Status ESP8266: |P021_status_lb| GitHub: |P021_github|_ diff --git a/docs/source/Plugin/P022.rst b/docs/source/Plugin/P022.rst index 29e7b5a96..e7000dbb1 100644 --- a/docs/source/Plugin/P022.rst +++ b/docs/source/Plugin/P022.rst @@ -13,7 +13,9 @@ Type: |P022_type| Name: |P022_name| -Status: |P022_status| +Status ESP32: |P022_status| + +Status ESP8266: |P022_status_lb| GitHub: |P022_github|_ diff --git a/docs/source/Plugin/P023.rst b/docs/source/Plugin/P023.rst index 1f02fcb0f..8461c5e1f 100644 --- a/docs/source/Plugin/P023.rst +++ b/docs/source/Plugin/P023.rst @@ -13,7 +13,9 @@ Type: |P023_type| Name: |P023_name| -Status: |P023_status| +Status ESP32: |P023_status| + +Status ESP8266: |P023_status_lb| GitHub: |P023_github|_ diff --git a/docs/source/Plugin/P024.rst b/docs/source/Plugin/P024.rst index db988701a..bf1650780 100644 --- a/docs/source/Plugin/P024.rst +++ b/docs/source/Plugin/P024.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p02x.repl +.. include:: ../Plugin/_plugin_substitutions_p02x.repl .. _P024_page: |P024_typename| @@ -13,7 +13,9 @@ Type: |P024_type| Name: |P024_name| -Status: |P024_status| +Status ESP32: |P024_status| + +Status ESP8266: |P024_status_lb| GitHub: |P024_github|_ diff --git a/docs/source/Plugin/P025.rst b/docs/source/Plugin/P025.rst index 42932641d..da0fc993a 100644 --- a/docs/source/Plugin/P025.rst +++ b/docs/source/Plugin/P025.rst @@ -13,7 +13,9 @@ Type: |P025_type| Name: |P025_name| -Status: |P025_status| +Status ESP32: |P025_status| + +Status ESP8266: |P025_status_lb| GitHub: |P025_github|_ diff --git a/docs/source/Plugin/P026.rst b/docs/source/Plugin/P026.rst index 0d3e873eb..df0c0a1e6 100644 --- a/docs/source/Plugin/P026.rst +++ b/docs/source/Plugin/P026.rst @@ -13,7 +13,9 @@ Type: |P026_type| Name: |P026_name| -Status: |P026_status| +Status ESP32: |P026_status| + +Status ESP8266: |P026_status_lb| GitHub: |P026_github|_ diff --git a/docs/source/Plugin/P027.rst b/docs/source/Plugin/P027.rst index 0311e6c3c..cde0e7b7c 100644 --- a/docs/source/Plugin/P027.rst +++ b/docs/source/Plugin/P027.rst @@ -13,7 +13,9 @@ Type: |P027_type| Name: |P027_name| -Status: |P027_status| +Status ESP32: |P027_status| + +Status ESP8266: |P027_status_lb| GitHub: |P027_github|_ diff --git a/docs/source/Plugin/P028.rst b/docs/source/Plugin/P028.rst index 529143a4c..fff9d3f78 100644 --- a/docs/source/Plugin/P028.rst +++ b/docs/source/Plugin/P028.rst @@ -13,7 +13,9 @@ Type: |P028_type| Name: |P028_name| -Status: |P028_status| +Status ESP32: |P028_status| + +Status ESP8266: |P028_status_lb| GitHub: |P028_github|_ diff --git a/docs/source/Plugin/P029.rst b/docs/source/Plugin/P029.rst index cd57c4c6a..99aef75ae 100644 --- a/docs/source/Plugin/P029.rst +++ b/docs/source/Plugin/P029.rst @@ -13,7 +13,9 @@ Type: |P029_type| Name: |P029_name| -Status: |P029_status| +Status ESP32: |P029_status| + +Status ESP8266: |P029_status_lb| GitHub: |P029_github|_ diff --git a/docs/source/Plugin/P030.rst b/docs/source/Plugin/P030.rst index 1dcbb4c8e..15d887044 100644 --- a/docs/source/Plugin/P030.rst +++ b/docs/source/Plugin/P030.rst @@ -6,7 +6,9 @@ Name: |P030_name| -Status: |P030_status| +Status ESP32: |P030_status| + +Status ESP8266: |P030_status_lb| .. note:: This plugin has been replaced by :ref:`p028_page` (though that will need reconfiguration) diff --git a/docs/source/Plugin/P031.rst b/docs/source/Plugin/P031.rst index b54dd9489..a65368636 100644 --- a/docs/source/Plugin/P031.rst +++ b/docs/source/Plugin/P031.rst @@ -13,7 +13,9 @@ Type: |P031_type| Name: |P031_name| -Status: |P031_status| +Status ESP32: |P031_status| + +Status ESP8266: |P031_status_lb| GitHub: |P031_github|_ diff --git a/docs/source/Plugin/P032.rst b/docs/source/Plugin/P032.rst index 19953d336..c020246fa 100644 --- a/docs/source/Plugin/P032.rst +++ b/docs/source/Plugin/P032.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p03x.repl +.. include:: ../Plugin/_plugin_substitutions_p03x.repl .. _P032_page: |P032_typename| @@ -13,7 +13,9 @@ Type: |P032_type| Name: |P032_name| -Status: |P032_status| +Status ESP32: |P032_status| + +Status ESP8266: |P032_status_lb| GitHub: |P032_github|_ diff --git a/docs/source/Plugin/P033.rst b/docs/source/Plugin/P033.rst index 2cc260e88..d7db4e7dc 100644 --- a/docs/source/Plugin/P033.rst +++ b/docs/source/Plugin/P033.rst @@ -13,7 +13,9 @@ Type: |P033_type| Name: |P033_name| -Status: |P033_status| +Status ESP32: |P033_status| + +Status ESP8266: |P033_status_lb| GitHub: |P033_github|_ diff --git a/docs/source/Plugin/P034.rst b/docs/source/Plugin/P034.rst index 7a01dab26..aa2475350 100644 --- a/docs/source/Plugin/P034.rst +++ b/docs/source/Plugin/P034.rst @@ -13,7 +13,9 @@ Type: |P034_type| Name: |P034_name| -Status: |P034_status| +Status ESP32: |P034_status| + +Status ESP8266: |P034_status_lb| GitHub: |P034_github|_ diff --git a/docs/source/Plugin/P035.rst b/docs/source/Plugin/P035.rst index 6c37d192b..f58cb5c5a 100644 --- a/docs/source/Plugin/P035.rst +++ b/docs/source/Plugin/P035.rst @@ -13,7 +13,9 @@ Type: |P035_type| Name: |P035_name| -Status: |P035_status| +Status ESP32: |P035_status| + +Status ESP8266: |P035_status_lb| GitHub: |P035_github|_ diff --git a/docs/source/Plugin/P036.rst b/docs/source/Plugin/P036.rst index 4cba31085..240c94a0e 100644 --- a/docs/source/Plugin/P036.rst +++ b/docs/source/Plugin/P036.rst @@ -13,7 +13,9 @@ Type: |P036_type| Name: |P036_name| -Status: |P036_status| +Status ESP32: |P036_status| + +Status ESP8266: |P036_status_lb| GitHub: |P036_github|_ diff --git a/docs/source/Plugin/P037.rst b/docs/source/Plugin/P037.rst index 6b53535bf..11d70a2b8 100644 --- a/docs/source/Plugin/P037.rst +++ b/docs/source/Plugin/P037.rst @@ -13,7 +13,9 @@ Type: |P037_type| Name: |P037_name| -Status: |P037_status| +Status ESP32: |P037_status| + +Status ESP8266: |P037_status_lb| GitHub: |P037_github|_ diff --git a/docs/source/Plugin/P038.rst b/docs/source/Plugin/P038.rst index e4cf8e458..025ba0a17 100644 --- a/docs/source/Plugin/P038.rst +++ b/docs/source/Plugin/P038.rst @@ -13,7 +13,9 @@ Type: |P038_type| Name: |P038_name| -Status: |P038_status| +Status ESP32: |P038_status| + +Status ESP8266: |P038_status_lb| GitHub: |P038_github|_ diff --git a/docs/source/Plugin/P039.rst b/docs/source/Plugin/P039.rst index 3fbcd1bac..af9653b3a 100644 --- a/docs/source/Plugin/P039.rst +++ b/docs/source/Plugin/P039.rst @@ -13,7 +13,9 @@ Type: |P039_type| Name: |P039_name| -Status: |P039_status| +Status ESP32: |P039_status| + +Status ESP8266: |P039_status_lb| GitHub: |P039_github|_ diff --git a/docs/source/Plugin/P040.rst b/docs/source/Plugin/P040.rst index f315cacf6..96a3ccc73 100644 --- a/docs/source/Plugin/P040.rst +++ b/docs/source/Plugin/P040.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p04x.repl +.. include:: ../Plugin/_plugin_substitutions_p04x.repl .. _P040_page: |P040_typename| @@ -13,7 +13,9 @@ Type: |P040_type| Name: |P040_name| -Status: |P040_status| +Status ESP32: |P040_status| + +Status ESP8266: |P040_status_lb| GitHub: |P040_github|_ diff --git a/docs/source/Plugin/P041.rst b/docs/source/Plugin/P041.rst index b6d609c75..011eee11c 100644 --- a/docs/source/Plugin/P041.rst +++ b/docs/source/Plugin/P041.rst @@ -13,7 +13,9 @@ Type: |P041_type| Name: |P041_name| -Status: |P041_status| +Status ESP32: |P041_status| + +Status ESP8266: |P041_status_lb| GitHub: |P041_github|_ diff --git a/docs/source/Plugin/P042.rst b/docs/source/Plugin/P042.rst index 076a183dc..c8b78b263 100644 --- a/docs/source/Plugin/P042.rst +++ b/docs/source/Plugin/P042.rst @@ -13,7 +13,9 @@ Type: |P042_type| Name: |P042_name| -Status: |P042_status| +Status ESP32: |P042_status| + +Status ESP8266: |P042_status_lb| GitHub: |P042_github|_ diff --git a/docs/source/Plugin/P043.rst b/docs/source/Plugin/P043.rst index ffcc91639..77416b796 100644 --- a/docs/source/Plugin/P043.rst +++ b/docs/source/Plugin/P043.rst @@ -13,7 +13,9 @@ Type: |P043_type| Name: |P043_name| -Status: |P043_status| +Status ESP32: |P043_status| + +Status ESP8266: |P043_status_lb| GitHub: |P043_github|_ diff --git a/docs/source/Plugin/P044.rst b/docs/source/Plugin/P044.rst index 3065fe445..0876a49ee 100644 --- a/docs/source/Plugin/P044.rst +++ b/docs/source/Plugin/P044.rst @@ -13,7 +13,9 @@ Type: |P044_type| Name: |P044_name| -Status: |P044_status| +Status ESP32: |P044_status| + +Status ESP8266: |P044_status_lb| GitHub: |P044_github|_ diff --git a/docs/source/Plugin/P045.rst b/docs/source/Plugin/P045.rst index 7c0293799..e3576e50d 100644 --- a/docs/source/Plugin/P045.rst +++ b/docs/source/Plugin/P045.rst @@ -13,7 +13,9 @@ Type: |P045_type| Name: |P045_name| -Status: |P045_status| +Status ESP32: |P045_status| + +Status ESP8266: |P045_status_lb| GitHub: |P045_github|_ diff --git a/docs/source/Plugin/P046.rst b/docs/source/Plugin/P046.rst index 2e8f055dc..66579accc 100644 --- a/docs/source/Plugin/P046.rst +++ b/docs/source/Plugin/P046.rst @@ -13,7 +13,9 @@ Type: |P046_type| Name: |P046_name| -Status: |P046_status| +Status ESP32: |P046_status| + +Status ESP8266: |P046_status_lb| GitHub: |P046_github|_ diff --git a/docs/source/Plugin/P047.rst b/docs/source/Plugin/P047.rst index 6567d0536..2b3f9aacd 100644 --- a/docs/source/Plugin/P047.rst +++ b/docs/source/Plugin/P047.rst @@ -13,7 +13,9 @@ Type: |P047_type| Name: |P047_name| -Status: |P047_status| +Status ESP32: |P047_status| + +Status ESP8266: |P047_status_lb| GitHub: |P047_github|_ diff --git a/docs/source/Plugin/P048.rst b/docs/source/Plugin/P048.rst index 777c666cf..1a35e9a6f 100644 --- a/docs/source/Plugin/P048.rst +++ b/docs/source/Plugin/P048.rst @@ -13,7 +13,9 @@ Type: |P048_type| Name: |P048_name| -Status: |P048_status| +Status ESP32: |P048_status| + +Status ESP8266: |P048_status_lb| GitHub: |P048_github|_ diff --git a/docs/source/Plugin/P049.rst b/docs/source/Plugin/P049.rst index d214deacf..06b6879ed 100644 --- a/docs/source/Plugin/P049.rst +++ b/docs/source/Plugin/P049.rst @@ -15,7 +15,9 @@ Port Type: |P049_porttype| Name: |P049_name| -Status: |P049_status| +Status ESP32: |P049_status| + +Status ESP8266: |P049_status_lb| GitHub: |P049_github|_ diff --git a/docs/source/Plugin/P050.rst b/docs/source/Plugin/P050.rst index 3c96f7e7c..dc229e218 100644 --- a/docs/source/Plugin/P050.rst +++ b/docs/source/Plugin/P050.rst @@ -13,7 +13,9 @@ Type: |P050_type| Name: |P050_name| -Status: |P050_status| +Status ESP32: |P050_status| + +Status ESP8266: |P050_status_lb| GitHub: |P050_github|_ diff --git a/docs/source/Plugin/P051.rst b/docs/source/Plugin/P051.rst index fb6133bc1..263b63ff0 100644 --- a/docs/source/Plugin/P051.rst +++ b/docs/source/Plugin/P051.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p05x.repl +.. include:: ../Plugin/_plugin_substitutions_p05x.repl .. _P051_page: |P051_typename| @@ -13,7 +13,9 @@ Type: |P051_type| Name: |P051_name| -Status: |P051_status| +Status ESP32: |P051_status| + +Status ESP8266: |P051_status_lb| GitHub: |P051_github|_ diff --git a/docs/source/Plugin/P052.rst b/docs/source/Plugin/P052.rst index 0890a8a64..c54ea0c4a 100644 --- a/docs/source/Plugin/P052.rst +++ b/docs/source/Plugin/P052.rst @@ -15,7 +15,9 @@ Port Type: |P052_porttype| Name: |P052_name| -Status: |P052_status| +Status ESP32: |P052_status| + +Status ESP8266: |P052_status_lb| GitHub: |P052_github|_ diff --git a/docs/source/Plugin/P053.rst b/docs/source/Plugin/P053.rst index e44ed465e..a85dd2ff2 100644 --- a/docs/source/Plugin/P053.rst +++ b/docs/source/Plugin/P053.rst @@ -15,7 +15,9 @@ Port Type: |P053_porttype| Name: |P053_name| -Status: |P053_status| +Status ESP32: |P053_status| + +Status ESP8266: |P053_status_lb| GitHub: |P053_github|_ diff --git a/docs/source/Plugin/P054.rst b/docs/source/Plugin/P054.rst index e1cf1f906..00032097b 100644 --- a/docs/source/Plugin/P054.rst +++ b/docs/source/Plugin/P054.rst @@ -13,7 +13,9 @@ Type: |P054_type| Name: |P054_name| -Status: |P054_status| +Status ESP32: |P054_status| + +Status ESP8266: |P054_status_lb| GitHub: |P054_github|_ diff --git a/docs/source/Plugin/P055.rst b/docs/source/Plugin/P055.rst index 2cd6a774d..8be2e1e5b 100644 --- a/docs/source/Plugin/P055.rst +++ b/docs/source/Plugin/P055.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p05x.repl +.. include:: ../Plugin/_plugin_substitutions_p05x.repl .. _P055_page: |P055_typename| @@ -13,7 +13,9 @@ Type: |P055_type| Name: |P055_name| -Status: |P055_status| +Status ESP32: |P055_status| + +Status ESP8266: |P055_status_lb| GitHub: |P055_github|_ diff --git a/docs/source/Plugin/P056.rst b/docs/source/Plugin/P056.rst index 1be2d1896..37ad42012 100644 --- a/docs/source/Plugin/P056.rst +++ b/docs/source/Plugin/P056.rst @@ -15,7 +15,9 @@ Port Type: |P056_porttype| Name: |P056_name| -Status: |P056_status| +Status ESP32: |P056_status| + +Status ESP8266: |P056_status_lb| GitHub: |P056_github|_ diff --git a/docs/source/Plugin/P057.rst b/docs/source/Plugin/P057.rst index 505a98480..b0cb84105 100644 --- a/docs/source/Plugin/P057.rst +++ b/docs/source/Plugin/P057.rst @@ -13,7 +13,9 @@ Type: |P057_type| Name: |P057_name| -Status: |P057_status| +Status ESP32: |P057_status| + +Status ESP8266: |P057_status_lb| GitHub: |P057_github|_ diff --git a/docs/source/Plugin/P058.rst b/docs/source/Plugin/P058.rst index 205df2465..a79be9ce5 100644 --- a/docs/source/Plugin/P058.rst +++ b/docs/source/Plugin/P058.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p05x.repl +.. include:: ../Plugin/_plugin_substitutions_p05x.repl .. _P058_page: |P058_typename| @@ -13,7 +13,9 @@ Type: |P058_type| Name: |P058_name| -Status: |P058_status| +Status ESP32: |P058_status| + +Status ESP8266: |P058_status_lb| GitHub: |P058_github|_ diff --git a/docs/source/Plugin/P059.rst b/docs/source/Plugin/P059.rst index c25af1f86..4ca1b5f83 100644 --- a/docs/source/Plugin/P059.rst +++ b/docs/source/Plugin/P059.rst @@ -13,7 +13,9 @@ Type: |P059_type| Name: |P059_name| -Status: |P059_status| +Status ESP32: |P059_status| + +Status ESP8266: |P059_status_lb| GitHub: |P059_github|_ diff --git a/docs/source/Plugin/P060.rst b/docs/source/Plugin/P060.rst index 49f3a5a4c..4ed928f19 100644 --- a/docs/source/Plugin/P060.rst +++ b/docs/source/Plugin/P060.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p06x.repl +.. include:: ../Plugin/_plugin_substitutions_p06x.repl .. _P060_page: |P060_typename| @@ -13,7 +13,9 @@ Type: |P060_type| Name: |P060_name| -Status: |P060_status| +Status ESP32: |P060_status| + +Status ESP8266: |P060_status_lb| GitHub: |P060_github|_ diff --git a/docs/source/Plugin/P061.rst b/docs/source/Plugin/P061.rst index 63134f09b..574c7698d 100644 --- a/docs/source/Plugin/P061.rst +++ b/docs/source/Plugin/P061.rst @@ -13,7 +13,9 @@ Type: |P061_type| Name: |P061_name| -Status: |P061_status| +Status ESP32: |P061_status| + +Status ESP8266: |P061_status_lb| GitHub: |P061_github|_ diff --git a/docs/source/Plugin/P062.rst b/docs/source/Plugin/P062.rst index 511614729..e9b1575f7 100644 --- a/docs/source/Plugin/P062.rst +++ b/docs/source/Plugin/P062.rst @@ -13,7 +13,9 @@ Type: |P062_type| Name: |P062_name| -Status: |P062_status| +Status ESP32: |P062_status| + +Status ESP8266: |P062_status_lb| GitHub: |P062_github|_ diff --git a/docs/source/Plugin/P063.rst b/docs/source/Plugin/P063.rst index 98f9a285a..0246fe502 100644 --- a/docs/source/Plugin/P063.rst +++ b/docs/source/Plugin/P063.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p06x.repl +.. include:: ../Plugin/_plugin_substitutions_p06x.repl .. _P063_page: |P063_typename| @@ -13,7 +13,9 @@ Type: |P063_type| Name: |P063_name| -Status: |P063_status| +Status ESP32: |P063_status| + +Status ESP8266: |P063_status_lb| GitHub: |P063_github|_ diff --git a/docs/source/Plugin/P064.rst b/docs/source/Plugin/P064.rst index 901a6ed27..9f1874acb 100644 --- a/docs/source/Plugin/P064.rst +++ b/docs/source/Plugin/P064.rst @@ -13,7 +13,9 @@ Type: |P064_type| Name: |P064_name| -Status: |P064_status| +Status ESP32: |P064_status| + +Status ESP8266: |P064_status_lb| GitHub: |P064_github|_ diff --git a/docs/source/Plugin/P065.rst b/docs/source/Plugin/P065.rst index 5d9b14c0b..13ce6f5c6 100644 --- a/docs/source/Plugin/P065.rst +++ b/docs/source/Plugin/P065.rst @@ -13,7 +13,9 @@ Type: |P065_type| Name: |P065_name| -Status: |P065_status| +Status ESP32: |P065_status| + +Status ESP8266: |P065_status_lb| GitHub: |P065_github|_ diff --git a/docs/source/Plugin/P066.rst b/docs/source/Plugin/P066.rst index cd25404a1..aac91d68e 100644 --- a/docs/source/Plugin/P066.rst +++ b/docs/source/Plugin/P066.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p06x.repl +.. include:: ../Plugin/_plugin_substitutions_p06x.repl .. _P066_page: |P066_typename| @@ -13,7 +13,9 @@ Type: |P066_type| Name: |P066_name| -Status: |P066_status| +Status ESP32: |P066_status| + +Status ESP8266: |P066_status_lb| GitHub: |P066_github|_ diff --git a/docs/source/Plugin/P067.rst b/docs/source/Plugin/P067.rst index 8bab4f204..dacc8c1c7 100644 --- a/docs/source/Plugin/P067.rst +++ b/docs/source/Plugin/P067.rst @@ -13,7 +13,9 @@ Type: |P067_type| Name: |P067_name| -Status: |P067_status| +Status ESP32: |P067_status| + +Status ESP8266: |P067_status_lb| GitHub: |P067_github|_ diff --git a/docs/source/Plugin/P068.rst b/docs/source/Plugin/P068.rst index 552ab24c7..fee94dc21 100644 --- a/docs/source/Plugin/P068.rst +++ b/docs/source/Plugin/P068.rst @@ -13,7 +13,9 @@ Type: |P068_type| Name: |P068_name| -Status: |P068_status| +Status ESP32: |P068_status| + +Status ESP8266: |P068_status_lb| GitHub: |P068_github|_ diff --git a/docs/source/Plugin/P069.rst b/docs/source/Plugin/P069.rst index ef983ba99..dbd782c46 100644 --- a/docs/source/Plugin/P069.rst +++ b/docs/source/Plugin/P069.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p06x.repl +.. include:: ../Plugin/_plugin_substitutions_p06x.repl .. _P069_page: |P069_typename| @@ -13,7 +13,9 @@ Type: |P069_type| Name: |P069_name| -Status: |P069_status| +Status ESP32: |P069_status| + +Status ESP8266: |P069_status_lb| GitHub: |P069_github|_ diff --git a/docs/source/Plugin/P070.rst b/docs/source/Plugin/P070.rst index 1399debc2..70dea8ed8 100644 --- a/docs/source/Plugin/P070.rst +++ b/docs/source/Plugin/P070.rst @@ -13,7 +13,9 @@ Type: |P070_type| Name: |P070_name| -Status: |P070_status| +Status ESP32: |P070_status| + +Status ESP8266: |P070_status_lb| GitHub: |P070_github|_ diff --git a/docs/source/Plugin/P071.rst b/docs/source/Plugin/P071.rst index 0b321bb9c..26825d935 100644 --- a/docs/source/Plugin/P071.rst +++ b/docs/source/Plugin/P071.rst @@ -15,7 +15,9 @@ Port Type: |P071_porttype| Name: |P071_name| -Status: |P071_status| +Status ESP32: |P071_status| + +Status ESP8266: |P071_status_lb| GitHub: |P071_github|_ diff --git a/docs/source/Plugin/P072.rst b/docs/source/Plugin/P072.rst index 3c40537fe..414d6254a 100644 --- a/docs/source/Plugin/P072.rst +++ b/docs/source/Plugin/P072.rst @@ -13,7 +13,9 @@ Type: |P072_type| Name: |P072_name| -Status: |P072_status| +Status ESP32: |P072_status| + +Status ESP8266: |P072_status_lb| GitHub: |P072_github|_ diff --git a/docs/source/Plugin/P073.rst b/docs/source/Plugin/P073.rst index 11185fa0c..1125448d4 100644 --- a/docs/source/Plugin/P073.rst +++ b/docs/source/Plugin/P073.rst @@ -13,7 +13,9 @@ Type: |P073_type| Name: |P073_name| -Status: |P073_status| +Status ESP32: |P073_status| + +Status ESP8266: |P073_status_lb| GitHub: |P073_github|_ diff --git a/docs/source/Plugin/P074.rst b/docs/source/Plugin/P074.rst index 385d5d76e..6c96f58cb 100644 --- a/docs/source/Plugin/P074.rst +++ b/docs/source/Plugin/P074.rst @@ -15,7 +15,9 @@ Port Type: |P074_porttype| Name: |P074_name| -Status: |P074_status| +Status ESP32: |P074_status| + +Status ESP8266: |P074_status_lb| GitHub: |P074_github|_ diff --git a/docs/source/Plugin/P075.rst b/docs/source/Plugin/P075.rst index 7792c91d3..9aa67264d 100644 --- a/docs/source/Plugin/P075.rst +++ b/docs/source/Plugin/P075.rst @@ -16,7 +16,9 @@ Type: |P075_type| Name: |P075_name| -Status: |P075_status| +Status ESP32: |P075_status| + +Status ESP8266: |P075_status_lb| GitHub: |P075_github|_ diff --git a/docs/source/Plugin/P076.rst b/docs/source/Plugin/P076.rst index 1fba7f82f..fc4201161 100644 --- a/docs/source/Plugin/P076.rst +++ b/docs/source/Plugin/P076.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p07x.repl +.. include:: ../Plugin/_plugin_substitutions_p07x.repl .. _P076_page: |P076_typename| @@ -13,7 +13,9 @@ Type: |P076_type| Name: |P076_name| -Status: |P076_status| +Status ESP32: |P076_status| + +Status ESP8266: |P076_status_lb| GitHub: |P076_github|_ diff --git a/docs/source/Plugin/P077.rst b/docs/source/Plugin/P077.rst index f60d887e2..cca21f59f 100644 --- a/docs/source/Plugin/P077.rst +++ b/docs/source/Plugin/P077.rst @@ -13,7 +13,9 @@ Type: |P077_type| Name: |P077_name| -Status: |P077_status| +Status ESP32: |P077_status| + +Status ESP8266: |P077_status_lb| GitHub: |P077_github|_ diff --git a/docs/source/Plugin/P078.rst b/docs/source/Plugin/P078.rst index 2aacf3f8b..7c3d7f9ef 100644 --- a/docs/source/Plugin/P078.rst +++ b/docs/source/Plugin/P078.rst @@ -15,7 +15,9 @@ Port Type: |P078_porttype| Name: |P078_name| -Status: |P078_status| +Status ESP32: |P078_status| + +Status ESP8266: |P078_status_lb| GitHub: |P078_github|_ diff --git a/docs/source/Plugin/P079.rst b/docs/source/Plugin/P079.rst index 4db6b46ac..d10ba4f93 100644 --- a/docs/source/Plugin/P079.rst +++ b/docs/source/Plugin/P079.rst @@ -15,7 +15,9 @@ Port Type: |P079_porttype| Name: |P079_name| -Status: |P079_status| +Status ESP32: |P079_status| + +Status ESP8266: |P079_status_lb| GitHub: |P079_github|_ diff --git a/docs/source/Plugin/P080.rst b/docs/source/Plugin/P080.rst index 2ec21ee4f..fb48ed857 100644 --- a/docs/source/Plugin/P080.rst +++ b/docs/source/Plugin/P080.rst @@ -13,7 +13,9 @@ Type: |P080_type| Name: |P080_name| -Status: |P080_status| +Status ESP32: |P080_status| + +Status ESP8266: |P080_status_lb| GitHub: |P080_github|_ diff --git a/docs/source/Plugin/P081.rst b/docs/source/Plugin/P081.rst index 232f6a703..792f8b358 100644 --- a/docs/source/Plugin/P081.rst +++ b/docs/source/Plugin/P081.rst @@ -13,7 +13,9 @@ Type: |P081_type| Name: |P081_name| -Status: |P081_status| +Status ESP32: |P081_status| + +Status ESP8266: |P081_status_lb| GitHub: |P081_github|_ diff --git a/docs/source/Plugin/P082.rst b/docs/source/Plugin/P082.rst index 5b3c3dea5..db01a1246 100644 --- a/docs/source/Plugin/P082.rst +++ b/docs/source/Plugin/P082.rst @@ -15,7 +15,9 @@ Port Type: |P082_porttype| Name: |P082_name| -Status: |P082_status| +Status ESP32: |P082_status| + +Status ESP8266: |P082_status_lb| GitHub: |P082_github|_ diff --git a/docs/source/Plugin/P083.rst b/docs/source/Plugin/P083.rst index 7e0958363..697de10de 100644 --- a/docs/source/Plugin/P083.rst +++ b/docs/source/Plugin/P083.rst @@ -13,7 +13,9 @@ Type: |P083_type| Name: |P083_name| -Status: |P083_status| +Status ESP32: |P083_status| + +Status ESP8266: |P083_status_lb| GitHub: |P083_github|_ diff --git a/docs/source/Plugin/P084.rst b/docs/source/Plugin/P084.rst index ae99791f6..8267afac5 100644 --- a/docs/source/Plugin/P084.rst +++ b/docs/source/Plugin/P084.rst @@ -13,7 +13,9 @@ Type: |P084_type| Name: |P084_name| -Status: |P084_status| +Status ESP32: |P084_status| + +Status ESP8266: |P084_status_lb| GitHub: |P084_github|_ diff --git a/docs/source/Plugin/P085.rst b/docs/source/Plugin/P085.rst index c257c0e3b..be08074f1 100644 --- a/docs/source/Plugin/P085.rst +++ b/docs/source/Plugin/P085.rst @@ -15,7 +15,9 @@ Port Type: |P085_porttype| Name: |P085_name| -Status: |P085_status| +Status ESP32: |P085_status| + +Status ESP8266: |P085_status_lb| GitHub: |P085_github|_ diff --git a/docs/source/Plugin/P086_Homie.rst b/docs/source/Plugin/P086.rst similarity index 98% rename from docs/source/Plugin/P086_Homie.rst rename to docs/source/Plugin/P086.rst index 9e9f2d7a0..c1873ffe1 100644 --- a/docs/source/Plugin/P086_Homie.rst +++ b/docs/source/Plugin/P086.rst @@ -13,7 +13,9 @@ Type: |P086_type| Name: |P086_name| -Status: |P086_status| +Status ESP32: |P086_status| + +Status ESP8266: |P086_status_lb| GitHub: |P086_github|_ diff --git a/docs/source/Plugin/P087.rst b/docs/source/Plugin/P087.rst index df4f1420d..c269949ba 100644 --- a/docs/source/Plugin/P087.rst +++ b/docs/source/Plugin/P087.rst @@ -15,7 +15,9 @@ Port Type: |P087_porttype| Name: |P087_name| -Status: |P087_status| +Status ESP32: |P087_status| + +Status ESP8266: |P087_status_lb| GitHub: |P087_github|_ diff --git a/docs/source/Plugin/P088.rst b/docs/source/Plugin/P088.rst index 34b3d7acf..ab3b5e1a7 100644 --- a/docs/source/Plugin/P088.rst +++ b/docs/source/Plugin/P088.rst @@ -13,7 +13,9 @@ Type: |P088_type| Name: |P088_name| -Status: |P088_status| +Status ESP32: |P088_status| + +Status ESP8266: |P088_status_lb| GitHub: |P088_github|_ diff --git a/docs/source/Plugin/P089.rst b/docs/source/Plugin/P089.rst index dbe0bc90e..6d467f1e0 100644 --- a/docs/source/Plugin/P089.rst +++ b/docs/source/Plugin/P089.rst @@ -13,7 +13,9 @@ Type: |P089_type| Name: |P089_name| -Status: |P089_status| +Status ESP32: |P089_status| + +Status ESP8266: |P089_status_lb| GitHub: |P089_github|_ diff --git a/docs/source/Plugin/P090.rst b/docs/source/Plugin/P090.rst index 92db938c6..a19301ec5 100644 --- a/docs/source/Plugin/P090.rst +++ b/docs/source/Plugin/P090.rst @@ -13,7 +13,9 @@ Type: |P090_type| Name: |P090_name| -Status: |P090_status| +Status ESP32: |P090_status| + +Status ESP8266: |P090_status_lb| GitHub: |P090_github|_ diff --git a/docs/source/Plugin/P091.rst b/docs/source/Plugin/P091.rst index 5621435f2..80b580b8d 100644 --- a/docs/source/Plugin/P091.rst +++ b/docs/source/Plugin/P091.rst @@ -13,7 +13,9 @@ Type: |P091_type| Name: |P091_name| -Status: |P091_status| +Status ESP32: |P091_status| + +Status ESP8266: |P091_status_lb| GitHub: |P091_github|_ diff --git a/docs/source/Plugin/P092.rst b/docs/source/Plugin/P092.rst index 8f6a5ba1f..af454d62c 100644 --- a/docs/source/Plugin/P092.rst +++ b/docs/source/Plugin/P092.rst @@ -13,7 +13,9 @@ Type: |P092_type| Name: |P092_name| -Status: |P092_status| +Status ESP32: |P092_status| + +Status ESP8266: |P092_status_lb| GitHub: |P092_github|_ diff --git a/docs/source/Plugin/P093.rst b/docs/source/Plugin/P093.rst index 105aad122..823c8d072 100644 --- a/docs/source/Plugin/P093.rst +++ b/docs/source/Plugin/P093.rst @@ -13,7 +13,9 @@ Type: |P093_type| Name: |P093_name| -Status: |P093_status| +Status ESP32: |P093_status| + +Status ESP8266: |P093_status_lb| GitHub: |P093_github|_ diff --git a/docs/source/Plugin/P094.rst b/docs/source/Plugin/P094.rst index 2553869b2..feda43d5f 100644 --- a/docs/source/Plugin/P094.rst +++ b/docs/source/Plugin/P094.rst @@ -13,7 +13,9 @@ Type: |P094_type| Name: |P094_name| -Status: |P094_status| +Status ESP32: |P094_status| + +Status ESP8266: |P094_status_lb| GitHub: |P094_github|_ diff --git a/docs/source/Plugin/P095.rst b/docs/source/Plugin/P095.rst index 203331c75..50fef8148 100644 --- a/docs/source/Plugin/P095.rst +++ b/docs/source/Plugin/P095.rst @@ -13,7 +13,9 @@ Type: |P095_type| Name: |P095_name| -Status: |P095_status| +Status ESP32: |P095_status| + +Status ESP8266: |P095_status_lb| GitHub: |P095_github|_ diff --git a/docs/source/Plugin/P097.rst b/docs/source/Plugin/P097.rst index 88a22a8a2..f25da5ce5 100644 --- a/docs/source/Plugin/P097.rst +++ b/docs/source/Plugin/P097.rst @@ -13,7 +13,9 @@ Type: |P097_type| Name: |P097_name| -Status: |P097_status| +Status ESP32: |P097_status| + +Status ESP8266: |P097_status_lb| GitHub: |P097_github|_ diff --git a/docs/source/Plugin/P098.rst b/docs/source/Plugin/P098.rst index 48f78edcf..11f802a0a 100644 --- a/docs/source/Plugin/P098.rst +++ b/docs/source/Plugin/P098.rst @@ -13,7 +13,9 @@ Type: |P098_type| Name: |P098_name| -Status: |P098_status| +Status ESP32: |P098_status| + +Status ESP8266: |P098_status_lb| GitHub: |P098_github|_ diff --git a/docs/source/Plugin/P099.rst b/docs/source/Plugin/P099.rst index c67fda1a9..1ccb6edf5 100644 --- a/docs/source/Plugin/P099.rst +++ b/docs/source/Plugin/P099.rst @@ -13,7 +13,9 @@ Type: |P099_type| Name: |P099_name| -Status: |P099_status| +Status ESP32: |P099_status| + +Status ESP8266: |P099_status_lb| GitHub: |P099_github|_ diff --git a/docs/source/Plugin/P100.rst b/docs/source/Plugin/P100.rst index c2315e01c..280acf8a7 100644 --- a/docs/source/Plugin/P100.rst +++ b/docs/source/Plugin/P100.rst @@ -13,7 +13,9 @@ Type: |P100_type| Name: |P100_name| -Status: |P100_status| +Status ESP32: |P100_status| + +Status ESP8266: |P100_status_lb| GitHub: |P100_github|_ diff --git a/docs/source/Plugin/P101.rst b/docs/source/Plugin/P101.rst index 967edbd4d..1edbd0c97 100644 --- a/docs/source/Plugin/P101.rst +++ b/docs/source/Plugin/P101.rst @@ -16,7 +16,9 @@ Name: |P101_name| Type: |P101_type| -Status: |P101_status| +Status ESP32: |P101_status| + +Status ESP8266: |P101_status_lb| GitHub: |P101_github|_ diff --git a/docs/source/Plugin/P102.rst b/docs/source/Plugin/P102.rst index 580305b73..977cd557f 100644 --- a/docs/source/Plugin/P102.rst +++ b/docs/source/Plugin/P102.rst @@ -13,7 +13,9 @@ Type: |P102_type| Name: |P102_name| -Status: |P102_status| +Status ESP32: |P102_status| + +Status ESP8266: |P102_status_lb| GitHub: |P102_github|_ diff --git a/docs/source/Plugin/P103.rst b/docs/source/Plugin/P103.rst index 3919afe46..51807d3df 100644 --- a/docs/source/Plugin/P103.rst +++ b/docs/source/Plugin/P103.rst @@ -13,7 +13,9 @@ Type: |P103_type| Name: |P103_name| -Status: |P103_status| +Status ESP32: |P103_status| + +Status ESP8266: |P103_status_lb| GitHub: |P103_github|_ diff --git a/docs/source/Plugin/P104-Font-Cyrillic-ext.rst b/docs/source/Plugin/P104-Font-Cyrillic-ext.rst new file mode 100644 index 000000000..9a3e6b01c --- /dev/null +++ b/docs/source/Plugin/P104-Font-Cyrillic-ext.rst @@ -0,0 +1,9 @@ +.. include:: ../Plugin/_plugin_substitutions_p10x.repl +.. _P104-Font-Cyrillic-ext_page: + +|P104-Font-Cyrillic-ext_typename| +====================================================== + +A set of Cyrillic characters, as an extension to the regular ASCII characters, numerics and punctuation. + +.. image:: P104_Font_Cyrillic-ext.png \ No newline at end of file diff --git a/docs/source/Plugin/P104.rst b/docs/source/Plugin/P104.rst index d4e36df2d..334002200 100644 --- a/docs/source/Plugin/P104.rst +++ b/docs/source/Plugin/P104.rst @@ -13,7 +13,9 @@ Type: |P104_type| Name: |P104_name| -Status: |P104_status| +Status ESP32: |P104_status| + +Status ESP8266: |P104_status_lb| GitHub: |P104_github|_ @@ -227,9 +229,9 @@ Available options: .. image:: P104_ZoneFontOptions.png :alt: Zone font options -All available fonts have been taken from the example code of the used Parola library. +Most available fonts have been taken from the example code of the used Parola library, and the Cyrillic font from a post in the arduino.ru forum. -The Arabic, Greek and Katakana fonts have all their characters in the 'high-ascii' range (> 128 ascii values), so they can (and should) **not** be used to 'translate' normal text to Arabic, Greek or Katakana. +The Arabic, Greek, Katakana and Cyrillic fonts have all their characters in the 'high-ascii' range (> 128 ascii values), so they can (and should) **not** be used to 'translate' normal text to Arabic, Greek or Katakana. On 'bin-size-challenged' builds the *Full, double height* font is replaced by the *Numeric, double height* font, to limit the bin-size a bit more. This font can display only numeric (actually hexadecimal, 0..9 and A..F) and date/time content. @@ -244,6 +246,7 @@ Font overview: * :ref:`P104-Font-Arabic_page` * :ref:`P104-Font-Greek_page` * :ref:`P104-Font-Katakana_page` +* :ref:`P104-Font-Cyrillic-ext_page` .. spacer diff --git a/docs/source/Plugin/P104_Font_Cyrillic-ext.png b/docs/source/Plugin/P104_Font_Cyrillic-ext.png new file mode 100644 index 000000000..021505c56 Binary files /dev/null and b/docs/source/Plugin/P104_Font_Cyrillic-ext.png differ diff --git a/docs/source/Plugin/P104_ZoneFontOptions.png b/docs/source/Plugin/P104_ZoneFontOptions.png index 037682e93..d344d19c7 100644 Binary files a/docs/source/Plugin/P104_ZoneFontOptions.png and b/docs/source/Plugin/P104_ZoneFontOptions.png differ diff --git a/docs/source/Plugin/P105.rst b/docs/source/Plugin/P105.rst index ead7d850c..5a6bb7793 100644 --- a/docs/source/Plugin/P105.rst +++ b/docs/source/Plugin/P105.rst @@ -13,7 +13,9 @@ Type: |P105_type| Name: |P105_name| -Status: |P105_status| +Status ESP32: |P105_status| + +Status ESP8266: |P105_status_lb| GitHub: |P105_github|_ diff --git a/docs/source/Plugin/P106.rst b/docs/source/Plugin/P106.rst index 1bbf059a3..f161bb8f2 100644 --- a/docs/source/Plugin/P106.rst +++ b/docs/source/Plugin/P106.rst @@ -13,7 +13,9 @@ Type: |P106_type| Name: |P106_name| -Status: |P106_status| +Status ESP32: |P106_status| + +Status ESP8266: |P106_status_lb| GitHub: |P106_github|_ diff --git a/docs/source/Plugin/P107.rst b/docs/source/Plugin/P107.rst index 319027cea..a31f3f7d7 100644 --- a/docs/source/Plugin/P107.rst +++ b/docs/source/Plugin/P107.rst @@ -13,7 +13,9 @@ Type: |P107_type| Name: |P107_name| -Status: |P107_status| +Status ESP32: |P107_status| + +Status ESP8266: |P107_status_lb| GitHub: |P107_github|_ diff --git a/docs/source/Plugin/P108.rst b/docs/source/Plugin/P108.rst index 23fafaf79..39ad26892 100644 --- a/docs/source/Plugin/P108.rst +++ b/docs/source/Plugin/P108.rst @@ -13,7 +13,9 @@ Type: |P108_type| Name: |P108_name| -Status: |P108_status| +Status ESP32: |P108_status| + +Status ESP8266: |P108_status_lb| GitHub: |P108_github|_ diff --git a/docs/source/Plugin/P109.rst b/docs/source/Plugin/P109.rst index 061ebefff..1e877f968 100644 --- a/docs/source/Plugin/P109.rst +++ b/docs/source/Plugin/P109.rst @@ -13,7 +13,9 @@ Type: |P109_type| Name: |P109_name| -Status: |P109_status| +Status ESP32: |P109_status| + +Status ESP8266: |P109_status_lb| GitHub: |P109_github|_ diff --git a/docs/source/Plugin/P110.rst b/docs/source/Plugin/P110.rst index 81b334023..f0189291d 100644 --- a/docs/source/Plugin/P110.rst +++ b/docs/source/Plugin/P110.rst @@ -13,7 +13,9 @@ Type: |P110_type| Name: |P110_name| -Status: |P110_status| +Status ESP32: |P110_status| + +Status ESP8266: |P110_status_lb| GitHub: |P110_github|_ diff --git a/docs/source/Plugin/P111.rst b/docs/source/Plugin/P111.rst index dffe177d1..f61e07bdb 100644 --- a/docs/source/Plugin/P111.rst +++ b/docs/source/Plugin/P111.rst @@ -13,7 +13,9 @@ Type: |P111_type| Name: |P111_name| -Status: |P111_status| +Status ESP32: |P111_status| + +Status ESP8266: |P111_status_lb| GitHub: |P111_github|_ diff --git a/docs/source/Plugin/P112.rst b/docs/source/Plugin/P112.rst index a18c09944..8f16af8ad 100644 --- a/docs/source/Plugin/P112.rst +++ b/docs/source/Plugin/P112.rst @@ -13,7 +13,9 @@ Type: |P112_type| Name: |P112_name| -Status: |P112_status| +Status ESP32: |P112_status| + +Status ESP8266: |P112_status_lb| GitHub: |P112_github|_ diff --git a/docs/source/Plugin/P113.rst b/docs/source/Plugin/P113.rst index b16b3810f..3ee7f8423 100644 --- a/docs/source/Plugin/P113.rst +++ b/docs/source/Plugin/P113.rst @@ -13,7 +13,9 @@ Type: |P113_type| Name: |P113_name| -Status: |P113_status| +Status ESP32: |P113_status| + +Status ESP8266: |P113_status_lb| GitHub: |P113_github|_ diff --git a/docs/source/Plugin/P114.rst b/docs/source/Plugin/P114.rst index 8d414ebc6..8d17e78d9 100644 --- a/docs/source/Plugin/P114.rst +++ b/docs/source/Plugin/P114.rst @@ -13,7 +13,9 @@ Type: |P114_type| Name: |P114_name| -Status: |P114_status| +Status ESP32: |P114_status| + +Status ESP8266: |P114_status_lb| GitHub: |P114_github|_ diff --git a/docs/source/Plugin/P115.rst b/docs/source/Plugin/P115.rst index 5d72a2f89..488301cdc 100644 --- a/docs/source/Plugin/P115.rst +++ b/docs/source/Plugin/P115.rst @@ -13,7 +13,9 @@ Type: |P115_type| Name: |P115_name| -Status: |P115_status| +Status ESP32: |P115_status| + +Status ESP8266: |P115_status_lb| GitHub: |P115_github|_ diff --git a/docs/source/Plugin/P116.rst b/docs/source/Plugin/P116.rst index 4b1309747..5fe73b21a 100644 --- a/docs/source/Plugin/P116.rst +++ b/docs/source/Plugin/P116.rst @@ -13,7 +13,9 @@ Type: |P116_type| Name: |P116_name| -Status: |P116_status| +Status ESP32: |P116_status| + +Status ESP8266: |P116_status_lb| GitHub: |P116_github|_ diff --git a/docs/source/Plugin/P117.rst b/docs/source/Plugin/P117.rst index 9fe2008e6..328c24bfa 100644 --- a/docs/source/Plugin/P117.rst +++ b/docs/source/Plugin/P117.rst @@ -13,7 +13,9 @@ Type: |P117_type| Name: |P117_name| -Status: |P117_status| +Status ESP32: |P117_status| + +Status ESP8266: |P117_status_lb| GitHub: |P117_github|_ diff --git a/docs/source/Plugin/P118.rst b/docs/source/Plugin/P118.rst index dc5a12ade..df7aa5d53 100644 --- a/docs/source/Plugin/P118.rst +++ b/docs/source/Plugin/P118.rst @@ -13,7 +13,9 @@ Type: |P118_type| Name: |P118_name| -Status: |P118_status| +Status ESP32: |P118_status| + +Status ESP8266: |P118_status_lb| GitHub: |P118_github|_ diff --git a/docs/source/Plugin/P119.rst b/docs/source/Plugin/P119.rst index ba315baea..b6ce30efb 100644 --- a/docs/source/Plugin/P119.rst +++ b/docs/source/Plugin/P119.rst @@ -13,7 +13,9 @@ Type: |P119_type| Name: |P119_name| -Status: |P119_status| +Status ESP32: |P119_status| + +Status ESP8266: |P119_status_lb| GitHub: |P119_github|_ diff --git a/docs/source/Plugin/P120.rst b/docs/source/Plugin/P120.rst index 837d6a581..e15a2a3d2 100644 --- a/docs/source/Plugin/P120.rst +++ b/docs/source/Plugin/P120.rst @@ -13,7 +13,9 @@ Type: |P120_type| Name: |P120_name| -Status: |P120_status| +Status ESP32: |P120_status| + +Status ESP8266: |P120_status_lb| GitHub: |P120_github|_ diff --git a/docs/source/Plugin/P121.rst b/docs/source/Plugin/P121.rst index e974ac663..4aab0841d 100644 --- a/docs/source/Plugin/P121.rst +++ b/docs/source/Plugin/P121.rst @@ -14,7 +14,9 @@ Type: |P121_type| Name: |P121_name| -Status: |P121_status| +Status ESP32: |P121_status| + +Status ESP8266: |P121_status_lb| GitHub: |P121_github|_ diff --git a/docs/source/Plugin/P122.rst b/docs/source/Plugin/P122.rst index 4fcc96aa8..f32a01f78 100644 --- a/docs/source/Plugin/P122.rst +++ b/docs/source/Plugin/P122.rst @@ -13,7 +13,9 @@ Type: |P122_type| Name: |P122_name| -Status: |P122_status| +Status ESP32: |P122_status| + +Status ESP8266: |P122_status_lb| GitHub: |P122_github|_ diff --git a/docs/source/Plugin/P123.rst b/docs/source/Plugin/P123.rst index e2e27af94..8186c84f7 100644 --- a/docs/source/Plugin/P123.rst +++ b/docs/source/Plugin/P123.rst @@ -13,7 +13,9 @@ Type: |P123_type| Name: |P123_name| -Status: |P123_status| +Status ESP32: |P123_status| + +Status ESP8266: |P123_status_lb| GitHub: |P123_github|_ diff --git a/docs/source/Plugin/P124.rst b/docs/source/Plugin/P124.rst index b593324a4..afda82faa 100644 --- a/docs/source/Plugin/P124.rst +++ b/docs/source/Plugin/P124.rst @@ -13,7 +13,9 @@ Type: |P124_type| Name: |P124_name| -Status: |P124_status| +Status ESP32: |P124_status| + +Status ESP8266: |P124_status_lb| GitHub: |P124_github|_ diff --git a/docs/source/Plugin/P125.rst b/docs/source/Plugin/P125.rst index 4b3f4a2aa..5cd407de4 100644 --- a/docs/source/Plugin/P125.rst +++ b/docs/source/Plugin/P125.rst @@ -13,7 +13,9 @@ Type: |P125_type| Name: |P125_name| -Status: |P125_status| +Status ESP32: |P125_status| + +Status ESP8266: |P125_status_lb| GitHub: |P125_github|_ diff --git a/docs/source/Plugin/P126.rst b/docs/source/Plugin/P126.rst index 5e79b017a..3ebddcf63 100644 --- a/docs/source/Plugin/P126.rst +++ b/docs/source/Plugin/P126.rst @@ -13,7 +13,9 @@ Type: |P126_type| Name: |P126_name| -Status: |P126_status| +Status ESP32: |P126_status| + +Status ESP8266: |P126_status_lb| GitHub: |P126_github|_ diff --git a/docs/source/Plugin/P127.rst b/docs/source/Plugin/P127.rst index 1c0293009..2128f77a4 100644 --- a/docs/source/Plugin/P127.rst +++ b/docs/source/Plugin/P127.rst @@ -13,7 +13,9 @@ Type: |P127_type| Name: |P127_name| -Status: |P127_status| +Status ESP32: |P127_status| + +Status ESP8266: |P127_status_lb| GitHub: |P127_github|_ diff --git a/docs/source/Plugin/P128.rst b/docs/source/Plugin/P128.rst index 4c4cbf517..f5329c6f8 100644 --- a/docs/source/Plugin/P128.rst +++ b/docs/source/Plugin/P128.rst @@ -13,7 +13,9 @@ Type: |P128_type| Name: |P128_name| -Status: |P128_status| +Status ESP32: |P128_status| + +Status ESP8266: |P128_status_lb| GitHub: |P128_github|_ diff --git a/docs/source/Plugin/P129.rst b/docs/source/Plugin/P129.rst index 662972271..9412ee3cd 100644 --- a/docs/source/Plugin/P129.rst +++ b/docs/source/Plugin/P129.rst @@ -13,7 +13,9 @@ Type: |P129_type| Name: |P129_name| -Status: |P129_status| +Status ESP32: |P129_status| + +Status ESP8266: |P129_status_lb| GitHub: |P129_github|_ diff --git a/docs/source/Plugin/P131.rst b/docs/source/Plugin/P131.rst index a8daa50bc..5869c0962 100644 --- a/docs/source/Plugin/P131.rst +++ b/docs/source/Plugin/P131.rst @@ -13,7 +13,9 @@ Type: |P131_type| Name: |P131_name| -Status: |P131_status| +Status ESP32: |P131_status| + +Status ESP8266: |P131_status_lb| GitHub: |P131_github|_ diff --git a/docs/source/Plugin/P132.rst b/docs/source/Plugin/P132.rst index 9e36b88a5..facf4d2ec 100644 --- a/docs/source/Plugin/P132.rst +++ b/docs/source/Plugin/P132.rst @@ -13,7 +13,9 @@ Type: |P132_type| Name: |P132_name| -Status: |P132_status| +Status ESP32: |P132_status| + +Status ESP8266: |P132_status_lb| GitHub: |P132_github|_ @@ -24,6 +26,159 @@ Used libraries: |P132_usedlibraries| Description ----------- +This plugin had support for INA3221 only, and has been extended with support for more types of INA sensors, though this extension is by default not enabled for ESP8266 builds. That version is available in :ref:`original-documentation`. + +Support for INAxxx DC Voltage/Current/Power measurement sensors, INA3221, INA219, INA226, INA228, INA230, INA231 and INA260. + +Existing plugin settings are forward compatible with the new settings. + +Configuration +------------- + +.. image:: P132_DeviceConfigurationV2.png + :alt: Device Configuration + +* **Name** A unique name should be entered here. + +* **Enabled** The device can be disabled or enabled. When not enabled the device should not use any resources. + +I2C Options +^^^^^^^^^^^^ + +The available settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page` + +* **I2C Address**: The address the sensor is using. The different addresses can be selected by connecting the appropriate signal to ``A0`` and ``A1`` when available. Often also documented on the board. + +For ``INA3221`` the range is limited to the first 4 addresses, for other INA types, 16 addresses are available: + +Selectable I2C addresses: + +.. csv-table:: + :header: "Address", "A0", "A1", "" + :widths: 10, 10, 10, 70 + + "0x40","GND","GND","" + "0x41","VS+","GND","" + "0x42","SDA","GND","" + "0x43","SCL","GND","" + "0x44","GND","VS+","" + "0x45","VS+","VS+","" + "0x46","SDA","VS+","" + "0x47","SCL","VS+","" + "0x48","GND","SDA","" + "0x49","VS+","SDA","" + "0x4A","SDA","SDA","" + "0x4B","SCL","SDA","" + "0x4C","GND","SCL","" + "0x4D","VS+","SCL","" + "0x4E","SDA","SCL","" + "0x4F","SCL","SCL","" + + +Device Settings +^^^^^^^^^^^^^^^ + +* **INA type**: Select the INA chip type you want to connect. This has to match with what is auto-detected by the library. As INA231 is often recognized as an INA226, this is an accepted alternative. And also the other way around. The difference is only in the specifications of the chip, from software side they are practically identical. + +.. image:: P132_InaTypeOptionsV2.png + +NB: INA219 is already supported by :ref:`P027_page` but is still added here as it is supported by the used library. + +* **Max. Current**: Configure the max. to be expected/measured current, so the library can optimize the configuration combined with the **Conversion rate** and **Shunt resistor** settings, below. Range is ``1..1022 A``, default set at ``10``. + +* **Power value 1..4**: Most INA types have 3 ``Power values`` available, only INA3221 has 9 values available. As a plugin only has 4 available output values, here we have to select what output receives what measured value. + +Available options for ``INA3221`` are: + +.. image:: P132_PowerValueOptionsV2_INA3221.png + :alt: Available Power Value options + +* *Voltage channel 1..3*: Measure the voltage from channel 1..3. +* *Current channel 1..3*: Measure the current from channel 1..3. +* *Power channel 1..3*: Calculated power from channel 1..3. + +Other INA types the available options are: + +.. image:: P132_PowerValueOptionsV2.png + :alt: Available Power Value options + +* *Voltage*: Measure the voltage in V. +* *Current*: Measure the current in mA. +* *Power*: Measure the power in mW. (Needs ``VBus`` to be connected to the sensor) + + +Hardware +^^^^^^^^ + +* **Shunt resistor**: Here you can select the resistance or shunt that is used, this should match closely with the **Max. Current** setting: + +.. image:: P132_ShuntResistorOptionsV2.png + :alt: Available options for shunt resistor values + +On many boards a shunt resistor is installed, that has one of the available resistance values. When larger currents are to be measured, an external shunt is mounted, and the sensor is connected via the Kelvin Connection that are available on the shunt (the small screws). The pre-installed shunt resistor must then be removed from the sensor-board, to avoid possible damage. These shunt are mostly specified for the current at wich a measurable voltage of 75 mV is reached. Some preselected values, up to the max. range of the sensor of ca. 1022 A, are selectable. + +NB: ``INA260`` has an internal shunt resistor of ``2 mOhm``, so when selecting that **INA type**, the setting is fixed at that value. + +Measurement +^^^^^^^^^^^ + +* **Averaging samples**: To increase the accuracy of measurements, the chip allows to take the average value for a set of samples. The desired number of samples can be selected in this setting, default: 1. + +.. image:: P132_AveragingSamplesOptions.png + :alt: Averaging samples options + +This setting is *not* available for ``INA219``, as this chip has the Averaging samples setting combined with the **Conversion rate**. + +When combining a higher number of samples with a longer **Conversion rate**, taking all possible measurements will require more time, affecting the **Interval** that would be feasible for a reliable measurement. The worst-case scenarios (maximum **Conversion rate** settings) have been calculated, and are shown as a note below this setting. + +* **Conversion rate Voltage/Current**: To change the accuracy of measurements, a different conversion rate can be selected. Lowering the conversion rate will decrease the accuracy, and allow a higher sampling frequency, but as the measurements are read at a smallest **Interval** of 1 second, there isn't much use in lowering the conversion rate. When increasing the conversion rate, accuracy will increase, but, also depending on the **Averaging samples** configured, increases the time it takes to complete all measurements. (Calculation formula below). + +There are separate **Conversion rate** settings for the **Voltage** and **Current** measurements, as that's available in the chip. + +.. image:: P132_ConversionRateOptions.png + :alt: Conversion rate options + +To calculate the minimally required **Interval** setting for the task, this formula can be used: + + | (3 * Averaging samples * Conversion rate Voltage) + (3 * Averaging samples * Conversion rate Current), and round that up to a seconds value. + + | Example: (Worst case) 3 * 1024 * 0.008244 + 3 * 1024 * 0.008244 = 50.65 seconds => ~51 minimum Interval. + + | Example: (Default case) 3 * 1 * 0.00144 + 3 * 1 * 0.00144 = 0.008 seconds => 1 minimum Interval. + +For ``INA219`` a different **Conversion rate** selector is available: + +.. image:: P132_ConversionRateOptionsV2_INA219.png + :alt: Conversion rate options + + +Also for ``INA228`` a different **Conversion rate** selector is available: + +.. image:: P132_ConversionRateOptionsV2_INA228.png + :alt: Conversion rate options + + +.. include:: DataAcquisition.repl + +Values +^^^^^^ + +The Values available for this sensor, are named ``Value1`` through ``Value4``, and can be adjusted as desired. The Formula field can be used to recalculate the shown results. Decimals should probably be set too. + +Per Value is a **Stats** checkbox available, that when checked, gathers the data and presents recent data in a graph, as described here: :ref:`Task Value Statistics: ` + +| + +| + +.. _original-documentation: + +Original documentation +---------------------- + +Description +----------- + The INA3221 Voltage/Current sensor can be used to measure voltage and/or current for DC input, up to 26V. It has 3 separate channels available. It can be used to replace the :ref:`p027_page`, except that the I2C protocol isn't directly compatible, so it is not a drop-in replacement. @@ -119,6 +274,8 @@ Change log .. versionchanged:: 2.0 ... + |added| 2025-12-27 Extended adding INA219, INA226, INA228, INA230, INA231 and INA260. + |added| 2022-04-23 Initial release version. diff --git a/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png new file mode 100644 index 000000000..0c35fa66d Binary files /dev/null and b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png differ diff --git a/docs/source/Plugin/P132_ConversionRateOptionsV2_INA228.png b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA228.png new file mode 100644 index 000000000..f2484bf7a Binary files /dev/null and b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA228.png differ diff --git a/docs/source/Plugin/P132_DeviceConfigurationV2.png b/docs/source/Plugin/P132_DeviceConfigurationV2.png new file mode 100644 index 000000000..4260a8323 Binary files /dev/null and b/docs/source/Plugin/P132_DeviceConfigurationV2.png differ diff --git a/docs/source/Plugin/P132_InaTypeOptionsV2.png b/docs/source/Plugin/P132_InaTypeOptionsV2.png new file mode 100644 index 000000000..c3bf21bd0 Binary files /dev/null and b/docs/source/Plugin/P132_InaTypeOptionsV2.png differ diff --git a/docs/source/Plugin/P132_PowerValueOptionsV2.png b/docs/source/Plugin/P132_PowerValueOptionsV2.png new file mode 100644 index 000000000..02da1e2e9 Binary files /dev/null and b/docs/source/Plugin/P132_PowerValueOptionsV2.png differ diff --git a/docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png b/docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png new file mode 100644 index 000000000..76d62a8e0 Binary files /dev/null and b/docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png differ diff --git a/docs/source/Plugin/P132_ShuntResistorOptionsV2.png b/docs/source/Plugin/P132_ShuntResistorOptionsV2.png new file mode 100644 index 000000000..13bb56a9d Binary files /dev/null and b/docs/source/Plugin/P132_ShuntResistorOptionsV2.png differ diff --git a/docs/source/Plugin/P133.rst b/docs/source/Plugin/P133.rst index 9abb02060..8c74e5954 100644 --- a/docs/source/Plugin/P133.rst +++ b/docs/source/Plugin/P133.rst @@ -13,7 +13,9 @@ Type: |P133_type| Name: |P133_name| -Status: |P133_status| +Status ESP32: |P133_status| + +Status ESP8266: |P133_status_lb| GitHub: |P133_github|_ diff --git a/docs/source/Plugin/P134.rst b/docs/source/Plugin/P134.rst index d56e2083b..5e7ed6254 100644 --- a/docs/source/Plugin/P134.rst +++ b/docs/source/Plugin/P134.rst @@ -13,7 +13,9 @@ Type: |P134_type| Name: |P134_name| -Status: |P134_status| +Status ESP32: |P134_status| + +Status ESP8266: |P134_status_lb| GitHub: |P134_github|_ diff --git a/docs/source/Plugin/P135.rst b/docs/source/Plugin/P135.rst index d88317a5e..b0ef729eb 100644 --- a/docs/source/Plugin/P135.rst +++ b/docs/source/Plugin/P135.rst @@ -13,7 +13,9 @@ Type: |P135_type| Name: |P135_name| -Status: |P135_status| +Status ESP32: |P135_status| + +Status ESP8266: |P135_status_lb| GitHub: |P135_github|_ diff --git a/docs/source/Plugin/P137.rst b/docs/source/Plugin/P137.rst index 8050593fb..f31ed4a07 100644 --- a/docs/source/Plugin/P137.rst +++ b/docs/source/Plugin/P137.rst @@ -13,7 +13,9 @@ Type: |P137_type| Name: |P137_name| -Status: |P137_status| +Status ESP32: |P137_status| + +Status ESP8266: |P137_status_lb| GitHub: |P137_github|_ diff --git a/docs/source/Plugin/P138.rst b/docs/source/Plugin/P138.rst index 86c61af15..8e1b597fd 100644 --- a/docs/source/Plugin/P138.rst +++ b/docs/source/Plugin/P138.rst @@ -13,7 +13,9 @@ Type: |P138_type| Name: |P138_name| -Status: |P138_status| +Status ESP32: |P138_status| + +Status ESP8266: |P138_status_lb| GitHub: |P138_github|_ diff --git a/docs/source/Plugin/P139.rst b/docs/source/Plugin/P139.rst index 8ee3f22ab..b0f74ba68 100644 --- a/docs/source/Plugin/P139.rst +++ b/docs/source/Plugin/P139.rst @@ -13,7 +13,9 @@ Type: |P139_type| Name: |P139_name| -Status: |P139_status| +Status ESP32: |P139_status| + +Status ESP8266: |P139_status_lb| GitHub: |P139_github|_ diff --git a/docs/source/Plugin/P140.rst b/docs/source/Plugin/P140.rst index 27809d207..b57093f55 100644 --- a/docs/source/Plugin/P140.rst +++ b/docs/source/Plugin/P140.rst @@ -13,7 +13,9 @@ Type: |P140_type| Name: |P140_name| -Status: |P140_status| +Status ESP32: |P140_status| + +Status ESP8266: |P140_status_lb| GitHub: |P140_github|_ diff --git a/docs/source/Plugin/P141.rst b/docs/source/Plugin/P141.rst index 695843205..d7ddccd38 100644 --- a/docs/source/Plugin/P141.rst +++ b/docs/source/Plugin/P141.rst @@ -13,7 +13,9 @@ Type: |P141_type| Name: |P141_name| -Status: |P141_status| +Status ESP32: |P141_status| + +Status ESP8266: |P141_status_lb| GitHub: |P141_github|_ diff --git a/docs/source/Plugin/P142.rst b/docs/source/Plugin/P142.rst index df437914a..fb387a56e 100644 --- a/docs/source/Plugin/P142.rst +++ b/docs/source/Plugin/P142.rst @@ -13,7 +13,9 @@ Type: |P142_type| Name: |P142_name| -Status: |P142_status| +Status ESP32: |P142_status| + +Status ESP8266: |P142_status_lb| GitHub: |P142_github|_ diff --git a/docs/source/Plugin/P143.rst b/docs/source/Plugin/P143.rst index cbb9cc872..1d76dc2f2 100644 --- a/docs/source/Plugin/P143.rst +++ b/docs/source/Plugin/P143.rst @@ -13,7 +13,9 @@ Type: |P143_type| Name: |P143_name| -Status: |P143_status| +Status ESP32: |P143_status| + +Status ESP8266: |P143_status_lb| GitHub: |P143_github|_ diff --git a/docs/source/Plugin/P144.rst b/docs/source/Plugin/P144.rst index 3d2cbfd37..264b96457 100644 --- a/docs/source/Plugin/P144.rst +++ b/docs/source/Plugin/P144.rst @@ -13,7 +13,9 @@ Type: |P144_type| Name: |P144_name| -Status: |P144_status| +Status ESP32: |P144_status| + +Status ESP8266: |P144_status_lb| GitHub: |P144_github|_ diff --git a/docs/source/Plugin/P145.rst b/docs/source/Plugin/P145.rst index a29d5a73c..c6ee3b4d3 100644 --- a/docs/source/Plugin/P145.rst +++ b/docs/source/Plugin/P145.rst @@ -13,7 +13,9 @@ Type: |P145_type| Name: |P145_name| -Status: |P145_status| +Status ESP32: |P145_status| + +Status ESP8266: |P145_status_lb| GitHub: |P145_github|_ diff --git a/docs/source/Plugin/P146.rst b/docs/source/Plugin/P146.rst index 47cee7523..344fb82fb 100644 --- a/docs/source/Plugin/P146.rst +++ b/docs/source/Plugin/P146.rst @@ -13,7 +13,9 @@ Type: |P146_type| Name: |P146_name| -Status: |P146_status| +Status ESP32: |P146_status| + +Status ESP8266: |P146_status_lb| GitHub: |P146_github|_ diff --git a/docs/source/Plugin/P147.rst b/docs/source/Plugin/P147.rst index aad053e19..272995abf 100644 --- a/docs/source/Plugin/P147.rst +++ b/docs/source/Plugin/P147.rst @@ -13,7 +13,9 @@ Type: |P147_type| Name: |P147_name| -Status: |P147_status| +Status ESP32: |P147_status| + +Status ESP8266: |P147_status_lb| GitHub: |P147_github|_ diff --git a/docs/source/Plugin/P148.rst b/docs/source/Plugin/P148.rst index d5f3f2ee4..ee0ed37a0 100644 --- a/docs/source/Plugin/P148.rst +++ b/docs/source/Plugin/P148.rst @@ -13,7 +13,9 @@ Type: |P148_type| Name: |P148_name| -Status: |P148_status| +Status ESP32: |P148_status| + +Status ESP8266: |P148_status_lb| GitHub: |P148_github|_ diff --git a/docs/source/Plugin/P150.rst b/docs/source/Plugin/P150.rst index 120713fea..d7df89bd5 100644 --- a/docs/source/Plugin/P150.rst +++ b/docs/source/Plugin/P150.rst @@ -13,7 +13,9 @@ Type: |P150_type| Name: |P150_name| -Status: |P150_status| +Status ESP32: |P150_status| + +Status ESP8266: |P150_status_lb| GitHub: |P150_github|_ diff --git a/docs/source/Plugin/P151.rst b/docs/source/Plugin/P151.rst index eaf295578..9911bf529 100644 --- a/docs/source/Plugin/P151.rst +++ b/docs/source/Plugin/P151.rst @@ -13,7 +13,9 @@ Type: |P151_type| Name: |P151_name| -Status: |P151_status| +Status ESP32: |P151_status| + +Status ESP8266: |P151_status_lb| GitHub: |P151_github|_ diff --git a/docs/source/Plugin/P152.rst b/docs/source/Plugin/P152.rst index 2951fed78..e8c42fa04 100644 --- a/docs/source/Plugin/P152.rst +++ b/docs/source/Plugin/P152.rst @@ -13,7 +13,9 @@ Type: |P152_type| Name: |P152_name| -Status: |P152_status| +Status ESP32: |P152_status| + +Status ESP8266: |P152_status_lb| GitHub: |P152_github|_ diff --git a/docs/source/Plugin/P153.rst b/docs/source/Plugin/P153.rst index b491cfc6e..5c62c0640 100644 --- a/docs/source/Plugin/P153.rst +++ b/docs/source/Plugin/P153.rst @@ -13,7 +13,9 @@ Type: |P153_type| Name: |P153_name| -Status: |P153_status| +Status ESP32: |P153_status| + +Status ESP8266: |P153_status_lb| GitHub: |P153_github|_ diff --git a/docs/source/Plugin/P154.rst b/docs/source/Plugin/P154.rst index eda33aa06..cfa91eb51 100644 --- a/docs/source/Plugin/P154.rst +++ b/docs/source/Plugin/P154.rst @@ -13,7 +13,9 @@ Type: |P154_type| Name: |P154_name| -Status: |P154_status| +Status ESP32: |P154_status| + +Status ESP8266: |P154_status_lb| GitHub: |P154_github|_ diff --git a/docs/source/Plugin/P159.rst b/docs/source/Plugin/P159.rst index a79975208..0a7d605ea 100644 --- a/docs/source/Plugin/P159.rst +++ b/docs/source/Plugin/P159.rst @@ -13,7 +13,9 @@ Type: |P159_type| Name: |P159_name| -Status: |P159_status| +Status ESP32: |P159_status| + +Status ESP8266: |P159_status_lb| GitHub: |P159_github|_ diff --git a/docs/source/Plugin/P162.rst b/docs/source/Plugin/P162.rst index 12417c60d..dfb7cd1cc 100644 --- a/docs/source/Plugin/P162.rst +++ b/docs/source/Plugin/P162.rst @@ -13,7 +13,9 @@ Type: |P162_type| Name: |P162_name| -Status: |P162_status| +Status ESP32: |P162_status| + +Status ESP8266: |P162_status_lb| GitHub: |P162_github|_ diff --git a/docs/source/Plugin/P163.rst b/docs/source/Plugin/P163.rst index f5de42010..fd7eeb06c 100644 --- a/docs/source/Plugin/P163.rst +++ b/docs/source/Plugin/P163.rst @@ -13,7 +13,9 @@ Type: |P163_type| Name: |P163_name| -Status: |P163_status| +Status ESP32: |P163_status| + +Status ESP8266: |P163_status_lb| GitHub: |P163_github|_ diff --git a/docs/source/Plugin/P164.rst b/docs/source/Plugin/P164.rst index 8f901cabc..ad4957c4a 100644 --- a/docs/source/Plugin/P164.rst +++ b/docs/source/Plugin/P164.rst @@ -13,7 +13,9 @@ Type: |P164_type| Name: |P164_name| -Status: |P164_status| +Status ESP32: |P164_status| + +Status ESP8266: |P164_status_lb| GitHub: |P164_github|_ diff --git a/docs/source/Plugin/P165.rst b/docs/source/Plugin/P165.rst index 0c685a0bc..922d0ef25 100644 --- a/docs/source/Plugin/P165.rst +++ b/docs/source/Plugin/P165.rst @@ -13,7 +13,9 @@ Type: |P165_type| Name: |P165_name| -Status: |P165_status| +Status ESP32: |P165_status| + +Status ESP8266: |P165_status_lb| GitHub: |P165_github|_ diff --git a/docs/source/Plugin/P166.rst b/docs/source/Plugin/P166.rst index b1ed23e58..0247673fd 100644 --- a/docs/source/Plugin/P166.rst +++ b/docs/source/Plugin/P166.rst @@ -13,7 +13,9 @@ Type: |P166_type| Name: |P166_name| -Status: |P166_status| +Status ESP32: |P166_status| + +Status ESP8266: |P166_status_lb| GitHub: |P166_github|_ diff --git a/docs/source/Plugin/P167.rst b/docs/source/Plugin/P167.rst index fc9cca40d..d60680c32 100644 --- a/docs/source/Plugin/P167.rst +++ b/docs/source/Plugin/P167.rst @@ -13,7 +13,9 @@ Type: |P167_type| Name: |P167_name| -Status: |P167_status| +Status ESP32: |P167_status| + +Status ESP8266: |P167_status_lb| GitHub: |P167_github|_ diff --git a/docs/source/Plugin/P168.rst b/docs/source/Plugin/P168.rst index 9a6320868..c676534ed 100644 --- a/docs/source/Plugin/P168.rst +++ b/docs/source/Plugin/P168.rst @@ -13,7 +13,9 @@ Type: |P168_type| Name: |P168_name| -Status: |P168_status| +Status ESP32: |P168_status| + +Status ESP8266: |P168_status_lb| GitHub: |P168_github|_ diff --git a/docs/source/Plugin/P169.rst b/docs/source/Plugin/P169.rst index 593a20180..22374dd35 100644 --- a/docs/source/Plugin/P169.rst +++ b/docs/source/Plugin/P169.rst @@ -13,7 +13,9 @@ Type: |P169_type| Name: |P169_name| -Status: |P169_status| +Status ESP32: |P169_status| + +Status ESP8266: |P169_status_lb| GitHub: |P169_github|_ diff --git a/docs/source/Plugin/P170.rst b/docs/source/Plugin/P170.rst index f6879df71..427260247 100644 --- a/docs/source/Plugin/P170.rst +++ b/docs/source/Plugin/P170.rst @@ -13,7 +13,9 @@ Type: |P170_type| Name: |P170_name| -Status: |P170_status| +Status ESP32: |P170_status| + +Status ESP8266: |P170_status_lb| GitHub: |P170_github|_ diff --git a/docs/source/Plugin/P172.rst b/docs/source/Plugin/P172.rst index 0d6c6e7e1..9545c29a0 100644 --- a/docs/source/Plugin/P172.rst +++ b/docs/source/Plugin/P172.rst @@ -13,7 +13,9 @@ Type: |P172_type| Name: |P172_name| -Status: |P172_status| +Status ESP32: |P172_status| + +Status ESP8266: |P172_status_lb| GitHub: |P172_github|_ diff --git a/docs/source/Plugin/P173.rst b/docs/source/Plugin/P173.rst index f52649c42..1a9691779 100644 --- a/docs/source/Plugin/P173.rst +++ b/docs/source/Plugin/P173.rst @@ -13,7 +13,9 @@ Type: |P173_type| Name: |P173_name| -Status: |P173_status| +Status ESP32: |P173_status| + +Status ESP8266: |P173_status_lb| GitHub: |P173_github|_ diff --git a/docs/source/Plugin/P175.rst b/docs/source/Plugin/P175.rst index de3a1c164..04dbdb32e 100644 --- a/docs/source/Plugin/P175.rst +++ b/docs/source/Plugin/P175.rst @@ -15,7 +15,9 @@ Port Type: |P175_porttype| Name: |P175_name| -Status: |P175_status| +Status ESP32: |P175_status| + +Status ESP8266: |P175_status_lb| GitHub: |P175_github|_ diff --git a/docs/source/Plugin/P176.rst b/docs/source/Plugin/P176.rst index be2209716..5108628e4 100644 --- a/docs/source/Plugin/P176.rst +++ b/docs/source/Plugin/P176.rst @@ -13,7 +13,9 @@ Type: |P176_type| Name: |P176_name| -Status: |P176_status| +Status ESP32: |P176_status| + +Status ESP8266: |P176_status_lb| GitHub: |P176_github|_ diff --git a/docs/source/Plugin/P177.rst b/docs/source/Plugin/P177.rst index 8e7f313b3..d5cf027af 100644 --- a/docs/source/Plugin/P177.rst +++ b/docs/source/Plugin/P177.rst @@ -13,7 +13,9 @@ Type: |P177_type| Name: |P177_name| -Status: |P177_status| +Status ESP32: |P177_status| + +Status ESP8266: |P177_status_lb| GitHub: |P177_github|_ diff --git a/docs/source/Plugin/P178.rst b/docs/source/Plugin/P178.rst index fb1bccd8c..22a3a58b7 100644 --- a/docs/source/Plugin/P178.rst +++ b/docs/source/Plugin/P178.rst @@ -13,7 +13,9 @@ Type: |P178_type| Name: |P178_name| -Status: |P178_status| +Status ESP32: |P178_status| + +Status ESP8266: |P178_status_lb| GitHub: |P178_github|_ diff --git a/docs/source/Plugin/P180.rst b/docs/source/Plugin/P180.rst index bbd986c3c..83ef08c1f 100644 --- a/docs/source/Plugin/P180.rst +++ b/docs/source/Plugin/P180.rst @@ -13,7 +13,9 @@ Type: |P180_type| Name: |P180_name| -Status: |P180_status| +Status ESP32: |P180_status| + +Status ESP8266: |P180_status_lb| GitHub: |P180_github|_ @@ -82,7 +84,7 @@ For each Value1 .. Value4 output, depending on the **Output Data Type** setting, * **Value x type**: Here you can select a specific output type for each Value. This shows a selection of the **Output Data Type** selector, but only the single-value types. This will also be used for sending to Domoticz controllers and for MQTT Auto-Discovery. -* **Cache-Name x (optional)**: When set to a unique name (for this task), the parsed & pre-processed command sequence will be stored in a memory-cache, to *significantly* improve the speed of the plugin in runtime. When not set, the command sequence will be parsed every time the task is run (**Interval** setting or ``taskRun`` command). To be able to use event processing for 1ps, 10ps of 50ps, setting a Cache-Name is required! +* **Cache-Name x**: Should be set to a unique name (for this task), so the parsed & pre-processed command sequence will be stored in a memory-cache, to *significantly* improve the speed of the plugin in runtime. Initially the Cache-name is set to ``c1`` .. ``c4`` (depending on the number of values selected) and can be changed as desired. When empty it will be reset to ``cX`` again. * **I2C Commands x**: The sequence of commands to execute for this value. Nothing will be done if this is empty. The corresponding Value output can be set from another I2C Commands sequence. The currently used vs. the available space is shown next to the field. I2C Commands are explained below. @@ -373,7 +375,7 @@ The plugin should be configured with these settings: * **Output Data Type**: ``Single`` -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``qrsize``, so the I2C Command sequence is cached for much improved execution-performance. And the additional de-duplication uses the ``1ps`` event-interval, and the sequence must be cached for that event to work. +* **Cache-Name 1**: Fill with a useable name, f.e. ``qrsize``, so the I2C Command sequence is cached for much improved execution-performance. And the additional de-duplication uses the ``1ps`` event-interval, and the sequence must be cached for that event to work. * **I2C Commands 1**: @@ -455,7 +457,7 @@ The plugin should be configured with these settings: ``put.b.2.0x3f.0xf9``: Stop continuous measurement. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``sdp``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``sdp``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -517,7 +519,7 @@ The plugin should be configured with these settings: * **Values**: the name can be changed to Distance, and the number of decimals 0. Stats can be enabled, as desired. The Unit of Measure should he set to ``cm``, and the Value Type to ``Distance`` -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -544,7 +546,7 @@ The plugin should be configured with these settings: * **Values**: the first name can be changed to ``Distance``, and the number of decimals 0. Stats can be enabled, as desired. The Unit of Measure should he set to ``cm``, and the Value Type to ``Distance``, the second name can be changed to f.e. ``SignalStrength``, and number of decimals to 0. Stats can be enabled, as desired. No further settings for this value, as it has an undefined unit and value type. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -590,15 +592,15 @@ The plugin should be configured with these settings: ``write.u8.0x0A.0x00``: Set sensor in Suspend mode. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``x``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``x``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: ``read.16le.0x01`` Read 2 bytes starting at register 1, the ``x`` value, and store in first Value field. -* **Cache-Name 2 (optional)**: Fill with a useable name, f.e. ``y``. +* **Cache-Name 2**: Fill with a useable name, f.e. ``y``. * **I2C Commands 2**: ``read.16le.0x03`` Read 2 bytes starting at register 3, the ``y`` value, and store in second Value field. -* **Cache-Name 3 (optional)**: Fill with a useable name, f.e. ``z``. +* **Cache-Name 3**: Fill with a useable name, f.e. ``z``. * **I2C Commands 3**: ``read.16le.0x05`` Read 2 bytes starting at register 5, the ``z`` value, and store in third Value field. diff --git a/docs/source/Plugin/P180_DeviceConfiguration.png b/docs/source/Plugin/P180_DeviceConfiguration.png index 52be493cb..19959d788 100644 Binary files a/docs/source/Plugin/P180_DeviceConfiguration.png and b/docs/source/Plugin/P180_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst index 131b0c33f..87f3366f5 100644 --- a/docs/source/Plugin/_Plugin.rst +++ b/docs/source/Plugin/_Plugin.rst @@ -331,186 +331,187 @@ There are different released versions of ESP Easy: :yellow:`ENERGY` :yellow:`DISPLAY` (split into sets A..B) :yellow:`IR` :yellow:`IRext` :yellow:`NEOPIXEL` :yellow:`CLIMATE` (split into sets A..B) are specialized builds holding all Energy-, Display-, Infra Red- (extended), NeoPixel- and Climate- related plugins. -:yellow:`MAX` is the build that has all plugins that are available in the ESPEasy repository. Available for ESP32 16MB and ESP32 8MB Flash units (available for ESP32 Classic, ESP32-S3 and ESP32-C6). +:yellow:`MAX` is the build that has all plugins that are available in the ESPEasy repository. Available for ESP32 16MB and ESP32 8MB Flash units (available for ESP32-Classic, ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61 and ESP32-P4). :gray:`RETIRED` plugin has been retired and removed from ESPEasy. 2022-07-22: :yellow:`TESTING` builds renamed to :yellow:`COLLECTION`. .. csv-table:: - :header: "Plugin name", "Build set", "Plugin number" - :widths: 10, 8, 5 + :header: "Plugin name", "ESP32 Plugin status","ESP8266 Plugin status", "Plugin number" + :widths: 9, 6, 6, 3 - ":ref:`P000_page`","|P000_status|","P000 (core)" - ":ref:`P001_page`","|P001_status|","P001" - ":ref:`P002_page`","|P002_status|","P002" - ":ref:`P003_page`","|P003_status|","P003" - ":ref:`P004_page`","|P004_status|","P004" - ":ref:`P005_page`","|P005_status|","P005" - ":ref:`P006_page`","|P006_status|","P006" - ":ref:`P007_page`","|P007_status|","P007" - ":ref:`P008_page`","|P008_status|","P008" - ":ref:`P009_page`","|P009_status|","P009" - ":ref:`P010_page`","|P010_status|","P010" - ":ref:`P011_page`","|P011_status|","P011" - ":ref:`P012_page`","|P012_status|","P012" - ":ref:`P013_page`","|P013_status|","P013" - ":ref:`P014_page`","|P014_status|","P014" - ":ref:`P015_page`","|P015_status|","P015" - ":ref:`P016_page`","|P016_status|","P016" - ":ref:`P017_page`","|P017_status|","P017" - ":ref:`P018_page`","|P018_status|","P018" - ":ref:`P019_page`","|P019_status|","P019" - ":ref:`P020_page`","|P020_status|","P020" - ":ref:`P021_page`","|P021_status|","P021" - ":ref:`P022_page`","|P022_status|","P022" - ":ref:`P023_page`","|P023_status|","P023" - ":ref:`P024_page`","|P024_status|","P024" - ":ref:`P025_page`","|P025_status|","P025" - ":ref:`P026_page`","|P026_status|","P026" - ":ref:`P027_page`","|P027_status|","P027" - ":ref:`P028_page`","|P028_status|","P028" - ":ref:`P029_page`","|P029_status|","P029" - ":ref:`P030_page`","|P030_status|","P030" - ":ref:`P031_page`","|P031_status|","P031" - ":ref:`P032_page`","|P032_status|","P032" - ":ref:`P033_page`","|P033_status|","P033" - ":ref:`P034_page`","|P034_status|","P034" - ":ref:`P035_page`","|P035_status|","P035" - ":ref:`P036_page`","|P036_status|","P036" - ":ref:`P037_page`","|P037_status|","P037" - ":ref:`P038_page`","|P038_status|","P038" - ":ref:`P039_page`","|P039_status|","P039" - ":ref:`P040_page`","|P040_status|","P040" - ":ref:`P041_page`","|P041_status|","P041" - ":ref:`P042_page`","|P042_status|","P042" - ":ref:`P043_page`","|P043_status|","P043" - ":ref:`P044_page`","|P044_status|","P044" - ":ref:`P045_page`","|P045_status|","P045" - ":ref:`P046_page`","|P046_status|","P046" - ":ref:`P047_page`","|P047_status|","P047" - ":ref:`P048_page`","|P048_status|","P048" - ":ref:`P049_page`","|P049_status|","P049" - ":ref:`P050_page`","|P050_status|","P050" - ":ref:`P051_page`","|P051_status|","P051" - ":ref:`P052_page`","|P052_status|","P052" - ":ref:`P053_page`","|P053_status|","P053" - ":ref:`P054_page`","|P054_status|","P054" - ":ref:`P055_page`","|P055_status|","P055" - ":ref:`P056_page`","|P056_status|","P056" - ":ref:`P057_page`","|P057_status|","P057" - ":ref:`P058_page`","|P058_status|","P058" - ":ref:`P059_page`","|P059_status|","P059" - ":ref:`P060_page`","|P060_status|","P060" - ":ref:`P061_page`","|P061_status|","P061" - ":ref:`P062_page`","|P062_status|","P062" - ":ref:`P063_page`","|P063_status|","P063" - ":ref:`P064_page`","|P064_status|","P064" - ":ref:`P065_page`","|P065_status|","P065" - ":ref:`P066_page`","|P066_status|","P066" - ":ref:`P067_page`","|P067_status|","P067" - ":ref:`P068_page`","|P068_status|","P068" - ":ref:`P069_page`","|P069_status|","P069" - ":ref:`P070_page`","|P070_status|","P070" - ":ref:`P071_page`","|P071_status|","P071" - ":ref:`P072_page`","|P072_status|","P072" - ":ref:`P073_page`","|P073_status|","P073" - ":ref:`P074_page`","|P074_status|","P074" - ":ref:`P075_page`","|P075_status|","P075" - ":ref:`P076_page`","|P076_status|","P076" - ":ref:`P077_page`","|P077_status|","P077" - ":ref:`P078_page`","|P078_status|","P078" - ":ref:`P079_page`","|P079_status|","P079" - ":ref:`P080_page`","|P080_status|","P080" - ":ref:`P081_page`","|P081_status|","P081" - ":ref:`P082_page`","|P082_status|","P082" - ":ref:`P083_page`","|P083_status|","P083" - ":ref:`P084_page`","|P084_status|","P084" - ":ref:`P085_page`","|P085_status|","P085" - ":ref:`P086_page`","|P086_status|","P086" - ":ref:`P087_page`","|P087_status|","P087" - ":ref:`P088_page`","|P088_status|","P088" - ":ref:`P089_page`","|P089_status|","P089" - ":ref:`P090_page`","|P090_status|","P090" - ":ref:`P091_page`","|P091_status|","P091" - ":ref:`P092_page`","|P092_status|","P092" - ":ref:`P093_page`","|P093_status|","P093" - ":ref:`P094_page`","|P094_status|","P094" - ":ref:`P095_page`","|P095_status| (ESP32)","P095" - ":ref:`P097_page`","|P097_status|","P097" - ":ref:`P098_page`","|P098_status|","P098" - ":ref:`P099_page`","|P099_status|","P099" - ":ref:`P100_page`","|P100_status|","P100" - ":ref:`P101_page`","|P101_status|","P101" - ":ref:`P102_page`","|P102_status|","P102" - ":ref:`P103_page`","|P103_status|","P103" - ":ref:`P104_page`","|P104_status|","P104" - ":ref:`P105_page`","|P105_status|","P105" - ":ref:`P106_page`","|P106_status|","P106" - ":ref:`P107_page`","|P107_status|","P107" - ":ref:`P108_page`","|P108_status|","P108" - ":ref:`P109_page`","|P109_status|","P109" - ":ref:`P110_page`","|P110_status|","P110" - ":ref:`P111_page`","|P111_status|","P111" - ":ref:`P112_page`","|P112_status|","P112" - ":ref:`P113_page`","|P113_status|","P113" - ":ref:`P114_page`","|P114_status|","P114" - ":ref:`P115_page`","|P115_status|","P115" - ":ref:`P116_page`","|P116_status|","P116" - ":ref:`P117_page`","|P117_status|","P117" - ":ref:`P118_page`","|P118_status|","P118" - ":ref:`P119_page`","|P119_status|","P119" - ":ref:`P120_page`","|P120_status|","P120" - ":ref:`P121_page`","|P121_status|","P121" - ":ref:`P122_page`","|P122_status|","P122" - ":ref:`P123_page`","|P123_status|","P123" - ":ref:`P124_page`","|P124_status|","P124" - ":ref:`P125_page`","|P125_status|","P125" - ":ref:`P126_page`","|P126_status|","P126" - ":ref:`P127_page`","|P127_status|","P127" - ":ref:`P128_page`","|P128_status|","P128" - ":ref:`P129_page`","|P129_status|","P129" - ":ref:`P131_page`","|P131_status|","P131" - ":ref:`P132_page`","|P132_status|","P132" - ":ref:`P133_page`","|P133_status|","P133" - ":ref:`P134_page`","|P134_status|","P134" - ":ref:`P135_page`","|P135_status|","P135" - ":ref:`P137_page`","|P137_status|","P137" - ":ref:`P138_page`","|P138_status|","P138" - ":ref:`P139_page`","|P139_status|","P139" - ":ref:`P140_page`","|P140_status|","P140" - ":ref:`P141_page`","|P141_status|","P141" - ":ref:`P142_page`","|P142_status|","P142" - ":ref:`P143_page`","|P143_status|","P143" - ":ref:`P144_page`","|P144_status|","P144" - ":ref:`P145_page`","|P145_status|","P145" - ":ref:`P146_page`","|P146_status|","P146" - ":ref:`P147_page`","|P147_status|","P147" - ":ref:`P148_page`","|P148_status|","P148" - ":ref:`P150_page`","|P150_status|","P150" - ":ref:`P151_page`","|P151_status|","P151" - ":ref:`P152_page`","|P152_status|","P152" - ":ref:`P153_page`","|P153_status|","P153" - ":ref:`P154_page`","|P154_status|","P154" - ":ref:`P159_page`","|P159_status|","P159" - ":ref:`P162_page`","|P162_status|","P162" - ":ref:`P163_page`","|P163_status|","P163" - ":ref:`P164_page`","|P164_status|","P164" - ":ref:`P165_page`","|P165_status|","P165" - ":ref:`P166_page`","|P166_status|","P166" - ":ref:`P167_page`","|P167_status|","P167" - ":ref:`P168_page`","|P168_status|","P168" - ":ref:`P169_page`","|P169_status|","P169" - ":ref:`P170_page`","|P170_status|","P170" - ":ref:`P172_page`","|P172_status|","P172" - ":ref:`P173_page`","|P173_status|","P173" - ":ref:`P175_page`","|P175_status|","P175" - ":ref:`P176_page`","|P176_status|","P176" - ":ref:`P177_page`","|P177_status|","P177" - ":ref:`P178_page`","|P178_status|","P178" - ":ref:`P180_page`","|P180_status|","P180" + ":ref:`P000_page`","|P000_status|","|P000_status_lb|","P000 (core)" + ":ref:`P001_page`","|P001_status|","|P001_status_lb|","P001" + ":ref:`P002_page`","|P002_status|","|P002_status_lb|","P002" + ":ref:`P003_page`","|P003_status|","|P003_status_lb|","P003" + ":ref:`P004_page`","|P004_status|","|P004_status_lb|","P004" + ":ref:`P005_page`","|P005_status|","|P005_status_lb|","P005" + ":ref:`P006_page`","|P006_status|","|P006_status_lb|","P006" + ":ref:`P007_page`","|P007_status|","|P007_status_lb|","P007" + ":ref:`P008_page`","|P008_status|","|P008_status_lb|","P008" + ":ref:`P009_page`","|P009_status|","|P009_status_lb|","P009" + ":ref:`P010_page`","|P010_status|","|P010_status_lb|","P010" + ":ref:`P011_page`","|P011_status|","|P011_status_lb|","P011" + ":ref:`P012_page`","|P012_status|","|P012_status_lb|","P012" + ":ref:`P013_page`","|P013_status|","|P013_status_lb|","P013" + ":ref:`P014_page`","|P014_status|","|P014_status_lb|","P014" + ":ref:`P015_page`","|P015_status|","|P015_status_lb|","P015" + ":ref:`P016_page`","|P016_status|","|P016_status_lb|","P016" + ":ref:`P017_page`","|P017_status|","|P017_status_lb|","P017" + ":ref:`P018_page`","|P018_status|","|P018_status_lb|","P018" + ":ref:`P019_page`","|P019_status|","|P019_status_lb|","P019" + ":ref:`P020_page`","|P020_status|","|P020_status_lb|","P020" + ":ref:`P021_page`","|P021_status|","|P021_status_lb|","P021" + ":ref:`P022_page`","|P022_status|","|P022_status_lb|","P022" + ":ref:`P023_page`","|P023_status|","|P023_status_lb|","P023" + ":ref:`P024_page`","|P024_status|","|P024_status_lb|","P024" + ":ref:`P025_page`","|P025_status|","|P025_status_lb|","P025" + ":ref:`P026_page`","|P026_status|","|P026_status_lb|","P026" + ":ref:`P027_page`","|P027_status|","|P027_status_lb|","P027" + ":ref:`P028_page`","|P028_status|","|P028_status_lb|","P028" + ":ref:`P029_page`","|P029_status|","|P029_status_lb|","P029" + ":ref:`P030_page`","|P030_status|","|P030_status_lb|","P030" + ":ref:`P031_page`","|P031_status|","|P031_status_lb|","P031" + ":ref:`P032_page`","|P032_status|","|P032_status_lb|","P032" + ":ref:`P033_page`","|P033_status|","|P033_status_lb|","P033" + ":ref:`P034_page`","|P034_status|","|P034_status_lb|","P034" + ":ref:`P035_page`","|P035_status|","|P035_status_lb|","P035" + ":ref:`P036_page`","|P036_status|","|P036_status_lb|","P036" + ":ref:`P037_page`","|P037_status|","|P037_status_lb|","P037" + ":ref:`P038_page`","|P038_status|","|P038_status_lb|","P038" + ":ref:`P039_page`","|P039_status|","|P039_status_lb|","P039" + ":ref:`P040_page`","|P040_status|","|P040_status_lb|","P040" + ":ref:`P041_page`","|P041_status|","|P041_status_lb|","P041" + ":ref:`P042_page`","|P042_status|","|P042_status_lb|","P042" + ":ref:`P043_page`","|P043_status|","|P043_status_lb|","P043" + ":ref:`P044_page`","|P044_status|","|P044_status_lb|","P044" + ":ref:`P045_page`","|P045_status|","|P045_status_lb|","P045" + ":ref:`P046_page`","|P046_status|","|P046_status_lb|","P046" + ":ref:`P047_page`","|P047_status|","|P047_status_lb|","P047" + ":ref:`P048_page`","|P048_status|","|P048_status_lb|","P048" + ":ref:`P049_page`","|P049_status|","|P049_status_lb|","P049" + ":ref:`P050_page`","|P050_status|","|P050_status_lb|","P050" + ":ref:`P051_page`","|P051_status|","|P051_status_lb|","P051" + ":ref:`P052_page`","|P052_status|","|P052_status_lb|","P052" + ":ref:`P053_page`","|P053_status|","|P053_status_lb|","P053" + ":ref:`P054_page`","|P054_status|","|P054_status_lb|","P054" + ":ref:`P055_page`","|P055_status|","|P055_status_lb|","P055" + ":ref:`P056_page`","|P056_status|","|P056_status_lb|","P056" + ":ref:`P057_page`","|P057_status|","|P057_status_lb|","P057" + ":ref:`P058_page`","|P058_status|","|P058_status_lb|","P058" + ":ref:`P059_page`","|P059_status|","|P059_status_lb|","P059" + ":ref:`P060_page`","|P060_status|","|P060_status_lb|","P060" + ":ref:`P061_page`","|P061_status|","|P061_status_lb|","P061" + ":ref:`P062_page`","|P062_status|","|P062_status_lb|","P062" + ":ref:`P063_page`","|P063_status|","|P063_status_lb|","P063" + ":ref:`P064_page`","|P064_status|","|P064_status_lb|","P064" + ":ref:`P065_page`","|P065_status|","|P065_status_lb|","P065" + ":ref:`P066_page`","|P066_status|","|P066_status_lb|","P066" + ":ref:`P067_page`","|P067_status|","|P067_status_lb|","P067" + ":ref:`P068_page`","|P068_status|","|P068_status_lb|","P068" + ":ref:`P069_page`","|P069_status|","|P069_status_lb|","P069" + ":ref:`P070_page`","|P070_status|","|P070_status_lb|","P070" + ":ref:`P071_page`","|P071_status|","|P071_status_lb|","P071" + ":ref:`P072_page`","|P072_status|","|P072_status_lb|","P072" + ":ref:`P073_page`","|P073_status|","|P073_status_lb|","P073" + ":ref:`P074_page`","|P074_status|","|P074_status_lb|","P074" + ":ref:`P075_page`","|P075_status|","|P075_status_lb|","P075" + ":ref:`P076_page`","|P076_status|","|P076_status_lb|","P076" + ":ref:`P077_page`","|P077_status|","|P077_status_lb|","P077" + ":ref:`P078_page`","|P078_status|","|P078_status_lb|","P078" + ":ref:`P079_page`","|P079_status|","|P079_status_lb|","P079" + ":ref:`P080_page`","|P080_status|","|P080_status_lb|","P080" + ":ref:`P081_page`","|P081_status|","|P081_status_lb|","P081" + ":ref:`P082_page`","|P082_status|","|P082_status_lb|","P082" + ":ref:`P083_page`","|P083_status|","|P083_status_lb|","P083" + ":ref:`P084_page`","|P084_status|","|P084_status_lb|","P084" + ":ref:`P085_page`","|P085_status|","|P085_status_lb|","P085" + ":ref:`P086_page`","|P086_status|","|P086_status_lb|","P086" + ":ref:`P087_page`","|P087_status|","|P087_status_lb|","P087" + ":ref:`P088_page`","|P088_status|","|P088_status_lb|","P088" + ":ref:`P089_page`","|P089_status|","|P089_status_lb|","P089" + ":ref:`P090_page`","|P090_status|","|P090_status_lb|","P090" + ":ref:`P091_page`","|P091_status|","|P091_status_lb|","P091" + ":ref:`P092_page`","|P092_status|","|P092_status_lb|","P092" + ":ref:`P093_page`","|P093_status|","|P093_status_lb|","P093" + ":ref:`P094_page`","|P094_status|","|P094_status_lb|","P094" + ":ref:`P095_page`","|P095_status|","|P095_status_lb|","P095" + ":ref:`P097_page`","|P097_status|","|P097_status_lb|","P097" + ":ref:`P098_page`","|P098_status|","|P098_status_lb|","P098" + ":ref:`P099_page`","|P099_status|","|P099_status_lb|","P099" + ":ref:`P100_page`","|P100_status|","|P100_status_lb|","P100" + ":ref:`P101_page`","|P101_status|","|P101_status_lb|","P101" + ":ref:`P102_page`","|P102_status|","|P102_status_lb|","P102" + ":ref:`P103_page`","|P103_status|","|P103_status_lb|","P103" + ":ref:`P104_page`","|P104_status|","|P104_status_lb|","P104" + ":ref:`P105_page`","|P105_status|","|P105_status_lb|","P105" + ":ref:`P106_page`","|P106_status|","|P106_status_lb|","P106" + ":ref:`P107_page`","|P107_status|","|P107_status_lb|","P107" + ":ref:`P108_page`","|P108_status|","|P108_status_lb|","P108" + ":ref:`P109_page`","|P109_status|","|P109_status_lb|","P109" + ":ref:`P110_page`","|P110_status|","|P110_status_lb|","P110" + ":ref:`P111_page`","|P111_status|","|P111_status_lb|","P111" + ":ref:`P112_page`","|P112_status|","|P112_status_lb|","P112" + ":ref:`P113_page`","|P113_status|","|P113_status_lb|","P113" + ":ref:`P114_page`","|P114_status|","|P114_status_lb|","P114" + ":ref:`P115_page`","|P115_status|","|P115_status_lb|","P115" + ":ref:`P116_page`","|P116_status|","|P116_status_lb|","P116" + ":ref:`P117_page`","|P117_status|","|P117_status_lb|","P117" + ":ref:`P118_page`","|P118_status|","|P118_status_lb|","P118" + ":ref:`P119_page`","|P119_status|","|P119_status_lb|","P119" + ":ref:`P120_page`","|P120_status|","|P120_status_lb|","P120" + ":ref:`P121_page`","|P121_status|","|P121_status_lb|","P121" + ":ref:`P122_page`","|P122_status|","|P122_status_lb|","P122" + ":ref:`P123_page`","|P123_status|","|P123_status_lb|","P123" + ":ref:`P124_page`","|P124_status|","|P124_status_lb|","P124" + ":ref:`P125_page`","|P125_status|","|P125_status_lb|","P125" + ":ref:`P126_page`","|P126_status|","|P126_status_lb|","P126" + ":ref:`P127_page`","|P127_status|","|P127_status_lb|","P127" + ":ref:`P128_page`","|P128_status|","|P128_status_lb|","P128" + ":ref:`P129_page`","|P129_status|","|P129_status_lb|","P129" + ":ref:`P131_page`","|P131_status|","|P131_status_lb|","P131" + ":ref:`P132_page`","|P132_status|","|P132_status_lb|","P132" + ":ref:`P133_page`","|P133_status|","|P133_status_lb|","P133" + ":ref:`P134_page`","|P134_status|","|P134_status_lb|","P134" + ":ref:`P135_page`","|P135_status|","|P135_status_lb|","P135" + ":ref:`P137_page`","|P137_status|","|P137_status_lb|","P137" + ":ref:`P138_page`","|P138_status|","|P138_status_lb|","P138" + ":ref:`P139_page`","|P139_status|","|P139_status_lb|","P139" + ":ref:`P140_page`","|P140_status|","|P140_status_lb|","P140" + ":ref:`P141_page`","|P141_status|","|P141_status_lb|","P141" + ":ref:`P142_page`","|P142_status|","|P142_status_lb|","P142" + ":ref:`P143_page`","|P143_status|","|P143_status_lb|","P143" + ":ref:`P144_page`","|P144_status|","|P144_status_lb|","P144" + ":ref:`P145_page`","|P145_status|","|P145_status_lb|","P145" + ":ref:`P146_page`","|P146_status|","|P146_status_lb|","P146" + ":ref:`P147_page`","|P147_status|","|P147_status_lb|","P147" + ":ref:`P148_page`","|P148_status|","|P148_status_lb|","P148" + ":ref:`P150_page`","|P150_status|","|P150_status_lb|","P150" + ":ref:`P151_page`","|P151_status|","|P151_status_lb|","P151" + ":ref:`P152_page`","|P152_status|","|P152_status_lb|","P152" + ":ref:`P153_page`","|P153_status|","|P153_status_lb|","P153" + ":ref:`P154_page`","|P154_status|","|P154_status_lb|","P154" + ":ref:`P159_page`","|P159_status|","|P159_status_lb|","P159" + ":ref:`P162_page`","|P162_status|","|P162_status_lb|","P162" + ":ref:`P163_page`","|P163_status|","|P163_status_lb|","P163" + ":ref:`P164_page`","|P164_status|","|P164_status_lb|","P164" + ":ref:`P165_page`","|P165_status|","|P165_status_lb|","P165" + ":ref:`P166_page`","|P166_status|","|P166_status_lb|","P166" + ":ref:`P167_page`","|P167_status|","|P167_status_lb|","P167" + ":ref:`P168_page`","|P168_status|","|P168_status_lb|","P168" + ":ref:`P169_page`","|P169_status|","|P169_status_lb|","P169" + ":ref:`P170_page`","|P170_status|","|P170_status_lb|","P170" + ":ref:`P172_page`","|P172_status|","|P172_status_lb|","P172" + ":ref:`P173_page`","|P173_status|","|P173_status_lb|","P173" + ":ref:`P175_page`","|P175_status|","|P175_status_lb|","P175" + ":ref:`P176_page`","|P176_status|","|P176_status_lb|","P176" + ":ref:`P177_page`","|P177_status|","|P177_status_lb|","P177" + ":ref:`P178_page`","|P178_status|","|P178_status_lb|","P178" + ":ref:`P180_page`","|P180_status|","|P180_status_lb|","P180" +.. include:: .. include:: _plugin_sets_overview.repl Plugins per Category diff --git a/docs/source/Plugin/_plugin_sets_overview.repl b/docs/source/Plugin/_plugin_sets_overview.repl index 6ba38b911..7294330f0 100644 --- a/docs/source/Plugin/_plugin_sets_overview.repl +++ b/docs/source/Plugin/_plugin_sets_overview.repl @@ -7,72 +7,77 @@ Build set: :green:`NORMAL` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P079_page`","P079" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION A` --------------------------------------------- @@ -80,116 +85,122 @@ Build set: :yellow:`COLLECTION A` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P067_page`","P067" - ":ref:`P068_page`","P068" - ":ref:`P070_page`","P070" - ":ref:`P071_page`","P071" - ":ref:`P072_page`","P072" - ":ref:`P073_page`","P073" - ":ref:`P074_page`","P074" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P080_page`","P080" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P083_page`","P083" - ":ref:`P084_page`","P084" - ":ref:`P086_page`","P086" - ":ref:`P089_page`","P089" - ":ref:`P090_page`","P090" - ":ref:`P095_page`","P095" - ":ref:`P097_page`","P097" - ":ref:`P098_page`","P098" - ":ref:`P105_page`","P105" - ":ref:`P134_page`","P134" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P067_page`", "✓", "✓", "P067" + ":ref:`P068_page`", "✓", "✓", "P068" + ":ref:`P070_page`", "✓", "✓", "P070" + ":ref:`P071_page`", "✓", "✓", "P071" + ":ref:`P072_page`", "✓", "✓", "P072" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P074_page`", "✓", "✓", "P074" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P080_page`", "✓", "✓", "P080" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P083_page`", "✓", "✓", "P083" + ":ref:`P084_page`", "✓", "✓", "P084" + ":ref:`P086_page`", "✓", "✓", "P086" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P090_page`", "✓", "✓", "P090" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P097_page`", "✓", "✓", "P097" + ":ref:`P098_page`", "✓", "✓", "P098" + ":ref:`P105_page`", "✓", "✓", "P105" + ":ref:`P134_page`", "✓", "✓", "P134" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION B` --------------------------------------------- @@ -197,111 +208,113 @@ Build set: :yellow:`COLLECTION B` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P069_page`","P069" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P095_page`","P095" - ":ref:`P100_page`","P100" - ":ref:`P101_page`","P101" - ":ref:`P106_page`","P106" - ":ref:`P107_page`","P107" - ":ref:`P108_page`","P108" - ":ref:`P110_page`","P110" - ":ref:`P113_page`","P113" - ":ref:`P115_page`","P115" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P069_page`", "✓", "✓", "P069" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P100_page`", "✓", "✓", "P100" + ":ref:`P101_page`", "✓", "✓", "P101" + ":ref:`P106_page`", "✓", "✓", "P106" + ":ref:`P107_page`", "✓", "✓", "P107" + ":ref:`P108_page`", "✓", "✓", "P108" + ":ref:`P110_page`", "✓", "✓", "P110" + ":ref:`P113_page`", "✓", "✓", "P113" + ":ref:`P115_page`", "✓", "✓", "P115" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION C` --------------------------------------------- @@ -309,107 +322,113 @@ Build set: :yellow:`COLLECTION C` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P085_page`","P085" - ":ref:`P087_page`","P087" - ":ref:`P089_page`","P089" - ":ref:`P091_page`","P091" - ":ref:`P092_page`","P092" - ":ref:`P095_page`","P095" - ":ref:`P111_page`","P111" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P143_page`","P143" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P085_page`", "✓", "✓", "P085" + ":ref:`P087_page`", "✓", "✓", "P087" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P091_page`", "✓", "✓", "P091" + ":ref:`P092_page`", "✓", "✓", "P092" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P111_page`", "✓", "✓", "P111" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P143_page`", "✓", "✓", "P143" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION D` --------------------------------------------- @@ -417,107 +436,110 @@ Build set: :yellow:`COLLECTION D` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P093_page`","P093" - ":ref:`P095_page`","P095" - ":ref:`P098_page`","P098" - ":ref:`P114_page`","P114" - ":ref:`P117_page`","P117" - ":ref:`P124_page`","P124" - ":ref:`P127_page`","P127" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P093_page`", "✓", "✓", "P093" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P098_page`", "✓", "✓", "P098" + ":ref:`P114_page`", "✓", "✓", "P114" + ":ref:`P117_page`", "✓", "✓", "P117" + ":ref:`P124_page`", "✓", "✓", "P124" + ":ref:`P127_page`", "✓", "✓", "P127" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION E` --------------------------------------------- @@ -525,111 +547,113 @@ Build set: :yellow:`COLLECTION E` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P095_page`","P095" - ":ref:`P119_page`","P119" - ":ref:`P120_page`","P120" - ":ref:`P121_page`","P121" - ":ref:`P125_page`","P125" - ":ref:`P126_page`","P126" - ":ref:`P129_page`","P129" - ":ref:`P133_page`","P133" - ":ref:`P135_page`","P135" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P144_page`","P144" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P119_page`", "✓", "✓", "P119" + ":ref:`P120_page`", "✓", "✓", "P120" + ":ref:`P121_page`", "✓", "✓", "P121" + ":ref:`P125_page`", "✓", "✓", "P125" + ":ref:`P126_page`", "✓", "✓", "P126" + ":ref:`P129_page`", "✓", "✓", "P129" + ":ref:`P133_page`", "✓", "✓", "P133" + ":ref:`P135_page`", "✓", "✓", "P135" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P144_page`", "✓", "✓", "P144" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION F` --------------------------------------------- @@ -637,109 +661,112 @@ Build set: :yellow:`COLLECTION F` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P095_page`","P095" - ":ref:`P112_page`","P112" - ":ref:`P118_page`","P118" - ":ref:`P122_page`","P122" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P145_page`","P145" - ":ref:`P146_page`","P146" - ":ref:`P147_page`","P147" - ":ref:`P150_page`","P150" - ":ref:`P151_page`","P151" - ":ref:`P152_page`","P152" - ":ref:`P153_page`","P153" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P112_page`", "✓", "✓", "P112" + ":ref:`P118_page`", "✓", "✓", "P118" + ":ref:`P122_page`", "✓", "✓", "P122" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P145_page`", "✓", "✓", "P145" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P147_page`", "✓", "✓", "P147" + ":ref:`P150_page`", "✓", "✓", "P150" + ":ref:`P151_page`", "✓", "✓", "P151" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P153_page`", "✓", "✓", "P153" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION G` --------------------------------------------- @@ -747,115 +774,118 @@ Build set: :yellow:`COLLECTION G` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P095_page`","P095" - ":ref:`P105_page`","P105" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P142_page`","P142" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P154_page`","P154" - ":ref:`P159_page`","P159" - ":ref:`P162_page`","P162" - ":ref:`P163_page`","P163" - ":ref:`P164_page`","P164" - ":ref:`P166_page`","P166" - ":ref:`P168_page`","P168" - ":ref:`P170_page`","P170" - ":ref:`P172_page`","P172" - ":ref:`P173_page`","P173" - ":ref:`P177_page`","P177" - ":ref:`P178_page`","P178" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P105_page`", "✓", "", "P105" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P142_page`", "✓", "✓", "P142" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P154_page`", "✓", "✓", "P154" + ":ref:`P159_page`", "✓", "✓", "P159" + ":ref:`P162_page`", "✓", "✓", "P162" + ":ref:`P163_page`", "✓", "", "P163" + ":ref:`P164_page`", "✓", "✓", "P164" + ":ref:`P166_page`", "✓", "✓", "P166" + ":ref:`P168_page`", "✓", "✓", "P168" + ":ref:`P170_page`", "✓", "✓", "P170" + ":ref:`P172_page`", "✓", "✓", "P172" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`COLLECTION H` --------------------------------------------- @@ -863,101 +893,110 @@ Build set: :yellow:`COLLECTION H` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P089_page`","P089" - ":ref:`P095_page`","P095" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P045_page`", "✓", "✓", "P045" + ":ref:`P046_page`", "✓", "✓", "P046" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P048_page`", "✓", "✓", "P048" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P050_page`", "✓", "✓", "P050" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P054_page`", "✓", "✓", "P054" + ":ref:`P055_page`", "✓", "✓", "P055" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P057_page`", "✓", "✓", "P057" + ":ref:`P058_page`", "✓", "✓", "P058" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P060_page`", "✓", "✓", "P060" + ":ref:`P061_page`", "✓", "✓", "P061" + ":ref:`P062_page`", "✓", "✓", "P062" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P064_page`", "✓", "✓", "P064" + ":ref:`P065_page`", "✓", "✓", "P065" + ":ref:`P066_page`", "✓", "✓", "P066" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P082_page`", "✓", "✓", "P082" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P173_page`", "✓", "", "P173" + ":ref:`P177_page`", "✓", "", "P177" + ":ref:`P178_page`", "✓", "", "P178" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`C012_page`", "✓", "✓", "C012" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`C014_page`", "✓", "✓", "C014" + ":ref:`C017_page`", "✓", "✓", "C017" + ":ref:`C018_page`", "✓", "✓", "C018" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`CLIMATE A` --------------------------------------------- @@ -965,75 +1004,76 @@ Build set: :yellow:`CLIMATE A` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P010_page`","P010" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P021_page`","P021" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P047_page`","P047" - ":ref:`P049_page`","P049" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P068_page`","P068" - ":ref:`P069_page`","P069" - ":ref:`P072_page`","P072" - ":ref:`P073_page`","P073" - ":ref:`P081_page`","P081" - ":ref:`P083_page`","P083" - ":ref:`P089_page`","P089" - ":ref:`P090_page`","P090" - ":ref:`P103_page`","P103" - ":ref:`P105_page`","P105" - ":ref:`P106_page`","P106" - ":ref:`P117_page`","P117" - ":ref:`P118_page`","P118" - ":ref:`P127_page`","P127" - ":ref:`P133_page`","P133" - ":ref:`P135_page`","P135" - ":ref:`P142_page`","P142" - ":ref:`P144_page`","P144" - ":ref:`P147_page`","P147" - ":ref:`P150_page`","P150" - ":ref:`P151_page`","P151" - ":ref:`P153_page`","P153" - ":ref:`P154_page`","P154" - ":ref:`P164_page`","P164" - ":ref:`P180_page`","P180" - ":ref:`C011_page`","C011" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P068_page`", "✓", "✓", "P068" + ":ref:`P069_page`", "✓", "✓", "P069" + ":ref:`P072_page`", "✓", "✓", "P072" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P083_page`", "✓", "✓", "P083" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P090_page`", "✓", "✓", "P090" + ":ref:`P103_page`", "✓", "✓", "P103" + ":ref:`P105_page`", "✓", "✓", "P105" + ":ref:`P106_page`", "✓", "✓", "P106" + ":ref:`P117_page`", "✓", "✓", "P117" + ":ref:`P118_page`", "✓", "✓", "P118" + ":ref:`P127_page`", "✓", "✓", "P127" + ":ref:`P133_page`", "✓", "✓", "P133" + ":ref:`P135_page`", "✓", "✓", "P135" + ":ref:`P142_page`", "✓", "✓", "P142" + ":ref:`P144_page`", "✓", "✓", "P144" + ":ref:`P147_page`", "✓", "✓", "P147" + ":ref:`P150_page`", "✓", "✓", "P150" + ":ref:`P151_page`", "✓", "✓", "P151" + ":ref:`P153_page`", "✓", "✓", "P153" + ":ref:`P154_page`", "✓", "✓", "P154" + ":ref:`P164_page`", "✓", "✓", "P164" + ":ref:`P167_page`", "✓", "✓", "P167" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" Build set: :yellow:`CLIMATE B` --------------------------------------------- @@ -1041,83 +1081,82 @@ Build set: :yellow:`CLIMATE B` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P010_page`","P010" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P021_page`","P021" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P047_page`","P047" - ":ref:`P049_page`","P049" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P068_page`","P068" - ":ref:`P069_page`","P069" - ":ref:`P072_page`","P072" - ":ref:`P073_page`","P073" - ":ref:`P081_page`","P081" - ":ref:`P083_page`","P083" - ":ref:`P089_page`","P089" - ":ref:`P090_page`","P090" - ":ref:`P103_page`","P103" - ":ref:`P105_page`","P105" - ":ref:`P106_page`","P106" - ":ref:`P117_page`","P117" - ":ref:`P118_page`","P118" - ":ref:`P127_page`","P127" - ":ref:`P133_page`","P133" - ":ref:`P135_page`","P135" - ":ref:`P142_page`","P142" - ":ref:`P144_page`","P144" - ":ref:`P147_page`","P147" - ":ref:`P150_page`","P150" - ":ref:`P151_page`","P151" - ":ref:`P153_page`","P153" - ":ref:`P154_page`","P154" - ":ref:`P164_page`","P164" - ":ref:`P167_page`","P167" - ":ref:`P168_page`","P168" - ":ref:`P169_page`","P169" - ":ref:`P172_page`","P172" - ":ref:`P173_page`","P173" - ":ref:`P175_page`","P175" - ":ref:`P177_page`","P177" - ":ref:`P178_page`","P178" - ":ref:`P180_page`","P180" - ":ref:`C011_page`","C011" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P047_page`", "✓", "✓", "P047" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P051_page`", "✓", "✓", "P051" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P068_page`", "✓", "✓", "P068" + ":ref:`P069_page`", "✓", "✓", "P069" + ":ref:`P072_page`", "✓", "✓", "P072" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P081_page`", "✓", "✓", "P081" + ":ref:`P083_page`", "✓", "✓", "P083" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P090_page`", "✓", "✓", "P090" + ":ref:`P103_page`", "✓", "✓", "P103" + ":ref:`P105_page`", "✓", "✓", "P105" + ":ref:`P106_page`", "✓", "✓", "P106" + ":ref:`P117_page`", "✓", "✓", "P117" + ":ref:`P118_page`", "✓", "✓", "P118" + ":ref:`P127_page`", "✓", "✓", "P127" + ":ref:`P133_page`", "✓", "✓", "P133" + ":ref:`P135_page`", "✓", "✓", "P135" + ":ref:`P142_page`", "✓", "✓", "P142" + ":ref:`P144_page`", "✓", "✓", "P144" + ":ref:`P147_page`", "✓", "✓", "P147" + ":ref:`P150_page`", "✓", "✓", "P150" + ":ref:`P151_page`", "✓", "✓", "P151" + ":ref:`P153_page`", "✓", "✓", "P153" + ":ref:`P154_page`", "✓", "✓", "P154" + ":ref:`P164_page`", "✓", "✓", "P164" + ":ref:`P168_page`", "✓", "✓", "P168" + ":ref:`P169_page`", "✓", "", "P169" + ":ref:`P172_page`", "✓", "✓", "P172" + ":ref:`P173_page`", "✓", "✓", "P173" + ":ref:`P175_page`", "✓", "", "P175" + ":ref:`P177_page`", "✓", "", "P177" + ":ref:`P178_page`", "✓", "", "P178" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C011_page`", "✓", "✓", "C011" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" Build set: :yellow:`DISPLAY A` --------------------------------------------- @@ -1125,80 +1164,84 @@ Build set: :yellow:`DISPLAY A` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P075_page`","P075" - ":ref:`P079_page`","P079" - ":ref:`P104_page`","P104" - ":ref:`P109_page`","P109" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P143_page`","P143" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P075_page`", "✓", "✓", "P075" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P104_page`", "✓", "✓", "P104" + ":ref:`P109_page`", "✓", "", "P109" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P143_page`", "✓", "✓", "P143" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`DISPLAY B` --------------------------------------------- @@ -1206,83 +1249,86 @@ Build set: :yellow:`DISPLAY B` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P079_page`","P079" - ":ref:`P095_page`","P095" - ":ref:`P099_page`","P099" - ":ref:`P109_page`","P109" - ":ref:`P116_page`","P116" - ":ref:`P123_page`","P123" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P141_page`","P141" - ":ref:`P143_page`","P143" - ":ref:`P146_page`","P146" - ":ref:`P148_page`","P148" - ":ref:`P152_page`","P152" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P095_page`", "✓", "✓", "P095" + ":ref:`P099_page`", "✓", "✓", "P099" + ":ref:`P109_page`", "✓", "", "P109" + ":ref:`P116_page`", "✓", "✓", "P116" + ":ref:`P123_page`", "✓", "", "P123" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P141_page`", "✓", "✓", "P141" + ":ref:`P143_page`", "✓", "✓", "P143" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P148_page`", "✓", "", "P148" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`ENERGY` --------------------------------------------- @@ -1290,87 +1336,93 @@ Build set: :yellow:`ENERGY` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P076_page`","P076" - ":ref:`P077_page`","P077" - ":ref:`P078_page`","P078" - ":ref:`P079_page`","P079" - ":ref:`P085_page`","P085" - ":ref:`P089_page`","P089" - ":ref:`P093_page`","P093" - ":ref:`P102_page`","P102" - ":ref:`P108_page`","P108" - ":ref:`P115_page`","P115" - ":ref:`P132_page`","P132" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P148_page`","P148" - ":ref:`P152_page`","P152" - ":ref:`P176_page`","P176" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P076_page`", "✓", "✓", "P076" + ":ref:`P077_page`", "✓", "✓", "P077" + ":ref:`P078_page`", "✓", "✓", "P078" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P085_page`", "✓", "✓", "P085" + ":ref:`P087_page`", "✓", "", "P087" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P093_page`", "✓", "✓", "P093" + ":ref:`P102_page`", "✓", "✓", "P102" + ":ref:`P108_page`", "✓", "✓", "P108" + ":ref:`P115_page`", "✓", "✓", "P115" + ":ref:`P132_page`", "✓", "✓", "P132" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P148_page`", "✓", "", "P148" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P176_page`", "✓", "", "P176" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`IR` --------------------------------------------- @@ -1378,73 +1430,76 @@ Build set: :yellow:`IR` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P016_page`","P016" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P035_page`","P035" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P079_page`","P079" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P016_page`", "✓", "✓", "P016" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P035_page`", "✓", "✓", "P035" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`IRext` --------------------------------------------- @@ -1452,73 +1507,75 @@ Build set: :yellow:`IRext` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P073_page`","P073" - ":ref:`P079_page`","P079" - ":ref:`P088_page`","P088" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P088_page`", "✓", "✓", "P088" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`NEOPIXEL` --------------------------------------------- @@ -1526,82 +1583,89 @@ Build set: :yellow:`NEOPIXEL` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P041_page`","P041" - ":ref:`P042_page`","P042" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P049_page`","P049" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P056_page`","P056" - ":ref:`P059_page`","P059" - ":ref:`P063_page`","P063" - ":ref:`P070_page`","P070" - ":ref:`P073_page`","P073" - ":ref:`P079_page`","P079" - ":ref:`P089_page`","P089" - ":ref:`P128_page`","P128" - ":ref:`P131_page`","P131" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P146_page`","P146" - ":ref:`P152_page`","P152" - ":ref:`P165_page`","P165" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C013_page`","C013" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "✓", "P000" + ":ref:`P001_page`", "✓", "✓", "P001" + ":ref:`P002_page`", "✓", "✓", "P002" + ":ref:`P003_page`", "✓", "✓", "P003" + ":ref:`P004_page`", "✓", "✓", "P004" + ":ref:`P005_page`", "✓", "✓", "P005" + ":ref:`P006_page`", "✓", "✓", "P006" + ":ref:`P007_page`", "✓", "✓", "P007" + ":ref:`P008_page`", "✓", "✓", "P008" + ":ref:`P009_page`", "✓", "✓", "P009" + ":ref:`P010_page`", "✓", "✓", "P010" + ":ref:`P011_page`", "✓", "✓", "P011" + ":ref:`P012_page`", "✓", "✓", "P012" + ":ref:`P013_page`", "✓", "✓", "P013" + ":ref:`P014_page`", "✓", "✓", "P014" + ":ref:`P015_page`", "✓", "✓", "P015" + ":ref:`P017_page`", "✓", "✓", "P017" + ":ref:`P018_page`", "✓", "✓", "P018" + ":ref:`P019_page`", "✓", "✓", "P019" + ":ref:`P020_page`", "✓", "✓", "P020" + ":ref:`P021_page`", "✓", "✓", "P021" + ":ref:`P022_page`", "✓", "✓", "P022" + ":ref:`P023_page`", "✓", "✓", "P023" + ":ref:`P024_page`", "✓", "✓", "P024" + ":ref:`P025_page`", "✓", "✓", "P025" + ":ref:`P026_page`", "✓", "✓", "P026" + ":ref:`P027_page`", "✓", "✓", "P027" + ":ref:`P028_page`", "✓", "✓", "P028" + ":ref:`P029_page`", "✓", "✓", "P029" + ":ref:`P031_page`", "✓", "✓", "P031" + ":ref:`P032_page`", "✓", "✓", "P032" + ":ref:`P033_page`", "✓", "✓", "P033" + ":ref:`P034_page`", "✓", "✓", "P034" + ":ref:`P036_page`", "✓", "✓", "P036" + ":ref:`P037_page`", "✓", "✓", "P037" + ":ref:`P038_page`", "✓", "✓", "P038" + ":ref:`P039_page`", "✓", "✓", "P039" + ":ref:`P040_page`", "✓", "✓", "P040" + ":ref:`P041_page`", "✓", "✓", "P041" + ":ref:`P042_page`", "✓", "✓", "P042" + ":ref:`P043_page`", "✓", "✓", "P043" + ":ref:`P044_page`", "✓", "✓", "P044" + ":ref:`P049_page`", "✓", "✓", "P049" + ":ref:`P052_page`", "✓", "✓", "P052" + ":ref:`P053_page`", "✓", "✓", "P053" + ":ref:`P056_page`", "✓", "✓", "P056" + ":ref:`P059_page`", "✓", "✓", "P059" + ":ref:`P063_page`", "✓", "✓", "P063" + ":ref:`P070_page`", "✓", "✓", "P070" + ":ref:`P073_page`", "✓", "✓", "P073" + ":ref:`P079_page`", "✓", "✓", "P079" + ":ref:`P089_page`", "✓", "✓", "P089" + ":ref:`P105_page`", "✓", "", "P105" + ":ref:`P128_page`", "✓", "✓", "P128" + ":ref:`P131_page`", "✓", "✓", "P131" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P146_page`", "✓", "✓", "P146" + ":ref:`P152_page`", "✓", "✓", "P152" + ":ref:`P165_page`", "✓", "✓", "P165" + ":ref:`P180_page`", "✓", "✓", "P180" + ":ref:`C001_page`", "✓", "✓", "C001" + ":ref:`C002_page`", "✓", "✓", "C002" + ":ref:`C003_page`", "✓", "✓", "C003" + ":ref:`C004_page`", "✓", "✓", "C004" + ":ref:`C005_page`", "✓", "✓", "C005" + ":ref:`C006_page`", "✓", "✓", "C006" + ":ref:`C007_page`", "✓", "✓", "C007" + ":ref:`C008_page`", "✓", "✓", "C008" + ":ref:`C009_page`", "✓", "✓", "C009" + ":ref:`C010_page`", "✓", "✓", "C010" + ":ref:`C013_page`", "✓", "✓", "C013" + ":ref:`N001_page`", "✓", "✓", "N001" + ":ref:`N002_page`", "✓", "✓", "N002" + ":ref:`NW001_page`", "✓", "✓", "NW001" + ":ref:`NW002_page`", "✓", "✓", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" Build set: :yellow:`MAX` --------------------------------------------- @@ -1609,194 +1673,199 @@ Build set: :yellow:`MAX` .. collapse:: Details... .. csv-table:: - :header: "Plugin name", "Plugin number" - :widths: 10, 5 + :header: "Plugin name", "ESP32", "ESP8266", "Plugin number" + :widths: 10, 3, 3, 3 - ":ref:`P000_page`","P000" - ":ref:`P001_page`","P001" - ":ref:`P002_page`","P002" - ":ref:`P003_page`","P003" - ":ref:`P004_page`","P004" - ":ref:`P005_page`","P005" - ":ref:`P006_page`","P006" - ":ref:`P007_page`","P007" - ":ref:`P008_page`","P008" - ":ref:`P009_page`","P009" - ":ref:`P010_page`","P010" - ":ref:`P011_page`","P011" - ":ref:`P012_page`","P012" - ":ref:`P013_page`","P013" - ":ref:`P014_page`","P014" - ":ref:`P015_page`","P015" - ":ref:`P016_page`","P016" - ":ref:`P017_page`","P017" - ":ref:`P018_page`","P018" - ":ref:`P019_page`","P019" - ":ref:`P020_page`","P020" - ":ref:`P021_page`","P021" - ":ref:`P022_page`","P022" - ":ref:`P023_page`","P023" - ":ref:`P024_page`","P024" - ":ref:`P025_page`","P025" - ":ref:`P026_page`","P026" - ":ref:`P027_page`","P027" - ":ref:`P028_page`","P028" - ":ref:`P029_page`","P029" - ":ref:`P030_page`","P030" - ":ref:`P031_page`","P031" - ":ref:`P032_page`","P032" - ":ref:`P033_page`","P033" - ":ref:`P034_page`","P034" - ":ref:`P035_page`","P035" - ":ref:`P036_page`","P036" - ":ref:`P037_page`","P037" - ":ref:`P038_page`","P038" - ":ref:`P039_page`","P039" - ":ref:`P040_page`","P040" - ":ref:`P041_page`","P041" - ":ref:`P042_page`","P042" - ":ref:`P043_page`","P043" - ":ref:`P044_page`","P044" - ":ref:`P045_page`","P045" - ":ref:`P046_page`","P046" - ":ref:`P047_page`","P047" - ":ref:`P048_page`","P048" - ":ref:`P049_page`","P049" - ":ref:`P050_page`","P050" - ":ref:`P051_page`","P051" - ":ref:`P052_page`","P052" - ":ref:`P053_page`","P053" - ":ref:`P054_page`","P054" - ":ref:`P055_page`","P055" - ":ref:`P056_page`","P056" - ":ref:`P057_page`","P057" - ":ref:`P058_page`","P058" - ":ref:`P059_page`","P059" - ":ref:`P060_page`","P060" - ":ref:`P061_page`","P061" - ":ref:`P062_page`","P062" - ":ref:`P063_page`","P063" - ":ref:`P064_page`","P064" - ":ref:`P065_page`","P065" - ":ref:`P066_page`","P066" - ":ref:`P067_page`","P067" - ":ref:`P068_page`","P068" - ":ref:`P069_page`","P069" - ":ref:`P070_page`","P070" - ":ref:`P071_page`","P071" - ":ref:`P072_page`","P072" - ":ref:`P073_page`","P073" - ":ref:`P074_page`","P074" - ":ref:`P075_page`","P075" - ":ref:`P076_page`","P076" - ":ref:`P077_page`","P077" - ":ref:`P078_page`","P078" - ":ref:`P079_page`","P079" - ":ref:`P080_page`","P080" - ":ref:`P081_page`","P081" - ":ref:`P082_page`","P082" - ":ref:`P083_page`","P083" - ":ref:`P084_page`","P084" - ":ref:`P085_page`","P085" - ":ref:`P086_page`","P086" - ":ref:`P087_page`","P087" - ":ref:`P088_page`","P088" - ":ref:`P089_page`","P089" - ":ref:`P090_page`","P090" - ":ref:`P091_page`","P091" - ":ref:`P092_page`","P092" - ":ref:`P093_page`","P093" - ":ref:`P094_page`","P094" - ":ref:`P095_page`","P095" - ":ref:`P097_page`","P097" - ":ref:`P098_page`","P098" - ":ref:`P099_page`","P099" - ":ref:`P100_page`","P100" - ":ref:`P101_page`","P101" - ":ref:`P102_page`","P102" - ":ref:`P103_page`","P103" - ":ref:`P104_page`","P104" - ":ref:`P105_page`","P105" - ":ref:`P106_page`","P106" - ":ref:`P107_page`","P107" - ":ref:`P108_page`","P108" - ":ref:`P109_page`","P109" - ":ref:`P110_page`","P110" - ":ref:`P111_page`","P111" - ":ref:`P112_page`","P112" - ":ref:`P113_page`","P113" - ":ref:`P114_page`","P114" - ":ref:`P115_page`","P115" - ":ref:`P116_page`","P116" - ":ref:`P117_page`","P117" - ":ref:`P118_page`","P118" - ":ref:`P119_page`","P119" - ":ref:`P120_page`","P120" - ":ref:`P121_page`","P121" - ":ref:`P122_page`","P122" - ":ref:`P123_page`","P123" - ":ref:`P124_page`","P124" - ":ref:`P125_page`","P125" - ":ref:`P126_page`","P126" - ":ref:`P127_page`","P127" - ":ref:`P128_page`","P128" - ":ref:`P129_page`","P129" - ":ref:`P131_page`","P131" - ":ref:`P132_page`","P132" - ":ref:`P133_page`","P133" - ":ref:`P134_page`","P134" - ":ref:`P135_page`","P135" - ":ref:`P137_page`","P137" - ":ref:`P138_page`","P138" - ":ref:`P139_page`","P139" - ":ref:`P140_page`","P140" - ":ref:`P141_page`","P141" - ":ref:`P142_page`","P142" - ":ref:`P143_page`","P143" - ":ref:`P144_page`","P144" - ":ref:`P145_page`","P145" - ":ref:`P146_page`","P146" - ":ref:`P147_page`","P147" - ":ref:`P148_page`","P148" - ":ref:`P150_page`","P150" - ":ref:`P151_page`","P151" - ":ref:`P152_page`","P152" - ":ref:`P153_page`","P153" - ":ref:`P154_page`","P154" - ":ref:`P159_page`","P159" - ":ref:`P162_page`","P162" - ":ref:`P163_page`","P163" - ":ref:`P164_page`","P164" - ":ref:`P165_page`","P165" - ":ref:`P166_page`","P166" - ":ref:`P167_page`","P167" - ":ref:`P168_page`","P168" - ":ref:`P169_page`","P169" - ":ref:`P170_page`","P170" - ":ref:`P172_page`","P172" - ":ref:`P173_page`","P173" - ":ref:`P175_page`","P175" - ":ref:`P176_page`","P176" - ":ref:`P177_page`","P177" - ":ref:`P178_page`","P178" - ":ref:`P180_page`","P180" - ":ref:`C001_page`","C001" - ":ref:`C002_page`","C002" - ":ref:`C003_page`","C003" - ":ref:`C004_page`","C004" - ":ref:`C005_page`","C005" - ":ref:`C006_page`","C006" - ":ref:`C007_page`","C007" - ":ref:`C008_page`","C008" - ":ref:`C009_page`","C009" - ":ref:`C010_page`","C010" - ":ref:`C011_page`","C011" - ":ref:`C012_page`","C012" - ":ref:`C013_page`","C013" - ":ref:`C014_page`","C014" - ":ref:`C016_page`","C016" - ":ref:`C017_page`","C017" - ":ref:`C018_page`","C018" - ":ref:`N001_page`","N001" - ":ref:`N002_page`","N002" + ":ref:`P000_page`", "✓", "", "P000" + ":ref:`P001_page`", "✓", "", "P001" + ":ref:`P002_page`", "✓", "", "P002" + ":ref:`P003_page`", "✓", "", "P003" + ":ref:`P004_page`", "✓", "", "P004" + ":ref:`P005_page`", "✓", "", "P005" + ":ref:`P006_page`", "✓", "", "P006" + ":ref:`P007_page`", "✓", "", "P007" + ":ref:`P008_page`", "✓", "", "P008" + ":ref:`P009_page`", "✓", "", "P009" + ":ref:`P010_page`", "✓", "", "P010" + ":ref:`P011_page`", "✓", "", "P011" + ":ref:`P012_page`", "✓", "", "P012" + ":ref:`P013_page`", "✓", "", "P013" + ":ref:`P014_page`", "✓", "", "P014" + ":ref:`P015_page`", "✓", "", "P015" + ":ref:`P016_page`", "✓", "", "P016" + ":ref:`P017_page`", "✓", "", "P017" + ":ref:`P018_page`", "✓", "", "P018" + ":ref:`P019_page`", "✓", "", "P019" + ":ref:`P020_page`", "✓", "", "P020" + ":ref:`P021_page`", "✓", "", "P021" + ":ref:`P022_page`", "✓", "", "P022" + ":ref:`P023_page`", "✓", "", "P023" + ":ref:`P024_page`", "✓", "", "P024" + ":ref:`P025_page`", "✓", "", "P025" + ":ref:`P026_page`", "✓", "", "P026" + ":ref:`P027_page`", "✓", "", "P027" + ":ref:`P028_page`", "✓", "", "P028" + ":ref:`P029_page`", "✓", "", "P029" + ":ref:`P030_page`", "✓", "", "P030" + ":ref:`P031_page`", "✓", "", "P031" + ":ref:`P032_page`", "✓", "", "P032" + ":ref:`P033_page`", "✓", "", "P033" + ":ref:`P034_page`", "✓", "", "P034" + ":ref:`P035_page`", "✓", "", "P035" + ":ref:`P036_page`", "✓", "", "P036" + ":ref:`P037_page`", "✓", "", "P037" + ":ref:`P038_page`", "✓", "", "P038" + ":ref:`P039_page`", "✓", "", "P039" + ":ref:`P040_page`", "✓", "", "P040" + ":ref:`P041_page`", "✓", "", "P041" + ":ref:`P042_page`", "✓", "", "P042" + ":ref:`P043_page`", "✓", "", "P043" + ":ref:`P044_page`", "✓", "", "P044" + ":ref:`P045_page`", "✓", "", "P045" + ":ref:`P046_page`", "✓", "", "P046" + ":ref:`P047_page`", "✓", "", "P047" + ":ref:`P048_page`", "✓", "", "P048" + ":ref:`P049_page`", "✓", "", "P049" + ":ref:`P050_page`", "✓", "", "P050" + ":ref:`P051_page`", "✓", "", "P051" + ":ref:`P052_page`", "✓", "", "P052" + ":ref:`P053_page`", "✓", "", "P053" + ":ref:`P054_page`", "✓", "", "P054" + ":ref:`P055_page`", "✓", "", "P055" + ":ref:`P056_page`", "✓", "", "P056" + ":ref:`P057_page`", "✓", "", "P057" + ":ref:`P058_page`", "✓", "", "P058" + ":ref:`P059_page`", "✓", "", "P059" + ":ref:`P060_page`", "✓", "", "P060" + ":ref:`P061_page`", "✓", "", "P061" + ":ref:`P062_page`", "✓", "", "P062" + ":ref:`P063_page`", "✓", "", "P063" + ":ref:`P064_page`", "✓", "", "P064" + ":ref:`P065_page`", "✓", "", "P065" + ":ref:`P066_page`", "✓", "", "P066" + ":ref:`P067_page`", "✓", "", "P067" + ":ref:`P068_page`", "✓", "", "P068" + ":ref:`P069_page`", "✓", "", "P069" + ":ref:`P070_page`", "✓", "", "P070" + ":ref:`P071_page`", "✓", "", "P071" + ":ref:`P072_page`", "✓", "", "P072" + ":ref:`P073_page`", "✓", "", "P073" + ":ref:`P074_page`", "✓", "", "P074" + ":ref:`P075_page`", "✓", "", "P075" + ":ref:`P076_page`", "✓", "", "P076" + ":ref:`P077_page`", "✓", "", "P077" + ":ref:`P078_page`", "✓", "", "P078" + ":ref:`P079_page`", "✓", "", "P079" + ":ref:`P080_page`", "✓", "", "P080" + ":ref:`P081_page`", "✓", "", "P081" + ":ref:`P082_page`", "✓", "", "P082" + ":ref:`P083_page`", "✓", "", "P083" + ":ref:`P084_page`", "✓", "", "P084" + ":ref:`P085_page`", "✓", "", "P085" + ":ref:`P086_page`", "✓", "", "P086" + ":ref:`P087_page`", "✓", "", "P087" + ":ref:`P088_page`", "✓", "", "P088" + ":ref:`P089_page`", "✓", "", "P089" + ":ref:`P090_page`", "✓", "", "P090" + ":ref:`P091_page`", "✓", "", "P091" + ":ref:`P092_page`", "✓", "", "P092" + ":ref:`P093_page`", "✓", "", "P093" + ":ref:`P094_page`", "✓", "✓", "P094" + ":ref:`P095_page`", "✓", "", "P095" + ":ref:`P097_page`", "✓", "", "P097" + ":ref:`P098_page`", "✓", "", "P098" + ":ref:`P099_page`", "✓", "", "P099" + ":ref:`P100_page`", "✓", "", "P100" + ":ref:`P101_page`", "✓", "", "P101" + ":ref:`P102_page`", "✓", "", "P102" + ":ref:`P103_page`", "✓", "", "P103" + ":ref:`P104_page`", "✓", "", "P104" + ":ref:`P105_page`", "✓", "", "P105" + ":ref:`P106_page`", "✓", "", "P106" + ":ref:`P107_page`", "✓", "", "P107" + ":ref:`P108_page`", "✓", "", "P108" + ":ref:`P109_page`", "✓", "", "P109" + ":ref:`P110_page`", "✓", "", "P110" + ":ref:`P111_page`", "✓", "", "P111" + ":ref:`P112_page`", "✓", "", "P112" + ":ref:`P113_page`", "✓", "", "P113" + ":ref:`P114_page`", "✓", "", "P114" + ":ref:`P115_page`", "✓", "", "P115" + ":ref:`P116_page`", "✓", "", "P116" + ":ref:`P117_page`", "✓", "", "P117" + ":ref:`P118_page`", "✓", "", "P118" + ":ref:`P119_page`", "✓", "", "P119" + ":ref:`P120_page`", "✓", "", "P120" + ":ref:`P121_page`", "✓", "", "P121" + ":ref:`P122_page`", "✓", "", "P122" + ":ref:`P123_page`", "✓", "", "P123" + ":ref:`P124_page`", "✓", "", "P124" + ":ref:`P125_page`", "✓", "", "P125" + ":ref:`P126_page`", "✓", "", "P126" + ":ref:`P127_page`", "✓", "", "P127" + ":ref:`P128_page`", "✓", "", "P128" + ":ref:`P129_page`", "✓", "", "P129" + ":ref:`P131_page`", "✓", "", "P131" + ":ref:`P132_page`", "✓", "", "P132" + ":ref:`P133_page`", "✓", "", "P133" + ":ref:`P134_page`", "✓", "", "P134" + ":ref:`P135_page`", "✓", "", "P135" + ":ref:`P137_page`", "✓", "", "P137" + ":ref:`P138_page`", "✓", "", "P138" + ":ref:`P139_page`", "✓", "", "P139" + ":ref:`P140_page`", "✓", "", "P140" + ":ref:`P141_page`", "✓", "", "P141" + ":ref:`P142_page`", "✓", "", "P142" + ":ref:`P143_page`", "✓", "", "P143" + ":ref:`P144_page`", "✓", "", "P144" + ":ref:`P145_page`", "✓", "", "P145" + ":ref:`P146_page`", "✓", "", "P146" + ":ref:`P147_page`", "✓", "", "P147" + ":ref:`P148_page`", "✓", "", "P148" + ":ref:`P150_page`", "✓", "", "P150" + ":ref:`P151_page`", "✓", "", "P151" + ":ref:`P152_page`", "✓", "", "P152" + ":ref:`P153_page`", "✓", "", "P153" + ":ref:`P154_page`", "✓", "", "P154" + ":ref:`P159_page`", "✓", "", "P159" + ":ref:`P162_page`", "✓", "", "P162" + ":ref:`P163_page`", "✓", "", "P163" + ":ref:`P164_page`", "✓", "", "P164" + ":ref:`P165_page`", "✓", "", "P165" + ":ref:`P166_page`", "✓", "", "P166" + ":ref:`P167_page`", "✓", "", "P167" + ":ref:`P168_page`", "✓", "", "P168" + ":ref:`P169_page`", "✓", "", "P169" + ":ref:`P170_page`", "✓", "", "P170" + ":ref:`P172_page`", "✓", "", "P172" + ":ref:`P173_page`", "✓", "", "P173" + ":ref:`P175_page`", "✓", "", "P175" + ":ref:`P176_page`", "✓", "", "P176" + ":ref:`P177_page`", "✓", "", "P177" + ":ref:`P178_page`", "✓", "", "P178" + ":ref:`P180_page`", "✓", "", "P180" + ":ref:`C001_page`", "✓", "", "C001" + ":ref:`C002_page`", "✓", "", "C002" + ":ref:`C003_page`", "✓", "", "C003" + ":ref:`C004_page`", "✓", "", "C004" + ":ref:`C005_page`", "✓", "", "C005" + ":ref:`C006_page`", "✓", "", "C006" + ":ref:`C007_page`", "✓", "", "C007" + ":ref:`C008_page`", "✓", "", "C008" + ":ref:`C009_page`", "✓", "", "C009" + ":ref:`C010_page`", "✓", "", "C010" + ":ref:`C011_page`", "✓", "", "C011" + ":ref:`C012_page`", "✓", "", "C012" + ":ref:`C013_page`", "✓", "", "C013" + ":ref:`C014_page`", "✓", "", "C014" + ":ref:`C016_page`", "✓", "", "C016" + ":ref:`C017_page`", "✓", "", "C017" + ":ref:`C018_page`", "✓", "", "C018" + ":ref:`N001_page`", "✓", "", "N001" + ":ref:`N002_page`", "✓", "", "N002" + ":ref:`NW001_page`", "✓", "", "NW001" + ":ref:`NW002_page`", "✓", "", "NW002" + ":ref:`NW003_page`", "✓", "", "NW003" + ":ref:`NW004_page`", "✓", "", "NW004" + ":ref:`NW005_page`", "✓", "", "NW005" diff --git a/docs/source/Plugin/_plugin_substitutions_p00x.repl b/docs/source/Plugin/_plugin_substitutions_p00x.repl index e8c62df87..b39292265 100644 --- a/docs/source/Plugin/_plugin_substitutions_p00x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p00x.repl @@ -5,6 +5,7 @@ .. |P000_typename| replace:: :cyan:`Internal GPIO handling` .. |P000_porttype| replace:: `.` .. |P000_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P000_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P000_github| replace:: P001_Switch.ino .. _P000_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P001_Switch.ino .. |P000_usedby_GPIO| replace:: :ref:`P000_Relay_page`, :ref:`P000_Servo_motor_page` @@ -22,6 +23,7 @@ .. |P001_porttype| replace:: `.` .. |P001_typename| replace:: :cyan:`Switch Input - Switch` .. |P001_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P001_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P001_github| replace:: P001_Switch.ino .. _P001_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P001_Switch.ino .. |P001_usedby| replace:: :ref:`P001_Switch_page`, :ref:`P001_Door_switch_page`, :ref:`P001_PIR_sensor_page` @@ -35,6 +37,7 @@ .. |P002_typename| replace:: :cyan:`Analog Input - Internal` .. |P002_porttype| replace:: `.` .. |P002_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P002_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P002_github| replace:: P002_ADC.ino .. _P002_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P002_ADC.ino .. |P002_usedby| replace:: `.` @@ -48,6 +51,7 @@ .. |P003_typename| replace:: :cyan:`Generic - Pulse counter` .. |P003_porttype| replace:: `.` .. |P003_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P003_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P003_github| replace:: P003_Pulse.ino .. _P003_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P003_Pulse.ino .. |P003_usedby| replace:: :ref:`P003_LJ12A3_page`, :ref:`P003_TCR5000_page`, :ref:`P003_YFS401_page` @@ -61,6 +65,7 @@ .. |P004_typename| replace:: :cyan:`Environment - DS18xxx/MAX31xxx/1-Wire Temperature` .. |P004_porttype| replace:: `1-Wire` .. |P004_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P004_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P004_github| replace:: P004_Dallas.ino .. _P004_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P004_Dallas.ino .. |P004_usedby| replace:: :ref:`P004_page` @@ -74,6 +79,7 @@ .. |P005_typename| replace:: :cyan:`Environment - DHT11/12/22 SONOFF2301/7021/MS01` .. |P005_porttype| replace:: `.` .. |P005_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P005_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P005_github| replace:: P005_DHT.ino .. _P005_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P005_DHT.ino .. |P005_usedby| replace:: :ref:`P005_DHT11_DHT22_page` @@ -87,6 +93,7 @@ .. |P006_typename| replace:: :cyan:`Environment - BMP085/180` .. |P006_porttype| replace:: `.` .. |P006_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P006_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P006_github| replace:: P006_BMP085.ino .. _P006_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P006_BMP085.ino .. |P006_usedby| replace:: :ref:`P006_BMP085_page`, :ref:`P006_BMP180_page` @@ -100,6 +107,7 @@ .. |P007_typename| replace:: :cyan:`Analog input - PCF8591` .. |P007_porttype| replace:: `.` .. |P007_status| replace:: :green:`NORMAL` +.. |P007_status_lb| replace:: :green:`NORMAL` .. |P007_github| replace:: P007_PCF8591.ino .. _P007_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P007_PCF8591.ino .. |P007_usedby| replace:: `.` @@ -113,6 +121,7 @@ .. |P008_typename| replace:: :cyan:`RFID - Wiegand` .. |P008_porttype| replace:: `.` .. |P008_status| replace:: :green:`NORMAL` +.. |P008_status_lb| replace:: :green:`NORMAL` .. |P008_github| replace:: P008_RFID.ino .. _P008_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P008_RFID.ino .. |P008_usedby| replace:: `.` @@ -126,6 +135,7 @@ .. |P009_typename| replace:: :cyan:`Switch Input - MCP23017` .. |P009_porttype| replace:: `.` .. |P009_status| replace:: :green:`NORMAL` +.. |P009_status_lb| replace:: :green:`NORMAL` .. |P009_github| replace:: P009_MCP.ino .. _P009_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P009_MCP.ino .. |P009_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p01x.repl b/docs/source/Plugin/_plugin_substitutions_p01x.repl index c9f68c4ac..19b64de09 100644 --- a/docs/source/Plugin/_plugin_substitutions_p01x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p01x.repl @@ -3,6 +3,7 @@ .. |P010_typename| replace:: :cyan:`Light/Lux - BH1750` .. |P010_porttype| replace:: `.` .. |P010_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P010_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P010_github| replace:: P010_BH1750.ino .. _P010_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P010_BH1750.ino .. |P010_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P011_typename| replace:: :cyan:`Extra IO - ProMini Extender` .. |P011_porttype| replace:: `.` .. |P011_status| replace:: :green:`NORMAL` +.. |P011_status_lb| replace:: :green:`NORMAL` .. |P011_github| replace:: P011_PME.ino .. _P011_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P011_PME.ino .. |P011_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P012_typename| replace:: :cyan:`Display - LCD2004` .. |P012_porttype| replace:: `.` .. |P012_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`DISPLAY A` +.. |P012_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`DISPLAY A` .. |P012_github| replace:: P012_LCD.ino .. _P012_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P012_LCD.ino .. |P012_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P013_typename| replace:: :cyan:`Distance - HC-SR04/RCW-0001/etc.` .. |P013_porttype| replace:: `.` .. |P013_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P013_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P013_github| replace:: P013_HCSR04.ino .. _P013_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P013_HCSR04.ino .. |P013_usedby| replace:: `.` @@ -56,6 +60,7 @@ .. |P014_typename| replace:: :cyan:`Environment - SI7021/HTU21D` .. |P014_porttype| replace:: `.` .. |P014_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P014_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P014_github| replace:: P014_SI70xx.ino .. _P014_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P014_SI70xx.ino .. |P014_usedby| replace:: `.` @@ -69,6 +74,7 @@ .. |P015_typename| replace:: :cyan:`Light/Lux - TSL2561` .. |P015_porttype| replace:: `.` .. |P015_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P015_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P015_github| replace:: P015_TSL2561.ino .. _P015_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P015_TSL2561.ino .. |P015_usedby| replace:: :ref:`P015_TSL2561_page` @@ -82,6 +88,7 @@ .. |P016_typename| replace:: :cyan:`Communication - IR Receive (TSOP4838)` .. |P016_porttype| replace:: `.` .. |P016_status| replace:: :yellow:`IR` +.. |P016_status_lb| replace:: :yellow:`IR` .. |P016_github| replace:: P016_IR.ino .. _P016_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P016_IR.ino .. |P016_usedby| replace:: `.` @@ -95,6 +102,7 @@ .. |P017_typename| replace:: :cyan:`RFID - PN532` .. |P017_porttype| replace:: `.` .. |P017_status| replace:: :green:`NORMAL` +.. |P017_status_lb| replace:: :green:`NORMAL` .. |P017_github| replace:: P017_PN532.ino .. _P017_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P017_PN532.ino .. |P017_usedby| replace:: `.` @@ -108,6 +116,7 @@ .. |P018_typename| replace:: :cyan:`Dust - Sharp GP2Y10` .. |P018_porttype| replace:: `.` .. |P018_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P018_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P018_github| replace:: P018_Dust.ino .. _P018_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P018_Dust.ino .. |P018_usedby| replace:: `.` @@ -121,6 +130,7 @@ .. |P019_typename| replace:: :cyan:`Switch input - PCF8574` .. |P019_porttype| replace:: `.` .. |P019_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P019_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P019_github| replace:: P019_PCF8574.ino .. _P019_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P019_PCF8574.ino .. |P019_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p02x.repl b/docs/source/Plugin/_plugin_substitutions_p02x.repl index eb1c1005d..0458e66be 100644 --- a/docs/source/Plugin/_plugin_substitutions_p02x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p02x.repl @@ -3,6 +3,7 @@ .. |P020_typename| replace:: :cyan:`Communication - Serial Server` .. |P020_porttype| replace:: `.` .. |P020_status| replace:: :green:`NORMAL` +.. |P020_status_lb| replace:: :green:`NORMAL` .. |P020_github| replace:: P020_Ser2Net.ino .. _P020_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P020_Ser2Net.ino .. |P020_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P021_typename| replace:: :cyan:`Regulator - Level Control` .. |P021_porttype| replace:: `.` .. |P021_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P021_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P021_github| replace:: P021_Level.ino .. _P021_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P021_Level.ino .. |P021_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P022_typename| replace:: :cyan:`Extra IO - PCA9685` .. |P022_porttype| replace:: `.` .. |P022_status| replace:: :green:`NORMAL` +.. |P022_status_lb| replace:: :green:`NORMAL` .. |P022_github| replace:: P022_PCA9685.ino .. _P022_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P022_PCA9685.ino .. |P022_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P023_typename| replace:: :cyan:`Display - OLED SSD1306` .. |P023_porttype| replace:: `.` .. |P023_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`DISPLAY A` +.. |P023_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` :yellow:`DISPLAY A` .. |P023_github| replace:: P023_OLED.ino .. _P023_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P023_OLED.ino .. |P023_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P024_typename| replace:: :cyan:`Environment - MLX90614` .. |P024_porttype| replace:: `.` .. |P024_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P024_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P024_github| replace:: P024_MLX90614.ino .. _P024_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P024_MLX90614.ino .. |P024_usedby| replace:: :ref:`P024_MLX90614_page` @@ -68,6 +73,7 @@ .. |P025_typename| replace:: :cyan:`Analog input - ADS1x15` .. |P025_porttype| replace:: `.` .. |P025_status| replace:: :green:`NORMAL` :yellow:`ENERGY` :yellow:`CLIMATE` +.. |P025_status_lb| replace:: :green:`NORMAL` :yellow:`ENERGY` :yellow:`CLIMATE` .. |P025_github| replace:: P025_ADS1115.ino .. _P025_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P025_ADS1115.ino .. |P025_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P026_typename| replace:: :cyan:`Generic - System Info` .. |P026_porttype| replace:: `.` .. |P026_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P026_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P026_github| replace:: P026_Sysinfo.ino .. _P026_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P026_Sysinfo.ino .. |P026_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P027_typename| replace:: :cyan:`Energy (DC) - INA219` .. |P027_porttype| replace:: `.` .. |P027_status| replace:: :green:`NORMAL` :yellow:`ENERGY` +.. |P027_status_lb| replace:: :green:`NORMAL` :yellow:`ENERGY` .. |P027_github| replace:: P027_INA219.ino .. _P027_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P027_INA219.ino .. |P027_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P028_typename| replace:: :cyan:`Environment - BMx280` .. |P028_porttype| replace:: `.` .. |P028_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P028_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P028_github| replace:: P028_BME280.ino .. _P028_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P028_BME280.ino .. |P028_usedby| replace:: :ref:`P028_page` @@ -120,6 +129,7 @@ .. |P029_typename| replace:: :cyan:`Output - Domoticz MQTT Helper` .. |P029_porttype| replace:: `.` .. |P029_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P029_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P029_github| replace:: P029_Output.ino .. _P029_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P029_Output.ino .. |P029_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p03x.repl b/docs/source/Plugin/_plugin_substitutions_p03x.repl index be1ffaede..21820fd96 100644 --- a/docs/source/Plugin/_plugin_substitutions_p03x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p03x.repl @@ -3,12 +3,14 @@ .. |P030_typename| replace:: :cyan:`Environment - BMP280` .. |P030_porttype| replace:: `.` .. |P030_status| replace:: :gray:`RETIRED` +.. |P030_status_lb| replace:: :gray:`RETIRED` .. |P031_name| replace:: :cyan:`SHT1x` .. |P031_type| replace:: :cyan:`Environment` .. |P031_typename| replace:: :cyan:`Environment - SHT1x` .. |P031_porttype| replace:: `.` .. |P031_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P031_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P031_github| replace:: P031_SHT1X.ino .. _P031_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P031_SHT1X.ino .. |P031_usedby| replace:: `.` @@ -22,6 +24,7 @@ .. |P032_typename| replace:: :cyan:`Environment - MS5611 (GY-63)` .. |P032_porttype| replace:: `.` .. |P032_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P032_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P032_github| replace:: P032_MS5611.ino .. _P032_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P032_MS5611.ino .. |P032_usedby| replace:: `.` @@ -35,6 +38,7 @@ .. |P033_typename| replace:: :cyan:`Generic - Dummy Device` .. |P033_porttype| replace:: `.` .. |P033_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P033_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P033_github| replace:: P033_Dummy.ino .. _P033_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P033_Dummy.ino .. |P033_usedby| replace:: `.` @@ -48,6 +52,7 @@ .. |P034_typename| replace:: :cyan:`Environment - DHT12 (I2C)` .. |P034_porttype| replace:: `.` .. |P034_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P034_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P034_github| replace:: P034_DHT12.ino .. _P034_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P034_DHT12.ino .. |P034_usedby| replace:: `.` @@ -61,6 +66,7 @@ .. |P035_typename| replace:: :cyan:`Communication - IR Transmit` .. |P035_porttype| replace:: `.` .. |P035_status| replace:: :yellow:`IR` +.. |P035_status_lb| replace:: :yellow:`IR` .. |P035_github| replace:: P035_IRTX.ino .. _P035_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P035_IRTX.ino .. |P035_usedby| replace:: `.` @@ -74,6 +80,7 @@ .. |P036_typename| replace:: :cyan:`Display - OLED SSD1306/SH1106 Framed` .. |P036_porttype| replace:: `.` .. |P036_status| replace:: :green:`NORMAL` :yellow:`DISPLAY` :yellow:`CLIMATE` +.. |P036_status_lb| replace:: :green:`NORMAL` :yellow:`DISPLAY` :yellow:`CLIMATE` .. |P036_github| replace:: P036_FrameOLED.ino .. _P036_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P036_FrameOLED.ino .. |P036_usedby| replace:: `.` @@ -87,6 +94,7 @@ .. |P037_typename| replace:: :cyan:`Generic - MQTT Import` .. |P037_porttype| replace:: `.` .. |P037_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P037_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P037_github| replace:: P037_MQTTImport.ino .. _P037_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P037_MQTTImport.ino .. |P037_usedby| replace:: `.` @@ -100,6 +108,7 @@ .. |P038_typename| replace:: :cyan:`Output - NeoPixel (Basic)` .. |P038_porttype| replace:: `.` .. |P038_status| replace:: :green:`NORMAL` :yellow:`NEOPIXEL` :yellow:`CLIMATE` +.. |P038_status_lb| replace:: :green:`NORMAL` :yellow:`NEOPIXEL` :yellow:`CLIMATE` .. |P038_github| replace:: P038_NeoPixel.ino .. _P038_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P038_NeoPixel.ino .. |P038_usedby| replace:: `NeoPixel RGB and RGBW LEDs and LED Strips` @@ -113,6 +122,7 @@ .. |P039_typename| replace:: :cyan:`Environment - Thermosensors` .. |P039_porttype| replace:: `.` .. |P039_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P039_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P039_github| replace:: P039_Thermosensors.ino .. _P039_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P039_Thermosensors.ino .. |P039_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p04x.repl b/docs/source/Plugin/_plugin_substitutions_p04x.repl index fb11018a5..d1f5a7864 100644 --- a/docs/source/Plugin/_plugin_substitutions_p04x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p04x.repl @@ -3,6 +3,7 @@ .. |P040_typename| replace:: :cyan:`RFID - ID12LA/RDM6300` .. |P040_porttype| replace:: `.` .. |P040_status| replace:: :green:`NORMAL` +.. |P040_status_lb| replace:: :green:`NORMAL` .. |P040_github| replace:: P040_ID12.ino .. _P040_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P040_ID12.ino .. |P040_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P041_typename| replace:: :cyan:`Output - NeoPixel (Word Clock)` .. |P041_porttype| replace:: `.` .. |P041_status| replace:: :yellow:`NEOPIXEL` +.. |P041_status_lb| replace:: :yellow:`NEOPIXEL` .. |P041_github| replace:: P041_NeoClock.ino .. _P041_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P041_NeoClock.ino .. |P041_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P042_typename| replace:: :cyan:`Output - NeoPixel (Candle)` .. |P042_porttype| replace:: `.` .. |P042_status| replace:: :yellow:`NEOPIXEL` +.. |P042_status_lb| replace:: :yellow:`NEOPIXEL` .. |P042_github| replace:: P042_Candle.ino .. _P042_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P042_Candle.ino .. |P042_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P043_typename| replace:: :cyan:`Output - Clock` .. |P043_porttype| replace:: `.` .. |P043_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P043_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P043_github| replace:: P043_ClkOutput.ino .. _P043_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P043_ClkOutput.ino .. |P043_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P044_typename| replace:: :cyan:`Communication - P1 Wifi Gateway` .. |P044_porttype| replace:: `.` .. |P044_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P044_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P044_github| replace:: P044_P1WifiGateway.ino .. _P044_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P044_P1WifiGateway.ino .. |P044_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P045_typename| replace:: :cyan:`Gyro - MPU 6050` .. |P045_porttype| replace:: `.` .. |P045_status| replace:: :yellow:`COLLECTION` +.. |P045_status_lb| replace:: :yellow:`COLLECTION` .. |P045_github| replace:: P045_MPU6050.ino .. _P045_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P045_MPU6050.ino .. |P045_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P046_typename| replace:: :cyan:`Hardware - Ventus W266` .. |P046_porttype| replace:: `.` .. |P046_status| replace:: :yellow:`COLLECTION` +.. |P046_status_lb| replace:: :yellow:`COLLECTION` .. |P046_github| replace:: P046_VentusW266.ino .. _P046_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P046_VentusW266.ino .. |P046_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P047_typename| replace:: :cyan:`Environment - Soil moisture sensor` .. |P047_porttype| replace:: `.` .. |P047_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` +.. |P047_status_lb| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` .. |P047_github| replace:: P047_i2c-soil-moisture-sensor.ino .. _P047_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P047_i2c-soil-moisture-sensor.ino .. |P047_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P048_typename| replace:: :cyan:`Motor - Adafruit Motorshield v2` .. |P048_porttype| replace:: `.` .. |P048_status| replace:: :yellow:`COLLECTION` +.. |P048_status_lb| replace:: :yellow:`COLLECTION` .. |P048_github| replace:: P048_Motorshield_v2.ino .. _P048_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P048_Motorshield_v2.ino .. |P048_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P049_typename| replace:: :cyan:`Gases - CO2 MH-Z19` .. |P049_porttype| replace:: `Serial` .. |P049_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P049_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P049_github| replace:: P049_MHZ19.ino .. _P049_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P049_MHZ19.ino .. |P049_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p05x.repl b/docs/source/Plugin/_plugin_substitutions_p05x.repl index 22dee3255..2355be524 100644 --- a/docs/source/Plugin/_plugin_substitutions_p05x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p05x.repl @@ -3,6 +3,7 @@ .. |P050_typename| replace:: :cyan:`Color - TCS34725` .. |P050_porttype| replace:: `.` .. |P050_status| replace:: :yellow:`COLLECTION` +.. |P050_status_lb| replace:: :yellow:`COLLECTION` .. |P050_github| replace:: P050_TCS34725.ino .. _P050_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P050_TCS34725.ino .. |P050_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P051_typename| replace:: :cyan:`Environment - AM2320` .. |P051_porttype| replace:: `.` .. |P051_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` +.. |P051_status_lb| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` .. |P051_github| replace:: P051_AM2320.ino .. _P051_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P051_AM2320.ino .. |P051_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P052_typename| replace:: :cyan:`Gases - CO2 Senseair` .. |P052_porttype| replace:: `Serial` .. |P052_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P052_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P052_github| replace:: P052_SenseAir.ino .. _P052_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P052_SenseAir.ino .. |P052_usedby| replace:: :ref:`P052_S8_page`, :ref:`P052_S11_page`, :ref:`P052_tSense_K70_page` @@ -42,6 +45,7 @@ .. |P053_typename| replace:: :cyan:`Dust - PMSx003 / PMSx003ST` .. |P053_porttype| replace:: `Serial` .. |P053_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P053_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P053_github| replace:: P053_PMSx003.ino .. _P053_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P053_PMSx003.ino .. |P053_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P054_typename| replace:: :cyan:`Communication - DMX512 TX` .. |P054_porttype| replace:: `Serial` .. |P054_status| replace:: :yellow:`COLLECTION` +.. |P054_status_lb| replace:: :yellow:`COLLECTION` .. |P054_github| replace:: P054_DMX512.ino .. _P054_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P054_DMX512.ino .. |P054_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P055_typename| replace:: :cyan:`Notify - Chiming` .. |P055_porttype| replace:: `.` .. |P055_status| replace:: :yellow:`COLLECTION` +.. |P055_status_lb| replace:: :yellow:`COLLECTION` .. |P055_github| replace:: P055_Chiming.ino .. _P055_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P055_Chiming.ino .. |P055_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P056_typename| replace:: :cyan:`Dust - SDS011/018/198` .. |P056_porttype| replace:: `Serial` .. |P056_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P056_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P056_github| replace:: P056_SDS011-Dust.ino .. _P056_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P056_SDS011-Dust.ino .. |P056_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P057_typename| replace:: :cyan:`Display - HT16K33` .. |P057_porttype| replace:: `.` .. |P057_status| replace:: :yellow:`DISPLAY` :yellow:`COLLECTION` +.. |P057_status_lb| replace:: :yellow:`DISPLAY` :yellow:`COLLECTION` .. |P057_github| replace:: P057_HT16K33_LED.ino .. _P057_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P057_HT16K33_LED.ino .. |P057_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P058_typename| replace:: :cyan:`Keypad - HT16K33` .. |P058_porttype| replace:: `.` .. |P058_status| replace:: :yellow:`COLLECTION` +.. |P058_status_lb| replace:: :yellow:`COLLECTION` .. |P058_github| replace:: P058_HT16K33_KeyPad.ino .. _P058_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P058_HT16K33_KeyPad.ino .. |P058_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P059_typename| replace:: :cyan:`Switch input - Rotary Encoder` .. |P059_porttype| replace:: `.` .. |P059_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P059_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P059_github| replace:: P059_Encoder.ino .. _P059_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P059_Encoder.ino .. |P059_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p06x.repl b/docs/source/Plugin/_plugin_substitutions_p06x.repl index 130ffedd6..41d4abdf2 100644 --- a/docs/source/Plugin/_plugin_substitutions_p06x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p06x.repl @@ -3,6 +3,7 @@ .. |P060_typename| replace:: :cyan:`Analog input - MCP3221` .. |P060_porttype| replace:: `.` .. |P060_status| replace:: :yellow:`COLLECTION` +.. |P060_status_lb| replace:: :yellow:`COLLECTION` .. |P060_github| replace:: P060_MCP3221.ino .. _P060_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P060_MCP3221.ino .. |P060_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P061_typename| replace:: :cyan:`Keypad - PCF8574 / MCP23017 / PCA8575` .. |P061_porttype| replace:: `.` .. |P061_status| replace:: :yellow:`COLLECTION` +.. |P061_status_lb| replace:: :yellow:`COLLECTION` .. |P061_github| replace:: P061_KeyPad.ino .. _P061_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P061_KeyPad.ino .. |P061_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P062_typename| replace:: :cyan:`Keypad - MPR121 Touch` .. |P062_porttype| replace:: `.` .. |P062_status| replace:: :yellow:`COLLECTION` +.. |P062_status_lb| replace:: :yellow:`COLLECTION` .. |P062_github| replace:: P062_MPR121_KeyPad.ino .. _P062_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P062_MPR121_KeyPad.ino .. |P062_usedby| replace:: `NXP MPR121 I2C controller` @@ -42,6 +45,7 @@ .. |P063_typename| replace:: :cyan:`Keypad - TTP229 Touch` .. |P063_porttype| replace:: `.` .. |P063_status| replace:: :green:`NORMAL` +.. |P063_status_lb| replace:: :green:`NORMAL` .. |P063_github| replace:: P063_TTP229_KeyPad.ino .. _P063_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P063_TTP229_KeyPad.ino .. |P063_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P064_typename| replace:: :cyan:`Gesture - APDS9960` .. |P064_porttype| replace:: `.` .. |P064_status| replace:: :yellow:`COLLECTION` +.. |P064_status_lb| replace:: :yellow:`COLLECTION` .. |P064_github| replace:: P064_APDS9960.ino .. _P064_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P064_APDS9960.ino .. |P064_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P065_typename| replace:: :cyan:`Notify - DFPlayer-Mini MP3` .. |P065_porttype| replace:: `.` .. |P065_status| replace:: :yellow:`COLLECTION` +.. |P065_status_lb| replace:: :yellow:`COLLECTION` .. |P065_github| replace:: P065_DRF0299_MP3.ino .. _P065_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P065_DRF0299_MP3.ino .. |P065_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P066_typename| replace:: :cyan:`Color - VEML6040` .. |P066_porttype| replace:: `.` .. |P066_status| replace:: :yellow:`COLLECTION` +.. |P066_status_lb| replace:: :yellow:`COLLECTION` .. |P066_github| replace:: P066_VEML6040.ino .. _P066_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P066_VEML6040.ino .. |P066_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P067_typename| replace:: :cyan:`Weight - HX711 Load Cell` .. |P067_porttype| replace:: `.` .. |P067_status| replace:: :yellow:`COLLECTION A` +.. |P067_status_lb| replace:: :yellow:`COLLECTION A` .. |P067_github| replace:: P067_HX711_Load_Cell.ino .. _P067_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P067_HX711_Load_Cell.ino .. |P067_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P068_typename| replace:: :cyan:`Environment - SHT3x` .. |P068_porttype| replace:: `.` .. |P068_status| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` +.. |P068_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` .. |P068_github| replace:: P068_SHT3x.ino .. _P068_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P068_SHT3x.ino .. |P068_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P069_typename| replace:: :cyan:`Environment - LM75A` .. |P069_porttype| replace:: `.` .. |P069_status| replace:: :yellow:`COLLECTION B` :yellow:`CLIMATE` +.. |P069_status_lb| replace:: :yellow:`COLLECTION B` :yellow:`CLIMATE` .. |P069_github| replace:: P069_LM75A.ino .. _P069_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P069_LM75A.ino .. |P069_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p07x.repl b/docs/source/Plugin/_plugin_substitutions_p07x.repl index 280f933fe..5686b41a9 100644 --- a/docs/source/Plugin/_plugin_substitutions_p07x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p07x.repl @@ -4,6 +4,7 @@ .. |P070_typename| replace:: :cyan:`Output - NeoPixel Ring Clock` .. |P070_porttype| replace:: `.` .. |P070_status| replace:: :yellow:`NEOPIXEL` :yellow:`COLLECTION A` +.. |P070_status_lb| replace:: :yellow:`NEOPIXEL` :yellow:`COLLECTION A` .. |P070_github| replace:: P070_NeoPixel_Clock.ino .. _P070_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P070_NeoPixel_Clock.ino .. |P070_usedby| replace:: `.` @@ -17,6 +18,7 @@ .. |P071_typename| replace:: :cyan:`Communication - Kamstrup Multical 401` .. |P071_porttype| replace:: `Serial` .. |P071_status| replace:: :yellow:`COLLECTION A` +.. |P071_status_lb| replace:: :yellow:`COLLECTION A` .. |P071_github| replace:: P071_Kamstrup401.ino .. _P071_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P071_Kamstrup401.ino .. |P071_usedby| replace:: `.` @@ -30,6 +32,7 @@ .. |P072_typename| replace:: :cyan:`Environment - HDC1000/HDC1008/HDC1010/HDC1050/HDC1080 (I2C)` .. |P072_porttype| replace:: `.` .. |P072_status| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` +.. |P072_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` .. |P072_github| replace:: P072_HDC1080.ino .. _P072_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P072_HDC1080.ino .. |P072_usedby| replace:: `.` @@ -43,6 +46,7 @@ .. |P073_typename| replace:: :cyan:`Display - 7-segment display` .. |P073_porttype| replace:: `.` .. |P073_status| replace:: :green:`NORMAL` :yellow:`CLIMATE` +.. |P073_status_lb| replace:: :green:`NORMAL` :yellow:`CLIMATE` .. |P073_github| replace:: P073_7DGT.ino .. _P073_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P073_7DGT.ino .. |P073_usedby| replace:: `.` @@ -56,6 +60,7 @@ .. |P074_typename| replace:: :cyan:`Light/Lux - TSL2591` .. |P074_porttype| replace:: `Serial` .. |P074_status| replace:: :yellow:`COLLECTION A` +.. |P074_status_lb| replace:: :yellow:`COLLECTION A` .. |P074_github| replace:: P074_TSL2591.ino .. _P074_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P074_TSL2591.ino .. |P074_usedby| replace:: `.` @@ -69,6 +74,7 @@ .. |P075_typename| replace:: :cyan:`Display - Nextion` .. |P075_porttype| replace:: `.` .. |P075_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY A` +.. |P075_status_lb| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY A` .. |P075_github| replace:: P075_Nextion.ino .. _P075_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P075_Nextion.ino .. |P075_usedby| replace:: `.` @@ -82,6 +88,7 @@ .. |P076_typename| replace:: :cyan:`Energy (AC) - HLW8012/BL0937` .. |P076_porttype| replace:: `.` .. |P076_status| replace:: :yellow:`ENERGY` +.. |P076_status_lb| replace:: :yellow:`ENERGY` .. |P076_github| replace:: P076_HLW8012.ino .. _P076_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P076_HLW8012.ino .. |P076_usedby| replace:: Sonoff POW (r1), Huafan SS, KMC 70011, Aplic WDP303075, SK03 Outdoor, BlitzWolf SHP, Teckin, Teckin US, Gosund SP1 v23 @@ -95,6 +102,7 @@ .. |P077_typename| replace:: :cyan:`Energy (AC) - CSE7766` .. |P077_porttype| replace:: `.` .. |P077_status| replace:: :yellow:`ENERGY` +.. |P077_status_lb| replace:: :yellow:`ENERGY` .. |P077_github| replace:: P077_CSE7766.ino .. _P077_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P077_CSE7766.ino .. |P077_usedby| replace:: `Sonoff S31, Sonoff Pow R2, Sonoff POW R3xx(D), Sonoff Dual R3` @@ -108,6 +116,7 @@ .. |P078_typename| replace:: :cyan:`Energy (AC) - Eastron SDMxxx Modbus` .. |P078_porttype| replace:: `Serial` .. |P078_status| replace:: :yellow:`ENERGY` +.. |P078_status_lb| replace:: :yellow:`ENERGY` .. |P078_github| replace:: P078_Eastron.ino .. _P078_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P078_Eastron.ino .. |P078_usedby| replace:: `Modbus variants of Eastron SDM120M, SDM120CT, SDM220M, SDM230M, SDM72M, SDM72CTM, SDM630, SDM630MCT, SDM630-EV, SDM54-M, SDM320Y, DDM18SD, SMART X-96, MCS-U22, TAiYEDQ TAC2100` @@ -121,6 +130,7 @@ .. |P079_typename| replace:: :cyan:`Motor - Wemos / Lolin Motorshield` .. |P079_porttype| replace:: :cyan:`I2C` .. |P079_status| replace:: :green:`NORMAL` +.. |P079_status_lb| replace:: :green:`NORMAL` .. |P079_github| replace:: P079_Wemos_Motorshield.ino .. _P079_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P079_Wemos_Motorshield.ino .. |P079_usedby_wemos| replace:: :ref:`P079_WemosMotorshield_page` diff --git a/docs/source/Plugin/_plugin_substitutions_p08x.repl b/docs/source/Plugin/_plugin_substitutions_p08x.repl index 506a7cc6b..2aeb0a03f 100644 --- a/docs/source/Plugin/_plugin_substitutions_p08x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p08x.repl @@ -3,6 +3,7 @@ .. |P080_typename| replace:: :cyan:`Switch input - iButton` .. |P080_porttype| replace:: `.` .. |P080_status| replace:: :yellow:`COLLECTION A` +.. |P080_status_lb| replace:: :yellow:`COLLECTION A` .. |P080_github| replace:: P080_DallasIButton.ino .. _P080_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P080_DallasIButton.ino .. |P080_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P081_typename| replace:: :cyan:`Generic - CRON` .. |P081_porttype| replace:: `.` .. |P081_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` +.. |P081_status_lb| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` .. |P081_github| replace:: P081_Cron.ino .. _P081_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P081_Cron.ino .. |P081_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P082_typename| replace:: :cyan:`Position - GPS` .. |P082_porttype| replace:: `Serial` .. |P082_status| replace:: :yellow:`COLLECTION` +.. |P082_status_lb| replace:: :yellow:`COLLECTION` .. |P082_github| replace:: P082_GPS.ino .. _P082_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P082_GPS.ino .. |P082_usedby| replace:: :ref:`P082_Neo-6M_page`, :ref:`P082_Neo-7M_page`, :ref:`P082_Neo-M8n_page` @@ -42,6 +45,7 @@ .. |P083_typename| replace:: :cyan:`Gases - SGP30 TVOC/eCO2` .. |P083_porttype| replace:: `.` .. |P083_status| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` +.. |P083_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` .. |P083_github| replace:: P083_SGP30.ino .. _P083_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P083_SGP30.ino .. |P083_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P084_typename| replace:: :cyan:`UV - VEML6070` .. |P084_porttype| replace:: `.` .. |P084_status| replace:: :yellow:`COLLECTION A` +.. |P084_status_lb| replace:: :yellow:`COLLECTION A` .. |P084_github| replace:: P084_VEML6070.ino .. _P084_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P084_VEML6070.ino .. |P084_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P085_typename| replace:: :cyan:`Energy (DC) - AcuDC243` .. |P085_porttype| replace:: `Serial` .. |P085_status| replace:: :yellow:`ENERGY` :yellow:`COLLECTION C` +.. |P085_status_lb| replace:: :yellow:`ENERGY` :yellow:`COLLECTION C` .. |P085_github| replace:: P085_AcuDC243.ino .. _P085_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P085_AcuDC243.ino .. |P085_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P086_typename| replace:: :cyan:`Generic - Homie receiver` .. |P086_porttype| replace:: `.` .. |P086_status| replace:: :yellow:`COLLECTION A` +.. |P086_status_lb| replace:: :yellow:`COLLECTION A` .. |P086_github| replace:: P086_Homie.ino .. _P086_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P086_Homie.ino .. |P086_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P087_typename| replace:: :cyan:`Communication - SerialProxy` .. |P087_porttype| replace:: `Serial` .. |P087_status| replace:: :yellow:`COLLECTION C` :yellow:`ENERGY` +.. |P087_status_lb| replace:: :yellow:`COLLECTION C` .. |P087_github| replace:: P087_SerialProxy.ino .. _P087_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P087_SerialProxy.ino .. |P087_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P088_typename| replace:: :cyan:`Energy (Heat) - HeatpumpIR` .. |P088_porttype| replace:: `.` .. |P088_status| replace:: :yellow:`IRext` +.. |P088_status_lb| replace:: :yellow:`IRext` .. |P088_github| replace:: P088_HeatpumpIR.ino .. _P088_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P088_HeatpumpIR.ino .. |P088_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P089_typename| replace:: :cyan:`Communication - Ping` .. |P089_porttype| replace:: `.` .. |P089_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P089_status_lb| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` :yellow:`ENERGY` :yellow:`NEOPIXEL` .. |P089_github| replace:: P089_Ping.ino .. _P089_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P089_Ping.ino .. |P089_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p09x.repl b/docs/source/Plugin/_plugin_substitutions_p09x.repl index 6890fa878..aaab65907 100644 --- a/docs/source/Plugin/_plugin_substitutions_p09x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p09x.repl @@ -3,6 +3,7 @@ .. |P090_typename| replace:: :cyan:`Gases - CCS811 TVOC/eCO2` .. |P090_porttype| replace:: `.` .. |P090_status| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` +.. |P090_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` .. |P090_github| replace:: P090_CCS811.ino .. _P090_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P090_CCS811.ino .. |P090_usedby| replace:: `.` @@ -15,6 +16,7 @@ .. |P091_type| replace:: :cyan:`Gases` .. |P091_typename| replace:: :cyan:`Switch input - Serial MCU controlled switch` .. |P091_status| replace:: :yellow:`COLLECTION C` +.. |P091_status_lb| replace:: :yellow:`COLLECTION C` .. |P091_github| replace:: P091_SerSwitch.ino .. _P091_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P091_SerSwitch.ino .. |P091_usedby| replace:: `.` @@ -27,6 +29,7 @@ .. |P092_type| replace:: :cyan:`Heating` .. |P092_typename| replace:: :cyan:`Heating - DL-Bus (Technische Alternative)` .. |P092_status| replace:: :yellow:`COLLECTION C` +.. |P092_status_lb| replace:: :yellow:`COLLECTION C` .. |P092_github| replace:: P092_DLbus.ino .. _P092_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P092_DLbus.ino .. |P092_usedby| replace:: `.` @@ -39,6 +42,7 @@ .. |P093_type| replace:: :cyan:`Energy (Heat)` .. |P093_typename| replace:: :cyan:`Energy (Heat) - Mitsubishi Heat Pump` .. |P093_status| replace:: :yellow:`COLLECTION D` :yellow:`ENERGY` +.. |P093_status_lb| replace:: :yellow:`COLLECTION D` :yellow:`ENERGY` .. |P093_github| replace:: P093_MitsubishiHP.ino .. _P093_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P093_MitsubishiHP.ino .. |P093_usedby| replace:: `.` @@ -51,6 +55,7 @@ .. |P094_type| replace:: :cyan:`Communication` .. |P094_typename| replace:: :cyan:`Communication - CUL Reader` .. |P094_status| replace:: :yellow:`MAX` +.. |P094_status_lb| replace:: :yellow:`MAX` .. |P094_github| replace:: P094_CULReader.ino .. _P094_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P094_CULReader.ino .. |P094_usedby| replace:: `.` @@ -63,7 +68,8 @@ .. |P095_type| replace:: :cyan:`Display` .. |P095_typename| replace:: :cyan:`Display - TFT ILI934x/ILI948x` .. |P095_porttype| replace:: `.` -.. |P095_status| replace:: :yellow:`COLLECTION` :yellow:`\(ESP32\)` :yellow:`DISPLAY B` +.. |P095_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY B` +.. |P095_status_lb| replace:: :yellow:`DISPLAY B` .. |P095_github| replace:: _P095_ILI9341.ino .. _P095_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P095_ILI9341.ino .. |P095_usedby| replace:: `.` @@ -77,6 +83,7 @@ .. |P097_typename| replace:: :cyan:`Internal - Touch ESP32` .. |P097_porttype| replace:: `.` .. |P097_status| replace:: :yellow:`COLLECTION A` +.. |P097_status_lb| replace:: :yellow:`COLLECTION A` .. |P097_github| replace:: _P097_Esp32Touch.ino .. _P097_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P097_Esp32Touch.ino .. |P097_usedby| replace:: `.` @@ -90,6 +97,7 @@ .. |P098_typename| replace:: :cyan:`Motor - PWM Motor` .. |P098_porttype| replace:: `.` .. |P098_status| replace:: :yellow:`COLLECTION A` :yellow:`COLLECTION D` +.. |P098_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`COLLECTION D` .. |P098_github| replace:: _P098_PWM_motor.ino .. _P098_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P098_PWM_motor.ino .. |P098_usedby| replace:: `.` @@ -103,6 +111,7 @@ .. |P099_typename| replace:: :cyan:`Touch - XPT2046 touchscreen on TFT display` .. |P099_porttype| replace:: `SPI` .. |P099_status| replace:: :yellow:`DISPLAY B` +.. |P099_status_lb| replace:: :yellow:`DISPLAY B` .. |P099_github| replace:: _P099_XPT2046Touch.ino .. _P099_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P099_XPT2046Touch.ino .. |P099_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p10x.repl b/docs/source/Plugin/_plugin_substitutions_p10x.repl index 9a282e375..24ce0675a 100644 --- a/docs/source/Plugin/_plugin_substitutions_p10x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p10x.repl @@ -3,6 +3,7 @@ .. |P100_typename| replace:: :cyan:`Pulse Counter - DS2423` .. |P100_porttype| replace:: `.` .. |P100_status| replace:: :yellow:`COLLECTION B` +.. |P100_status_lb| replace:: :yellow:`COLLECTION B` .. |P100_github| replace:: P100_CCS811.ino .. _P100_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P100_DS2423_counter.ino .. |P100_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P101_typename| replace:: :cyan:`Communication - Wake On LAN` .. |P101_porttype| replace:: `.` .. |P101_status| replace:: :yellow:`COLLECTION B` +.. |P101_status_lb| replace:: :yellow:`COLLECTION B` .. |P101_github| replace:: P101_WakeOnLan.ino .. _P101_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P101_WakeOnLan.ino .. |P101_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P102_typename| replace:: :cyan:`Energy - PZEM-004T (AC) / PZEM-017 (DC)` .. |P102_porttype| replace:: `.` .. |P102_status| replace:: :yellow:`ENERGY` +.. |P102_status_lb| replace:: :yellow:`ENERGY` .. |P102_github| replace:: P102_PZEM004Tv3.ino .. _P102_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P102_PZEM004Tv3.ino .. |P102_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P103_typename| replace:: :cyan:`Environment - Atlas Scientific EZO pH ORP EC DO HUM` .. |P103_porttype| replace:: `.` .. |P103_status| replace:: :yellow:`CLIMATE` +.. |P103_status_lb| replace:: :yellow:`CLIMATE` .. |P103_github| replace:: P103_Atlas_EZO_pH_ORP_EC_DO.ino .. _P103_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino .. |P103_usedby| replace:: `.` @@ -56,6 +60,7 @@ .. |P104_typename| replace:: :cyan:`Display - MAX7219 dot matrix` .. |P104_porttype| replace:: `SPI` .. |P104_status| replace:: :yellow:`DISPLAY A` +.. |P104_status_lb| replace:: :yellow:`DISPLAY A` .. |P104_github| replace:: P104_max7219_Dotmatrix.ino .. _P104_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P104_max7210_Dotmatrix.ino .. |P104_usedby| replace:: `.` @@ -73,13 +78,15 @@ .. |P104-Font-Arabic_typename| replace:: `Arabic font characters` .. |P104-Font-Greek_typename| replace:: `Greek font characters` .. |P104-Font-Katakana_typename| replace:: `Katakana font characters` +.. |P104-Font-Cyrillic-ext_typename| replace:: `Cyrillic ext. font characters` .. |P105_name| replace:: :cyan:`AHT1x/AHT2x/DHT20/AM2301B` .. |P105_type| replace:: :cyan:`Environment` .. |P105_typename| replace:: :cyan:`Environment - AHT1x/AHT2x/DHT20/AM2301B` .. |P105_porttype| replace:: `.` -.. |P105_status| replace:: :yellow:`COLLECTION A` :yellow:`COLLECTION G` :yellow:`CLIMATE` +.. |P105_status| replace:: :yellow:`COLLECTION A` :yellow:`COLLECTION G` :yellow:`CLIMATE` :yellow:`NEOPIXEL` +.. |P105_status_lb| replace:: :yellow:`COLLECTION A` :yellow:`CLIMATE` .. |P105_github| replace:: P105_AHT.ino .. _P105_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P105_AHT.ino .. |P105_usedby| replace:: `.` @@ -97,6 +104,7 @@ .. |P106_typename| replace:: :cyan:`Environment - BME68x` .. |P106_porttype| replace:: `.` .. |P106_status| replace:: :yellow:`COLLECTION B` :yellow:`CLIMATE` +.. |P106_status_lb| replace:: :yellow:`COLLECTION B` :yellow:`CLIMATE` .. |P106_github| replace:: P106_BME680.ino .. _P106_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P106_BME680.ino .. |P106_usedby| replace:: `.` @@ -110,6 +118,7 @@ .. |P107_typename| replace:: :cyan:`UV - SI1145` .. |P107_porttype| replace:: `.` .. |P107_status| replace:: :yellow:`COLLECTION B` +.. |P107_status_lb| replace:: :yellow:`COLLECTION B` .. |P107_github| replace:: P107_Si1145.ino .. _P107_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P107_Si1145.ino .. |P107_usedby| replace:: `.` @@ -123,6 +132,7 @@ .. |P108_typename| replace:: :cyan:`Energy (AC) - DDS238-x` .. |P108_porttype| replace:: `.` .. |P108_status| replace:: :yellow:`COLLECTION B` :yellow:`ENERGY` +.. |P108_status_lb| replace:: :yellow:`COLLECTION B` :yellow:`ENERGY` .. |P108_github| replace:: P108_DDS238.ino .. _P108_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P108_DDS238.ino .. |P108_usedby| replace:: `.` @@ -136,6 +146,7 @@ .. |P109_typename| replace:: :cyan:`Display - ThermoOLED` .. |P109_porttype| replace:: `.` .. |P109_status| replace:: :yellow:`DISPLAY` +.. |P109_status_lb| replace:: `.` .. |P109_github| replace:: P109_ThermoOLED.ino .. _P109_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P109_ThermoOLED.ino .. |P109_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p11x.repl b/docs/source/Plugin/_plugin_substitutions_p11x.repl index 031b9b7ca..264a8611f 100644 --- a/docs/source/Plugin/_plugin_substitutions_p11x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p11x.repl @@ -3,6 +3,7 @@ .. |P110_typename| replace:: :cyan:`Distance - VL53L0X (200cm)` .. |P110_porttype| replace:: `.` .. |P110_status| replace:: :yellow:`COLLECTION B` +.. |P110_status_lb| replace:: :yellow:`COLLECTION B` .. |P110_github| replace:: P110_VL53L0X.ino .. _P110_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P110_VL53L0X.ino .. |P110_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P111_typename| replace:: :cyan:`RFID - MFRC522` .. |P111_porttype| replace:: `.` .. |P111_status| replace:: :yellow:`COLLECTION C` +.. |P111_status_lb| replace:: :yellow:`COLLECTION C` .. |P111_github| replace:: P111_RC522_RFID.ino .. _P111_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P111_RC522_RFID.ino .. |P111_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P112_typename| replace:: :cyan:`Color - AS7265X` .. |P112_porttype| replace:: `.` .. |P112_status| replace:: :yellow:`COLLECTION F` +.. |P112_status_lb| replace:: :yellow:`COLLECTION F` .. |P112_github| replace:: P112_AS7265x.ino .. _P112_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P112_AS7265x.ino .. |P112_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P113_typename| replace:: :cyan:`Distance - VL53L1X (400cm)` .. |P113_porttype| replace:: `.` .. |P113_status| replace:: :yellow:`COLLECTION B` +.. |P113_status_lb| replace:: :yellow:`COLLECTION B` .. |P113_github| replace:: P113_VL53L1X.ino .. _P113_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P113_VL53L1X.ino .. |P113_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P114_typename| replace:: :cyan:`UV - VEML6075` .. |P114_porttype| replace:: `.` .. |P114_status| replace:: :yellow:`COLLECTION D` +.. |P114_status_lb| replace:: :yellow:`COLLECTION D` .. |P114_github| replace:: P114_VEML6075.ino .. _P114_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P114_VEML6075.ino .. |P114_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P115_typename| replace:: :cyan:`Energy - MAX1704x` .. |P115_porttype| replace:: `.` .. |P115_status| replace:: :yellow:`COLLECTION B` :yellow:`ENERGY` +.. |P115_status_lb| replace:: :yellow:`COLLECTION B` :yellow:`ENERGY` .. |P115_github| replace:: _P115_MAX1704x_v2.ino .. _P115_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P115_MAX1704x_v2.ino .. |P115_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P116_typename| replace:: :cyan:`Display - ST77xx TFT` .. |P116_porttype| replace:: `.` .. |P116_status| replace:: :yellow:`DISPLAY B` +.. |P116_status_lb| replace:: :yellow:`DISPLAY B` .. |P116_github| replace:: _P116_ST77xx.ino .. _P116_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P116_ST77xx.ino .. |P116_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P117_typename| replace:: :cyan:`Gases - SCD30 CO2, Humidity, Temperature` .. |P117_porttype| replace:: `.` .. |P117_status| replace:: :yellow:`COLLECTION D` :yellow:`CLIMATE` +.. |P117_status_lb| replace:: :yellow:`COLLECTION D` :yellow:`CLIMATE` .. |P117_github| replace:: _P117_SCD30.ino .. _P117_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P117_SCD30.ino .. |P117_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P118_typename| replace:: :cyan:`Communication - Itho` .. |P118_porttype| replace:: `.` .. |P118_status| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` +.. |P118_status_lb| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` .. |P118_github| replace:: _P118_Itho.ino .. _P118_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P118_Itho.ino .. |P118_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P119_typename| replace:: :cyan:`Gyro - ITG3205` .. |P119_porttype| replace:: `.` .. |P119_status| replace:: :yellow:`COLLECTION E` +.. |P119_status_lb| replace:: :yellow:`COLLECTION E` .. |P119_github| replace:: _P119_ITG3205_Gyro.ino .. _P119_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P119_ITG3205_Gyro.ino .. |P119_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p12x.repl b/docs/source/Plugin/_plugin_substitutions_p12x.repl index 26cd48c12..30c437db8 100644 --- a/docs/source/Plugin/_plugin_substitutions_p12x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p12x.repl @@ -3,6 +3,7 @@ .. |P120_typename| replace:: :cyan:`Acceleration - ADXL345 (I2C)` .. |P120_porttype| replace:: `.` .. |P120_status| replace:: :yellow:`COLLECTION E` +.. |P120_status_lb| replace:: :yellow:`COLLECTION E` .. |P120_github| replace:: P120_ADXL345_Accelerometer.ino .. _P120_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P120_ADXL345_Accelerometer.ino .. |P120_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P121_typename| replace:: :cyan:`Position - HMC5883L` .. |P121_porttype| replace:: `.` .. |P121_status| replace:: :yellow:`COLLECTION E` +.. |P121_status_lb| replace:: :yellow:`COLLECTION E` .. |P121_github| replace:: P121_HMC5883L.ino .. _P121_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P121_HMC5883L.ino .. |P121_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P122_typename| replace:: :cyan:`Environment - SHT2x` .. |P122_porttype| replace:: `.` .. |P122_status| replace:: :yellow:`COLLECTION F` +.. |P122_status_lb| replace:: :yellow:`COLLECTION F` .. |P122_github| replace:: P122_SHT2x.ino .. _P122_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P122_SHT2x.ino .. |P122_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P123_typename| replace:: :cyan:`Touch - I2C Touchscreens` .. |P123_porttype| replace:: `.` .. |P123_status| replace:: :yellow:`DISPLAY B` +.. |P123_status_lb| replace:: `.` .. |P123_github| replace:: P123_I2CTouch.ino .. _P123_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P123_I2CTouch.ino .. |P123_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P124_typename| replace:: :cyan:`Output - I2C Multi Relay` .. |P124_porttype| replace:: `.` .. |P124_status| replace:: :yellow:`COLLECTION D` +.. |P124_status_lb| replace:: :yellow:`COLLECTION D` .. |P124_github| replace:: P124_MultiTelay.ino .. _P124_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P124_MyltiRelay.ino .. |P124_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P125_typename| replace:: :cyan:`Acceleration - ADXL345 (SPI)` .. |P125_porttype| replace:: `.` .. |P125_status| replace:: :yellow:`COLLECTION E` +.. |P125_status_lb| replace:: :yellow:`COLLECTION E` .. |P125_github| replace:: P125_ADXL345_SPI.ino .. _P125_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P125_ADXL345_SPI.ino .. |P125_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P126_typename| replace:: :cyan:`Output - Shift registers (74HC595)` .. |P126_porttype| replace:: `.` .. |P126_status| replace:: :yellow:`COLLECTION E` +.. |P126_status_lb| replace:: :yellow:`COLLECTION E` .. |P126_github| replace:: P126_74HC595.ino .. _P126_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P126_74HC595.ino .. |P126_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P127_typename| replace:: :cyan:`Gases - CO2 CDM7160` .. |P127_porttype| replace:: `.` .. |P127_status| replace:: :yellow:`COLLECTION D` :yellow:`CLIMATE` +.. |P127_status_lb| replace:: :yellow:`COLLECTION D` :yellow:`CLIMATE` .. |P127_github| replace:: P127_CDM7160.ino .. _P127_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P127_CDM7160.ino .. |P127_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P128_typename| replace:: :cyan:`Output - NeoPixel (BusFX)` .. |P128_porttype| replace:: `.` .. |P128_status| replace:: :yellow:`NEOPIXEL` +.. |P128_status_lb| replace:: :yellow:`NEOPIXEL` .. |P128_github| replace:: P128_NeoPixelBusFX.ino .. _P128_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P128_NeoPixelBusFX.ino .. |P128_usedby| replace:: `.` @@ -120,6 +129,7 @@ .. |P129_typename| replace:: :cyan:`Input - Shift registers (74HC165)` .. |P129_porttype| replace:: `.` .. |P129_status| replace:: :yellow:`COLLECTION E` +.. |P129_status_lb| replace:: :yellow:`COLLECTION E` .. |P129_github| replace:: P129_74HC165.ino .. _P129_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P129_74HC165.ino .. |P129_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p13x.repl b/docs/source/Plugin/_plugin_substitutions_p13x.repl index b40952258..1ebb7941f 100644 --- a/docs/source/Plugin/_plugin_substitutions_p13x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p13x.repl @@ -3,6 +3,7 @@ .. |P131_typename| replace:: :cyan:`Display - NeoPixel Matrix` .. |P131_porttype| replace:: `.` .. |P131_status| replace:: :yellow:`NEOPIXEL` +.. |P131_status_lb| replace:: :yellow:`NEOPIXEL` .. |P131_github| replace:: P131_NeoPixelMatrix.ino .. _P131_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P131_NeoPixelMatrix.ino .. |P131_usedby| replace:: `.` @@ -11,24 +12,26 @@ .. |P131_compileinfo| replace:: `.` .. |P131_usedlibraries| replace:: `https://github.com/adafruit/Adafruit_NeoMatrix` -.. |P132_name| replace:: :cyan:`INA3221` +.. |P132_name| replace:: :cyan:`INA3221/INA226/INA228/INA231/INA260` .. |P132_type| replace:: :cyan:`Energy (DC)` -.. |P132_typename| replace:: :cyan:`Energy (DC) - INA3221` +.. |P132_typename| replace:: :cyan:`Energy (DC) - INA3221/INA226/INA228/INA231/INA260` .. |P132_porttype| replace:: `.` .. |P132_status| replace:: :yellow:`ENERGY` +.. |P132_status_lb| replace:: :yellow:`ENERGY` .. |P132_github| replace:: P132_INA3221.ino .. _P132_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P132_INA3221.ino .. |P132_usedby| replace:: `.` -.. |P132_shortinfo| replace:: `3-channel I2C DC Voltage/Current sensor` +.. |P132_shortinfo| replace:: `I2C DC Voltage/Current sensor` .. |P132_maintainer| replace:: `tonhuisman` .. |P132_compileinfo| replace:: `.` -.. |P132_usedlibraries| replace:: `.` +.. |P132_usedlibraries| replace:: `https://github.com/Zanduino/INA (small adjustments applied)` .. |P133_name| replace:: :cyan:`LTR390 UV` .. |P133_type| replace:: :cyan:`UV` .. |P133_typename| replace:: :cyan:`UV - LTR390` .. |P133_porttype| replace:: `.` .. |P133_status| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` +.. |P133_status_lb| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` .. |P133_github| replace:: P133_LTR390.ino .. _P133_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P133_LTR390.ino .. |P133_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P134_typename| replace:: :cyan:`Distance - A02YYUW` .. |P134_porttype| replace:: `.` .. |P134_status| replace:: :yellow:`COLLECTION A` +.. |P134_status_lb| replace:: :yellow:`COLLECTION A` .. |P134_github| replace:: P134_A02YYUW.ino .. _P134_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P134_A02YYUW.ino .. |P134_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P135_typename| replace:: :cyan:`Gases - CO2 SCD4x` .. |P135_porttype| replace:: `.` .. |P135_status| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` +.. |P135_status_lb| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` .. |P135_github| replace:: P135_SCD4x.ino .. _P135_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P135_SCD4x.ino .. |P135_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P137_typename| replace:: :cyan:`Power mgt - AXP192 Power management ESP32` .. |P137_porttype| replace:: `.` .. |P137_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P137_status_lb| replace:: `.` .. |P137_github| replace:: P137_AXP192.ino .. _P137_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P137_AXP192.ino .. |P137_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P138_typename| replace:: :cyan:`Power mgt - IP5306 Power management` .. |P138_porttype| replace:: `.` .. |P138_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P138_status_lb| replace:: `.` .. |P138_github| replace:: P138_IP5306.ino .. _P138_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P138_IP5306.ino .. |P138_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P139_typename| replace:: :cyan:`Power mgt - AXP2101 Power management ESP32` .. |P139_porttype| replace:: `.` .. |P139_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P139_status_lb| replace:: `.` .. |P139_github| replace:: P139_AXP2101.ino .. _P139_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P139_AXP2101.ino .. |P139_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p14x.repl b/docs/source/Plugin/_plugin_substitutions_p14x.repl index 49ff184bc..a527cf495 100644 --- a/docs/source/Plugin/_plugin_substitutions_p14x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p14x.repl @@ -3,6 +3,7 @@ .. |P140_typename| replace:: :cyan:`Keypad - CardKB I2C Keyboard` .. |P140_porttype| replace:: `.` .. |P140_status| replace:: :yellow:`ESP32` +.. |P140_status_lb| replace:: `.` .. |P140_github| replace:: P140_CardKB.ino .. _P140_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P140_CardKB.ino .. |P140_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P141_typename| replace:: :cyan:`Display - PCD8544 Nokia 5110 LCD` .. |P141_porttype| replace:: `.` .. |P141_status| replace:: :yellow:`DISPLAY B` +.. |P141_status_lb| replace:: :yellow:`DISPLAY B` .. |P141_github| replace:: P141_PCD8544_Nokia5110.ino .. _P141_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P141_PCD8544_Nokia5110.ino .. |P141_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P142_typename| replace:: :cyan:`Position - AS5600(L) Magnetic angle` .. |P142_porttype| replace:: `.` .. |P142_status| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` +.. |P142_status_lb| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` .. |P142_github| replace:: P142_AS5600.ino .. _P142_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P142_AS5600.ino .. |P142_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P143_typename| replace:: :cyan:`Switch input - I2C Rotary encoders` .. |P143_porttype| replace:: `.` .. |P143_status| replace:: :yellow:`COLLECTION C` :yellow:`DISPLAY` +.. |P143_status_lb| replace:: :yellow:`COLLECTION C` :yellow:`DISPLAY` .. |P143_github| replace:: P143_I2C_Rotary.ino .. _P143_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P143_I2C_Rotary.ino .. |P143_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P144_typename| replace:: :cyan:`Dust - PM1006(K) (Vindriktning)` .. |P144_porttype| replace:: `.` .. |P144_status| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` +.. |P144_status_lb| replace:: :yellow:`COLLECTION E` :yellow:`CLIMATE` .. |P144_github| replace:: P144_Vindriktning.ino .. _P144_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P144_Vindrikning.ino .. |P144_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P145_typename| replace:: :cyan:`Gases - MQxxx (MQ135 CO2, MQ3 Alcohol)` .. |P145_porttype| replace:: `.` .. |P145_status| replace:: :yellow:`COLLECTION F` +.. |P145_status_lb| replace:: :yellow:`COLLECTION F` .. |P145_github| replace:: P145_MQxxx.ino .. _P145_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P145_MQxxx.ino .. |P145_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P146_typename| replace:: :cyan:`Generic - Cache Reader` .. |P146_porttype| replace:: `.` .. |P146_status| replace:: :green:`NORMAL` +.. |P146_status_lb| replace:: :green:`NORMAL` .. |P146_github| replace:: P146_CacheControllerReader.ino .. _P146_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P146_CacheControllerReader.ino .. |P146_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P147_typename| replace:: :cyan:`Gases - SGP4x VOC(/NOx)` .. |P147_porttype| replace:: `.` .. |P147_status| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` +.. |P147_status_lb| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` .. |P147_github| replace:: P147_SGP4x.ino .. _P147_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P147_SGP4x.ino .. |P147_usedby| replace:: `.` @@ -107,6 +115,7 @@ .. |P148_typename| replace:: :cyan:`Display - POWR3xxD/THR3xxD` .. |P148_porttype| replace:: `.` .. |P148_status| replace:: :yellow:`DISPLAY B` :yellow:`ENERGY` +.. |P148_status_lb| replace:: `.` .. |P148_github| replace:: P148_POWRxxD_THR3xxD.ino .. _P148_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P148_POWRxxD_THR3xxD.ino .. |P148_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p15x.repl b/docs/source/Plugin/_plugin_substitutions_p15x.repl index 3781633ef..f83592e64 100644 --- a/docs/source/Plugin/_plugin_substitutions_p15x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p15x.repl @@ -3,6 +3,7 @@ .. |P150_typename| replace:: :cyan:`Environment - TMP117 Temperature` .. |P150_porttype| replace:: `.` .. |P150_status| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` +.. |P150_status_lb| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` .. |P150_github| replace:: P150_TMP117.ino .. _P150_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P150_TMP117.ino .. |P150_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P151_typename| replace:: :cyan:`Environment - I2C Honeywell Pressure` .. |P151_porttype| replace:: `.` .. |P151_status| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` +.. |P151_status_lb| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` .. |P151_github| replace:: P151_Honeywell_pressure.ino .. _P151_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P151_Honeywell_pressure.ino .. |P151_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P152_typename| replace:: :cyan:`Output - ESP32 DAC` .. |P152_porttype| replace:: `.` .. |P152_status| replace:: :green:`NORMAL` +.. |P152_status_lb| replace:: :green:`NORMAL` .. |P152_github| replace:: P152_DAC.ino .. _P152_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P152_DAC.ino .. |P152_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P153_typename| replace:: :cyan:`Environment - SHT4x` .. |P153_porttype| replace:: `.` .. |P153_status| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` +.. |P153_status_lb| replace:: :yellow:`COLLECTION F` :yellow:`CLIMATE` .. |P153_github| replace:: P153_SHT4x.ino .. _P153_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P153_SHT4x.ino .. |P153_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P154_typename| replace:: :cyan:`Environment - BMP3xx (I2C)` .. |P154_porttype| replace:: `.` .. |P154_status| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` +.. |P154_status_lb| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` .. |P154_github| replace:: P154_BMP3xx.ino .. _P154_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P154_BMP3xx.ino .. |P154_usedby| replace:: `.` @@ -68,6 +73,7 @@ .. |P159_typename| replace:: :cyan:`Presence - LD2410` .. |P159_porttype| replace:: `.` .. |P159_status| replace:: :yellow:`COLLECTION G` +.. |P159_status_lb| replace:: :yellow:`COLLECTION G` .. |P159_github| replace:: P159_LD2410.ino .. _P159_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P159_LD2410.ino .. |P159_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p16x.repl b/docs/source/Plugin/_plugin_substitutions_p16x.repl index 4c533a76e..121b7ea69 100644 --- a/docs/source/Plugin/_plugin_substitutions_p16x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p16x.repl @@ -3,6 +3,7 @@ .. |P162_typename| replace:: :cyan:`Output - MCP42xxx Digipot` .. |P162_porttype| replace:: `.` .. |P162_status| replace:: :yellow:`COLLECTION G` +.. |P162_status_lb| replace:: :yellow:`COLLECTION G` .. |P162_github| replace:: P162_MCP42xxx.ino .. _P162_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P162_MCP42xxx.ino .. |P162_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P163_typename| replace:: :cyan:`Environment - RadSens I2C radiation counter` .. |P163_porttype| replace:: `.` .. |P163_status| replace:: :yellow:`COLLECTION G` +.. |P163_status_lb| replace:: `.` .. |P163_github| replace:: P163_RadSens.ino .. _P163_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P163_RadSens.ino .. |P163_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P164_typename| replace:: :cyan:`Gases - ENS16x TVOC/eCO2` .. |P164_porttype| replace:: `.` .. |P164_status| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` +.. |P164_status_lb| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE` .. |P164_github| replace:: P164_gases_ens160.ino .. _P164_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P164_gases_ens160.ino .. |P164_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P165_typename| replace:: :cyan:`Display - NeoPixel (7-Segment)` .. |P165_porttype| replace:: `.` .. |P165_status| replace:: :yellow:`NEOPIXEL` +.. |P165_status_lb| replace:: :yellow:`NEOPIXEL` .. |P165_github| replace:: P165_7SegNeopixel.ino .. _P165_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P165_7SegNeopixel.ino .. |P165_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P166_typename| replace:: :cyan:`Output - GP8403 Dual channel DAC 0-10V` .. |P166_porttype| replace:: `.` .. |P166_status| replace:: :yellow:`COLLECTION G` +.. |P166_status_lb| replace:: :yellow:`COLLECTION G` .. |P166_github| replace:: P166_GP8403.ino .. _P166_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P166_GP8403.ino .. |P166_usedby| replace:: `.` @@ -67,7 +72,8 @@ .. |P167_type| replace:: :cyan:`Environment` .. |P167_typename| replace:: :cyan:`Environment - Sensirion SEN5x (IKEA Vindstyrka)` .. |P167_porttype| replace:: `.` -.. |P167_status| replace:: :yellow:`CLIMATE B` +.. |P167_status| replace:: :yellow:`CLIMATE A` +.. |P167_status_lb| replace:: :yellow:`CLIMATE A` .. |P167_github| replace:: P167_Vindstyrka.ino .. _P167_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P167_Vindstyrka.ino .. |P167_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P168_typename| replace:: :cyan:`Light/Lux - VEML6030/VEML7700` .. |P168_porttype| replace:: `.` .. |P168_status| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE B` +.. |P168_status_lb| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE B` .. |P168_github| replace:: P168_VEML6030_7700.ino .. _P168_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P168_VEML6030_7700.ino .. |P168_usedby| replace:: `.` @@ -94,6 +101,7 @@ .. |P169_typename| replace:: :cyan:`Environment - AS3935 Lightning Detector` .. |P169_porttype| replace:: `.` .. |P169_status| replace:: :yellow:`CLIMATE B` +.. |P169_status_lb| replace:: `.` .. |P169_github| replace:: P169_AS3935_LightningDetector.ino .. _P169_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P169_AS3935_LightningDetector.ino .. |P169_usedby| replace:: `.` diff --git a/docs/source/Plugin/_plugin_substitutions_p17x.repl b/docs/source/Plugin/_plugin_substitutions_p17x.repl index 48d81f750..f22675238 100644 --- a/docs/source/Plugin/_plugin_substitutions_p17x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p17x.repl @@ -3,6 +3,7 @@ .. |P170_typename| replace:: :cyan:`Input - I2C Liquid level sensor` .. |P170_porttype| replace:: `.` .. |P170_status| replace:: :yellow:`COLLECTION G` +.. |P170_status_lb| replace:: :yellow:`COLLECTION G` .. |P170_github| replace:: P170_Waterlevel.ino .. _P170_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P170_Waterlevel.ino .. |P170_usedby| replace:: `.` @@ -16,6 +17,7 @@ .. |P172_typename| replace:: :cyan:`Environment - BMP3xx (SPI)` .. |P172_porttype| replace:: `.` .. |P172_status| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE B` +.. |P172_status_lb| replace:: :yellow:`COLLECTION G` :yellow:`CLIMATE B` .. |P172_github| replace:: P172_BMP3xx_SPI.ino .. _P172_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P172_BMP3xx_SPI.ino .. |P172_usedby| replace:: `.` @@ -29,6 +31,7 @@ .. |P173_typename| replace:: :cyan:`Environment - SHTC3` .. |P173_porttype| replace:: `.` .. |P173_status| replace:: :yellow:`COLLECTION H` :yellow:`CLIMATE B` +.. |P173_status_lb| replace:: :yellow:`CLIMATE B` .. |P173_github| replace:: P173_SHTC3.ino .. _P173_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P173_SHTC3.ino .. |P173_usedby| replace:: `.` @@ -42,6 +45,7 @@ .. |P175_typename| replace:: :cyan:`Dust - PMSx003i (I2C)` .. |P175_porttype| replace:: `I2C` .. |P175_status| replace:: :yellow:`CLIMATE B` +.. |P175_status_lb| replace:: `.` .. |P175_github| replace:: P175_PMSx003i.ino .. _P175_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P175_PMSx003i.ino .. |P175_usedby| replace:: `.` @@ -55,6 +59,7 @@ .. |P176_typename| replace:: :cyan:`Communication - Victron VE.Direct` .. |P176_porttype| replace:: `.` .. |P176_status| replace:: :yellow:`ENERGY` +.. |P176_status_lb| replace:: `.` .. |P176_github| replace:: P176_VE_Direct.ino .. _P176_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P176_VE_Direct.ino .. |P176_usedby| replace:: `Victron Energy devices supporting the VE.Direct protocol` @@ -68,6 +73,7 @@ .. |P177_typename| replace:: :cyan:`Environment - XDB401 I2C Pressure` .. |P177_porttype| replace:: `.` .. |P177_status| replace:: :yellow:`COLLECTION H` :yellow:`CLIMATE B` +.. |P177_status_lb| replace:: `.` .. |P177_github| replace:: P177_XDB_pressure.ino .. _P177_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P177_XDB_pressure.ino .. |P177_usedby| replace:: `.` @@ -81,6 +87,7 @@ .. |P178_typename| replace:: :cyan:`Extra IO - LU9685 Servo controller` .. |P178_porttype| replace:: `.` .. |P178_status| replace:: :yellow:`COLLECTION H` :yellow:`CLIMATE B` +.. |P178_status_lb| replace:: `.` .. |P178_github| replace:: P178_LU9685.ino .. _P178_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P178_LU9685.ino .. |P178_usedby| replace:: `LU9685 16/20 channel servo controller` diff --git a/docs/source/Plugin/_plugin_substitutions_p18x.repl b/docs/source/Plugin/_plugin_substitutions_p18x.repl index f6f5c0479..654288b7c 100644 --- a/docs/source/Plugin/_plugin_substitutions_p18x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p18x.repl @@ -3,6 +3,7 @@ .. |P180_typename| replace:: :cyan:`Generic - I2C Generic` .. |P180_porttype| replace:: `.` .. |P180_status| replace:: :yellow:`COLLECTION` :yellow:`CLIMATE` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P180_status_lb| replace:: :yellow:`NEOPIXEL` .. |P180_github| replace:: P180_I2C_generic.ino .. _P180_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P180_I2C_generic.ino .. |P180_usedby| replace:: `.` diff --git a/docs/source/conf.py b/docs/source/conf.py index 96817160c..09eac6e2e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,7 +15,7 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) -from recommonmark.parser import CommonMarkParser +# from recommonmark.parser import CommonMarkParser import sphinx_bootstrap_theme # -- Project information ----------------------------------------------------- @@ -52,15 +52,15 @@ extensions = [ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -source_parsers = { - '.md': CommonMarkParser, -} +# source_parsers = { +# '.md': CommonMarkParser, +# } # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = ['.rst', '.md'] -#source_suffix = '.rst' +# source_suffix = ['.rst', '.md'] +source_suffix = {'.rst': 'restructuredtext'} # The master toctree document. master_doc = 'index' diff --git a/docs/source/index.rst b/docs/source/index.rst index 7adfb7c4e..377074cc5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,7 @@ Welcome to ESP Easy's documentation! ESPEasy/FunctionBlocks.rst Config/Config.rst Hardware/Hardware.rst + Interfaces/Interfaces.rst Network/_Network.rst Controller/_Controller.rst Plugin/_Plugin.rst @@ -56,6 +57,7 @@ Welcome to ESP Easy's documentation! Controller/C* Notify/N* Plugin/P* + Network/NW* Rules/HTTPResponseParser.rst diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp index 88c0e2ae5..ce52a44d5 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp @@ -111,6 +111,9 @@ @param cs Chip select pin # (OK to pass -1 if CS tied to GND). @param dc Data/Command pin # (required). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ @@ -133,14 +136,20 @@ Adafruit_ILI9341::Adafruit_ILI9341(int8_t cs, int8_t dc, int8_t rst, uint8_t mod @param cs Chip select pin # (optional, pass -1 if unused and CS is tied to GND). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ -// Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, -// int8_t rst) -// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, spiClass, cs, dc, -// rst) {} +Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, + int8_t rst, uint8_t model, uint16_t w, uint16_t h) + : Adafruit_SPITFT(w, h, spiClass, cs, dc, rst) { + _model = model; + _w = w; + _h = h; +} #endif // end !ESP8266 /**************************************************************************/ diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h index 349323700..68735d225 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h @@ -163,8 +163,8 @@ public: #if !defined(ESP8266) - // Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, - // int8_t rst = -1); + Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + int8_t rst = -1, uint8_t model = 0, uint16_t w = 0, uint16_t h = 0); #endif // end !ESP8266 // Adafruit_ILI9341(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, // int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp index f57ae8ec4..4f59d31e4 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp @@ -58,10 +58,10 @@ Port_ESPEasySerial_USB_HWCDC_t::Port_ESPEasySerial_USB_HWCDC_t(const ESPEasySeri { _config.port = ESPEasySerialPort::usb_hw_cdc; #if !ARDUINO_USB_CDC_ON_BOOT - USB.begin(); +// USB.begin(); #endif if (_hwcdc_serial != nullptr) { - _hwcdc_serial->end(); +// _hwcdc_serial->end(); // _config.rxBuffSize = _hwcdc_serial->setRxBufferSize(_config.rxBuffSize); // _config.txBuffSize = _hwcdc_serial->setTxBufferSize(_config.txBuffSize); diff --git a/lib/GPIO_DirectAccess/GPIO_Direct_Access.cpp b/lib/GPIO_DirectAccess/GPIO_Direct_Access.cpp index d8001d0d4..2e1962e67 100644 --- a/lib/GPIO_DirectAccess/GPIO_Direct_Access.cpp +++ b/lib/GPIO_DirectAccess/GPIO_Direct_Access.cpp @@ -124,4 +124,29 @@ void IRAM_ATTR DIRECT_PINMODE_INPUT_ISR(IO_REG_TYPE pin) DIRECT_MODE_INPUT(reg, PIN_TO_BITMASK(pin)); } +int32_t IRAM_ATTR DIRECT_measureWaitForPinState_ISR( + IO_REG_TYPE gpio_pin_rx, + uint32_t start_usec, + int32_t timeout_usec, + bool newState) +{ + int32_t passed{}; + + do { + passed = (int32_t) ((uint32_t)micros() - start_usec); + } while (passed < timeout_usec && + (!DIRECT_pinRead_ISR(gpio_pin_rx) == newState)); // Using '!' to do a quick cast to bool + + if ((passed > timeout_usec) || + (!DIRECT_pinRead_ISR(gpio_pin_rx) == newState)) { + return -1; + } + + // N.B. we allow the situation where passed == timeout_usec + // and pin might have reached the state we're waiting for. + // This way we don't need to average for time before reading pin state and after. + return passed; + +} + #endif // if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) diff --git a/lib/GPIO_DirectAccess/GPIO_Direct_Access.h b/lib/GPIO_DirectAccess/GPIO_Direct_Access.h index 625ea1fec..25481d2c2 100644 --- a/lib/GPIO_DirectAccess/GPIO_Direct_Access.h +++ b/lib/GPIO_DirectAccess/GPIO_Direct_Access.h @@ -131,6 +131,8 @@ void DIRECT_pinWrite_ISR(IO_REG_TYPE pin, bool pinstate); void DIRECT_PINMODE_OUTPUT_ISR(IO_REG_TYPE pin); void DIRECT_PINMODE_INPUT_ISR(IO_REG_TYPE pin); +int32_t DIRECT_measureWaitForPinState_ISR(IO_REG_TYPE gpio_pin_rx, uint32_t start_usec, int32_t timeout_usec, bool newState); + #elif defined(ARDUINO_ARCH_ESP32) #if ESP_IDF_VERSION_MAJOR < 5 #include @@ -292,6 +294,8 @@ void DIRECT_pinWrite_ISR(IO_REG_TYPE pin, bool pinstate); void DIRECT_PINMODE_OUTPUT_ISR(IO_REG_TYPE pin); void DIRECT_PINMODE_INPUT_ISR(IO_REG_TYPE pin); +int32_t DIRECT_measureWaitForPinState_ISR(IO_REG_TYPE gpio_pin_rx, uint32_t start_usec, int32_t timeout_usec, bool newState); + /* // https://github.com/PaulStoffregen/OneWire/pull/47 // https://github.com/stickbreaker/OneWire/commit/6eb7fc1c11a15b6ac8c60e5671cf36eb6829f82c diff --git a/lib/INA/.gitattributes b/lib/INA/.gitattributes new file mode 100644 index 000000000..6adacb617 --- /dev/null +++ b/lib/INA/.gitattributes @@ -0,0 +1,42 @@ +######################################################################## +# This section defines which file types are associated with which # +# linguist-documentation language types. Initially this was set so that# +# the "Arduino" language was chosen, but that is no longer considered # +# a language so everything has been reverted to "c++" # +# # +# Date Author Comments # +# ========== ========== ============================================== # +# 2018-06-24 SV-Zanshin Changed file # +# # +######################################################################## +examples/ linguist-documentation=false +*.c linguist-language=c++ +*.ino linguist-language=c++ +*.pde linguist-language=c++ +*.c linguist-language=c++ +*.h linguist-language=c++ +*.cpp linguist-language=c++ + +######################################################################## +# Auto detect text files and perform LF normalization # +######################################################################## +* text=auto + +######################################################################## +# Custom setting for Visual Studio/Atmel Studio files # +######################################################################## +*.cs diff=csharp + +######################################################################## +# Standard to msysgit # +######################################################################## +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/lib/INA/.gitignore b/lib/INA/.gitignore new file mode 100644 index 000000000..b7eba2c0c --- /dev/null +++ b/lib/INA/.gitignore @@ -0,0 +1,109 @@ +######################################################################## +# This file defines which file types are to be ignored and skipped by # +# git so that they are not transferred and committed. # +# # +# Date Author Comments # +# ========== ========== ============================================== # +# 2021-12-11 SV-Zanshin New ignore for Visual Studio 2022 *.vcxitems # +# 2020-12-11 SV-Zanshin Ignores for doxygen and clang-format files # +# 2019-01-31 SV-Zanshin Ignores for doxygen # +# 2018-09-22 SV-Zanshin Ignores for MS VS 2017 # +# 2018-06-24 SV-Zanshin Changed file # +# # +######################################################################## + +######################################################################## +# Windows image file caches # +######################################################################## +Thumbs.db +ehthumbs.db + +######################################################################## +# Windows folder config file # +######################################################################## +Desktop.ini + +######################################################################## +# Recycle Bin used on file shares # +######################################################################## +$RECYCLE.BIN/ + +######################################################################## +# Windows Installer files # +######################################################################## +*.cab +*.msi +*.msm +*.msp + +######################################################################## +# Windows shortcuts # +######################################################################## +*.lnk + +######################################################################## +# Operating System Files - OSX # +######################################################################## +.DS_Store +.AppleDouble +.LSOverride + +######################################################################## +# Thumbnails # +######################################################################## +._* + +######################################################################## +# Files that might appear in the root of a volume # +######################################################################## +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +######################################################################## +# Directories potentially created on remote AFP share # +######################################################################## +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +######################################################################## +# Files and directories from the Atmel Studio Arduino IDE # +######################################################################## +.vs +__vm +Debug +Release +*.atsln +*.componentinfo.xml +*.cppproj + +######################################################################## +# Files and directories from Microsoft Visual Studio # +######################################################################## +*.sln +*.vcxproj +*.filters +*.user +*.vcxitems + +######################################################################## +# Files and directories from Visual Studio Code # +######################################################################## +.vscode + +######################################################################## +# Files and directories from doxygen # +######################################################################## +html +Doxyfile + +######################################################################## +# Clang-format files # +######################################################################## +.clang-format diff --git a/lib/INA/LICENSE b/lib/INA/LICENSE new file mode 100644 index 000000000..9e419e042 --- /dev/null +++ b/lib/INA/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/lib/INA/examples/BackgroundRead/.due.test.skip b/lib/INA/examples/BackgroundRead/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.esp32.test.skip b/lib/INA/examples/BackgroundRead/.esp32.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.esp8266.test.skip b/lib/INA/examples/BackgroundRead/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.m4.test.skip b/lib/INA/examples/BackgroundRead/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.zero.test.skip b/lib/INA/examples/BackgroundRead/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/BackgroundRead.ino b/lib/INA/examples/BackgroundRead/BackgroundRead.ino new file mode 100644 index 000000000..763a46a23 --- /dev/null +++ b/lib/INA/examples/BackgroundRead/BackgroundRead.ino @@ -0,0 +1,192 @@ +/*! + * + * @file BackgroundRead.ino + * + * @brief Example program for the INA Library demonstrating background reads + * + * @section BackgroundRead_section Description + * + * Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality. + * It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and + * current information in the background while allowing the main Arduino code to continue processing + * normally until it is ready to consume the readings.\n\n + * + * The example program uses the Arduino AVR-based interrupt mechanism and will not function on other + * platforms\n\n + * + * Detailed documentation can be found on the GitHub Wiki pages at + * https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of + * different types and this program demonstrates interrupts and background processing, it will limit + * itself to using the first INA226 detected. This is easily changed in the if another device type + * or device number to test is required.\n + * + * This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place, + * this is the same setup that can be found in the Adafruit INA226 breakout board. The complex + * calibration options are done at runtime using the 2 parameters specified in the "begin()" call + * and the library has gone to great lengths to avoid the use of floating point to conserve space + * and minimize runtime. This demo program uses floating point only to convert and display the data + * conveniently. The INA226 uses 15 bits of precision, and even though the current and watt + * information is returned using 32-bit integers the precision remains the same.\n The INA226 is set + * up to measure using the maximum conversion length (and maximum accuracy) and then average those + * readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2 + * times a second. The pin-change interrupt handler is called when a reading is finished and the + * INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global + * variables. The main program will do whatever processing it has to and every 5 seconds it will + * display the current averaged readings and reset them.\n + * + * The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + * contains the information required in order to hook up the device. Unfortunately it comes as a + * VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is + * quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I + * needed in order to monitor a 24V battery system which goes above 28V while charging and which is + * above the absolute limits of the INA219). It is also significantly more accurate than the INA219, + * plus has an alert pin.\n The interrupt is set to pin 8. The tests were done on an Arduino Micro, + * and the Atmel 82U4 chip only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so + * pin 8 was chosen. + * + * @section BackgroundRead_license GNU General Public License v3.0 + * + * This program is free software : you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version.This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + * have received a copy of the GNU General Public License along with this program(see + * https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + * . + * + * @section BackgroundRead_author Author + * + * Written by Arnd at https://www.github.com/SV-Zanshin + * + * @section BackgroundRead_versions Changelog + * + * Version | Date | Developer | Comments + * ------- | ---------- | ----------- | ------------------------------------------------------------ + * 1.0.5 | 2020-12-01 | SV-Zanshin | Corrected "alertOnConversion()" call + * 1.0.4 | 2019-02-16 | SV-Zanshin | ifdef so that sketch won't compile on incompatible platforms + * 1.0.3 | 2019-01-09 | SV-Zanshin | Cleaned up doxygen formatting + * 1.0.2 | 2018-12-28 | SV-Zanshin | Converted comments to doxygen format + * 1.0.0 | 2018-06-23 | SV-Zanshin | Cloned and adapted example from old deprecated INA226 + * library + * + */ +#if !defined(__AVR__) + #error Example program only functions on Atmel AVR-Based platforms +#endif + +/************************************************************************************************** +** Declare all include files ** +**************************************************************************************************/ +#include // Include the INA library + +/************************************************************************************************** +** Declare program Constants ** +**************************************************************************************************/ +const uint8_t INA_ALERT_PIN = 8; ///< Pin-Change pin used for the INA "ALERT" functionality +const uint8_t GREEN_LED_PIN = 13; ///< Arduino standard green LED +const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed + +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +INA_Class INA; ///< INA class instantiation +volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example +volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings +volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings +volatile uint8_t readings = 0; ///< Number of measurements taken + +ISR(PCINT0_vect) { + /*! + @brief Interrupt service routine for the PCINT0_vect + @details Routine is called whenever the INA_ALERT_PIN changes value + */ + *digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK + PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group + sei(); // Enable interrupts (for I2C calls) + digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED + sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum + sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum + readings++; + INA.waitForConversion(deviceNumber); // Wait for conversion & INA int. flag + cli(); // Disable interrupts + *digitalPinToPCMSK(INA_ALERT_PIN) |= + bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group +} // of ISR handler for INT0 group of pins + +/*! + @brief Arduino method called once at startup to initialize the system + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns + @return void +*/ +void setup() { + pinMode(GREEN_LED_PIN, OUTPUT); // Make the internal LED an output pin + digitalWrite(GREEN_LED_PIN, true); // Turn on the LED + pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with internal pull-up resistor + *digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group + Serial.begin(SERIAL_SPEED); +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, wait 2 seconds for initialization + delay(2000); +#endif + Serial.print(F("\n\nBackground INA Read V1.0.5\n")); + uint8_t devicesFound = 0; + while (deviceNumber == UINT8_MAX) // Loop until we find the first device + { + devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor + for (uint8_t i = 0; i < devicesFound; i++) { + /* Change the "INA226" in the following statement to whatever device you have attached + and want to measure */ + if (strcmp(INA.getDeviceName(i), "INA226") == 0) { + deviceNumber = i; + INA.reset(deviceNumber); // Reset device to default settings + break; + } // of if-then we have found an INA226 + } // of for-next loop through all devices found + if (deviceNumber == UINT8_MAX) { + Serial.print(F("No INA found. Waiting 5s and retrying...\n")); + delay(5000); + } // of if-then no INA226 found + } // of if-then no device found + Serial.print(F("Found INA at device number ")); + Serial.println(deviceNumber); + Serial.println(); + INA.setAveraging(64, deviceNumber); // Average each reading 64 times + INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously + INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish +} // of method setup() + +void loop() { + /*! + @brief Arduino method for the main program loop + @details This is the main program for the Arduino IDE, it is called in an infinite loop. The + INA226 measurements are triggered by the interrupt handler each time a conversion is + ready and stored in variables. The main program doesn't call any INA library functions, + that is done in the interrupt handler. Each time 10 readings have been collected the + program will output the averaged values and measurements resume from that point onwards + @return void + */ + static long lastMillis = millis(); // Store the last time we printed something + if (readings >= 10) { + Serial.print(F("Averaging readings taken over ")); + Serial.print((float)(millis() - lastMillis) / 1000, 2); + Serial.print(F(" seconds.\nBus voltage: ")); + Serial.print((float)sumBusMillVolts / readings / 1000.0, 4); + Serial.print(F("V\nBus amperage: ")); + Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4); + Serial.print(F("mA\n\n")); + lastMillis = millis(); + cli(); // Disable interrupts to reset values + readings = 0; + sumBusMillVolts = 0; + sumBusMicroAmps = 0; + sei(); // Enable interrupts again + } // of if-then we've reached the required amount of readings +} // of method loop() diff --git a/lib/INA/examples/BackgroundRead_ESP32/.due.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.esp8266.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.leonardo.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.leonardo.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.m4.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.mega2560.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.mega2560.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.uno.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.uno.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.zero.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino b/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino new file mode 100644 index 000000000..30fb842f6 --- /dev/null +++ b/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino @@ -0,0 +1,176 @@ +/*! + * + * @file BackgroundRead_ESP32.ino + * + * @brief Example program for the INA Library demonstrating background reads + * + * @section BackgroundRead_ESP32_section Description + * + * Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality. + * It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and + * current information in the background while allowing the main Arduino code to continue processing + * normally until it is ready to consume the readings.\n\n + * + * This example program is designed for the ESP32/ESP8266 and will not function on other + * platforms\n\n + * + * Detailed documentation can be found on the GitHub Wiki pages at + * https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of + * different types and this program demonstrates interrupts and background processing, it will limit + * itself to using the first INA226 detected. This is easily changed in the if another device type + * or device number to test is required.\n + * + * This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place, + * this is the same setup that can be found in the Adafruit INA226 breakout board. The complex + * calibration options are done at runtime using the 2 parameters specified in the "begin()" call + * and the library has gone to great lengths to avoid the use of floating point to conserve space + * and minimize runtime. This demo program uses floating point only to convert and display the data + * conveniently. The INA226 uses 15 bits of precision, and even though the current and watt + * information is returned using 32-bit integers the precision remains the same.\n The INA226 is set + * up to measure using the maximum conversion length (and maximum accuracy) and then average those + * readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2 + * times a second. The pin-change interrupt handler is called when a reading is finished and the + * INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global + * variables. The main program will do whatever processing it has to and every 5 seconds it will + * display the current averaged readings and reset them.\n + * + * The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + * contains the information required in order to hook up the device. Unfortunately it comes as a + * VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is + * quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I + * needed in order to monitor a 24V battery system which goes above 28V while charging and which is + * above the absolute limits of the INA219). It is also significantly more accurate than the INA219, + * plus has an alert pin.\n + * + * The interrupt is set to pin 8. The tests were done on an Arduino Micro, and the Atmel 82U4 chip + * only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so pin 8 was chosen.\n + * + * @section BackgroundRead_ESP32_license GNU General Public License v3.0 + * + * This program is free software : you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version.This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + * have received a copy of the GNU General Public License along with this program(see + * https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + * . + * + * @section BackgroundRead_ESP32_author Author + * + * Written by Arnd at https://www.github.com/SV-Zanshin + * + * @section BackgroundRead_ESP32_versions Changelog + * + * Version | Date | Developer | Comments + * ------- | ---------- | ----------- | -------- + * 1.0.3 | 2020-12-02 | SV-Zanshin | Corrected call to "AlertOnConversion()" + * 1.0.2 | 2020-06-30 | SV-Zanshin | Issue #58 - clang-formatted document + * 1.0.1 | 2020-03-24 | SV-Zanshin | Issue #53 - Doxygen documentation + * 1.0.0 | 2019-02-17 | SV-Zanshin | Cloned and adapted from "BackgroundRead.ino" program + * + */ +#if !defined(ESP32) + #error Example program only functions on the ESP32 / ESP8266 platforms +#endif + +/************************************************************************************************** +** Declare all include files ** +**************************************************************************************************/ +#include // Include the INA library + +/************************************************************************************************** +** Declare program Constants, global variables and instantiate classes ** +**************************************************************************************************/ +INA_Class INA; ///< INA class instantiation +const uint8_t INA_ALERT_PIN = A0; ///< Pin-Change used for INA "ALERT" functionality +const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed +volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example +volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings +volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings +volatile uint8_t readings = 0; ///< Number of measurements taken +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; ///< Synchronization variable + +void IRAM_ATTR InterruptHandler() { + /*! + @brief Interrupt service routine for the INA pin + @details Routine is called whenever the INA_ALERT_PIN changes value + */ + portENTER_CRITICAL_ISR(&mux); + sei(); // Enable interrupts (for I2C calls) + sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum + sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum + readings++; + INA.waitForConversion(deviceNumber); // Wait for conv and reset interrupt + cli(); // Disable interrupts + portEXIT_CRITICAL_ISR(&mux); +} // of ISR for handling interrupts + +void setup() { + /*! + @brief Arduino method called once at startup to initialize the system + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns + @return void + */ + pinMode(INA_ALERT_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(INA_ALERT_PIN), InterruptHandler, FALLING); + Serial.begin(SERIAL_SPEED); + Serial.print(F("\n\nBackground INA Read V1.0.1\n")); + uint8_t devicesFound = 0; + while (deviceNumber == UINT8_MAX) // Loop until we find the first device + { + devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor + Serial.println(INA.getDeviceName(devicesFound - 1)); + for (uint8_t i = 0; i < devicesFound; i++) { + /* Change the "INA226" in the following statement to whatever device you have attached and + want to measure */ + if (strcmp(INA.getDeviceName(i), "INA219") == 0) { + deviceNumber = i; + INA.reset(deviceNumber); // Reset device to default settings + break; + } // of if-then we have found an INA226 + } // of for-next loop through all devices found + if (deviceNumber == UINT8_MAX) { + Serial.print(F("No INA found. Waiting 5s and retrying...\n")); + delay(5000); + } // of if-then no INA226 found + } // of if-then no device found + Serial.print(F("Found INA at device number ")); + Serial.println(deviceNumber); + Serial.println(); + INA.setAveraging(64, deviceNumber); // Average each reading 64 times + INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously + INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish +} // of method setup() + +void loop() { + /*! + @brief Arduino method for the main program loop + @details This is the main program for the Arduino IDE, it is called in an infinite loop. The + INA226 measurements are triggered by the interrupt handler each time a conversion is + ready and stored in variables. The main program doesn't call any INA library functions, + that is done in the interrupt handler. Each time 10 readings have been collected the + program will output the averaged values and measurements resume from that point onwards + @return void + */ + static long lastMillis = millis(); // Store the last time we printed something + if (readings >= 10) { + Serial.print(F("Averaging readings taken over ")); + Serial.print((float)(millis() - lastMillis) / 1000, 2); + Serial.print(F(" seconds.\nBus voltage: ")); + Serial.print((float)sumBusMillVolts / readings / 1000.0, 4); + Serial.print(F("V\nBus amperage: ")); + Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4); + Serial.print(F("mA\n\n")); + lastMillis = millis(); + cli(); // Disable interrupts to reset values + readings = 0; + sumBusMillVolts = 0; + sumBusMicroAmps = 0; + sei(); // Enable interrupts again + } // of if-then we've reached the required amount of readings +} // of method loop() diff --git a/lib/INA/examples/DataLogger/.due.test.skip b/lib/INA/examples/DataLogger/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.esp32.test.skip b/lib/INA/examples/DataLogger/.esp32.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.esp8266.test.skip b/lib/INA/examples/DataLogger/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.leonardo.test.skip b/lib/INA/examples/DataLogger/.leonardo.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.m4.test.skip b/lib/INA/examples/DataLogger/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.mega2560.test.skip b/lib/INA/examples/DataLogger/.mega2560.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.uno.test.skip b/lib/INA/examples/DataLogger/.uno.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.zero.test.skip b/lib/INA/examples/DataLogger/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/DataLogger.ino b/lib/INA/examples/DataLogger/DataLogger.ino new file mode 100644 index 000000000..a1aa7667a --- /dev/null +++ b/lib/INA/examples/DataLogger/DataLogger.ino @@ -0,0 +1,430 @@ +/* + Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality in + order to trigger readings in the background and using a timer on the Arduino to trigger data + averaging and storing or displaying the computed values. + + The INA226 is set up to pull the alert pin down when a measurement is ready. The program has set + the bus and shunt to the maximum conversion time of 8.244ms and then averaging to 8, so each + measurement will take about 64ms. The interrupt vector "PCINT0_vect" is called and the readings + from the INA226 are read and added to the averages. + + A timer interrupt is defined in the setup() method that triggers a call to the vector + "TIMER1_COMPA_vect" once every second. The average values collected in the "PCINT0_vect" call are + then taken and stored in memory. As the amount of RAM is limited and the absolute readings are 2 + Bytes long while the delta values to the previous measurement are usually quite small, a variable + length Huffmann coding has been implemented at a nibble (4 bit) level to provide a higher-density + method of storing data. Each array is declared at 600 Bytes (one array for voltage measurements and + one array for shunt voltage) and those 1200 Bytes total can store up to 18 minutes of per-second + data, which would otherwise occupy over 4Kb memory. The Huffmann encoding method is described in + more detail in the interrupt code below. + + This example works on Atmel-Arduinos since it uses Atmel interrupts which are different on + processors such as the ESP32. The value of ARRAY_BYTES is set at 1200, which works on Arduinos with + 2K or more of RAM, smaller processors would need to reduce this value in order to work correctly. + The example is also coded for the INA226, as a chip with an ALERT pin is required for the program + to work; additionally the hard-coded LSB values for the bus voltage and shunt voltage have been set + to those used in the INA226. + + Detailed documentation can be found on the GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki + + This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ω resistor in place, this is + the same setup that can be found in the Adafruit INA226 breakout board. The complex calibration + options are done at runtime using the 2 parameters specified in the "begin()" call and the library + has gone to great lengths to avoid the use of floating point to conserve space and minimize + runtime. This demo program uses floating point only to convert and display the data conveniently. + The INA226 uses 15 bits of precision, and even though the current and watt information is returned + using 32-bit integers the precision remains the same. + + The INA226 is set up to measure using the maximum conversion length (and maximum accuracy) and then + average those readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just + less than 2 times a second. The pin-change interrupt handler is called when a reading is finished + and the INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the + global variables. The main program will do whatever processing it has to and every 5 seconds it + will display the current averaged readings and reset them. + + The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + contains the information required in order to hook up the device. Unfortunately it comes as a VSSOP + package but it can be soldered onto a breakout board for breadboard use. The INA226 is quite + similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I needed in + order to monitor a 24V battery system which goes above 28V while charging and which is above the + absolute limits of the INA219). It is also significantly more accurate than the INA219, plus has an + alert pin. + + Interrupts on Arduinos can get a bit confusing, differentiating between external interrupts and pin + change interrupts. The external interrupts are limited and which pins are available are different + for each processor, see + https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ for + additional information. Pin Change interrupts, on the other hand, can be assigned to most pins, but + these interrupts are shared in groups of pins (call "ports") and when the interrupts are triggered + they call one of 3 possible ISRs. This program makes use of PCINT0_vect and the interrupt is set to + pin 8. The tests were done on an Arduino UNO and Arduino Micro using this pin + + Sometimes the INA devices will do a soft/hard reset on voltage spikes (despite using decoupling + capacitors) and since the "PCINT0_vect" is called only when the ALERT pin is pulled low and the + default mode of the INA226 upon reset is "off, this would result in the program never collecting + statistics. For this reason the TIMER1 is used as a watchdog timer, triggering an interrupt every + second. If no measurements are detected then the INA226 is manually reset and processing continues. + + GNU General Public License 3 + ============================ + This program is free software: you can redistribute it and/or modify it under the terms of the GNU + General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should + have received a copy of the GNU General Public License along with this program (see + https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + . + + Vers. Date Developer Comments + ====== ========== ========== ============================================================== + 1.0.1 2020-06-30 SV-Zanshin Issue #58 - clang-formatted document + 1.0.0 2018-10-13 SV-Zanshin Ready for publishing + 1.0.0 2018-10-03 SV-Zanshin Cloned and adapted example +*/ +#include // INA Library + +#include "MB85_FRAM.h" // I2C FRAM Library +/************************************************************************************************** +** Declare program Constants ** +**************************************************************************************************/ +const uint8_t INA_ALERT_PIN = 8; // Pin 8. +const uint8_t GREEN_LED_PIN = 13; // Green LED (standard location) +const uint32_t SERIAL_SPEED = 115200; // Use fast serial speed +const uint16_t ARRAY_BYTES = 1200; // Bytes in data array +/************************************************************************************************** +** Declare global variables, structures and instantiate classes ** +**************************************************************************************************/ +uint8_t deviceNumber = UINT8_MAX; // Device Number to use in example +volatile uint64_t sumBusRaw = 0; // Sum of bus raw values +volatile int64_t sumShuntRaw = 0; // Sum of shunt raw values +volatile uint8_t readings = 0; // Number of measurements taken +uint8_t chips_detected = 0; // Number of I2C FRAM chips detected +volatile uint32_t framIndex = 0; // Index to the next free position +INA_Class INA; // INA class instantiation +MB85_FRAM_Class FRAM; // FRAM Memory class instantiation + +void writeNibble(uint8_t dataArray[], const uint16_t nibblePos, const uint8_t nibbleData) { + /************************************************************************************************ + ** Method "writeNibble()" will write the LSB 4 bits of "nibbleData" to the "dataArray" nibble ** + ** offset "nibblePos", each index position is 4 bits. ** + ************************************************************************************************/ + uint8_t writeByte = *(dataArray + (nibblePos / 2)); // Read the correct byte and select + if (nibblePos & 1) { // whether the LSB or MSB is to be set + writeByte = (writeByte & 0xF0) | (nibbleData & 0xF); // Keep MSB & set the LSB to value + } else { + writeByte = (nibbleData << 4) | (writeByte & 0xF); // Keep LSB & set the MSB to value + } // of if-then-else nibblePos is odd + *(dataArray + (nibblePos / 2)) = writeByte; // Write the new value to array +} // of method "writeNibble()" +uint8_t readNibble(uint8_t dataArray[], const uint16_t nibblePos) { + /************************************************************************************************ + ** Method "readNibble()" will read the nibble addressed by "nibblePos" into the write the 4 ** + ** LSB bits of the return value. Each index position of the virtual array is 4 bits. ** + ************************************************************************************************/ + uint8_t returnVal = *(dataArray + (nibblePos / 2)); // Read the correct byte and select + if (nibblePos & 1) { // whether the LSB or MSB is to be returned + returnVal = returnVal & 0xF; // Use the 4 LSB bits + } else { + returnVal = returnVal >> 4; // Use the 4 MSB bits + } // of if-then-else nibblePos is odd + return returnVal; // Return the computed nibble +} // of method "readNibble()" +ISR(PCINT0_vect) { + /************************************************************************************************ + ** Declare interrupt service routine for the pin-change interrupt on pin 8 which is set in the ** + ** setup() method ** + ************************************************************************************************/ + static uint16_t tempsumBusRaw; // Declare as static to only init 1 + static int16_t tempsumShuntRaw; // Declare as static to only init 1 + *digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK + PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group + digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED to show we are working + sei(); // Enable interrupts for I2C calls + tempsumBusRaw = INA.getBusRaw(deviceNumber); // Read the current value into temp + tempsumShuntRaw = INA.getShuntRaw(deviceNumber); // Read the current value into temp + INA.waitForConversion(deviceNumber); // Resets interrupt flag and start + cli(); // Disable interrupts + sumBusRaw += tempsumBusRaw; // copy value while ints disabled + sumShuntRaw += tempsumShuntRaw; // copy value while ints disabled + readings++; // Increment the number of readings + *digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group +} // of ISR handler for INT0 group of pins +void writeDataToArray(uint8_t dataArray[], uint16_t &nibbleIndex, const int16_t deltaData) { + /************************************************************************************************ + ** Function "writeDataToArray()" writes 4LSB from "nibbleData" to the "nibbleIndex" nibble in ** + *"dataArray". A Huffmann-like encoding with variable length is used to write the delta values ** + ** to the appropriate array. Each array "index" element is one nibble (4 bits) and the MSB ** + ** characters of the MSB nibble denote the record type. If the MSB is "B0" then it is a ** + ** 1-nibble long value, if the 2 MSB are "B10" then it is a 2 nibble value, if "B110" then 3 ** + ** nibbles, "B1110" denotes 4 and "B1111" denotes 5 nibbles. See the table below: ** + ** ** + ** 24-Bit representation Data Bits Value Range ** + ** ===================== ============ =============== ** + ** ----------------0xxx 3 bits data -4 to 3 ** + ** ------------10xxxxxx 6 bits data -16 to 15 ** + ** --------110xxxxxxxxx 9 bits data -256 to 255 ** + ** ----1110xxxxxxxxxxxx 12 bits data -2048 to 2047 ** + ** 1111xxxxxxxxxxxxxxxx 16 bits data -32768 to 32767 ** + ************************************************************************************************/ + + if (deltaData >= -4 && deltaData <= 3) // 1N, format 0xxx + { + writeNibble(dataArray, nibbleIndex++, deltaData & B111); // Write 1N to array + } else { + if (deltaData >= -16 && deltaData <= 15) // 2N, format 10xxxxxx + { + writeNibble(dataArray, nibbleIndex++, ((deltaData >> 4) & B11) | B1000); // write MSB + writeNibble(dataArray, nibbleIndex++, deltaData); // write LSB + } else { + if (deltaData >= -256 && deltaData <= 255) // 3N, format 110xxxxxxxxx + { + writeNibble(dataArray, nibbleIndex++, ((deltaData >> 8) & 1) | B1100); // Set 3MSB 9th bit + writeNibble(dataArray, nibbleIndex++, deltaData >> 4 & B1111); // write 4 MSB bits byte 1 + writeNibble(dataArray, nibbleIndex++, deltaData); // write 4 LSB bits byte 1 + } else { + if (deltaData >= -2048 && deltaData <= 2047) // 4N, format 1110xxxxxxxxxxxx + { + writeNibble(dataArray, nibbleIndex++, B1110); // Header nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble + } else { // 5N, fmt 1111xxxxxxxxxxxxxxxx + writeNibble(dataArray, nibbleIndex++, B1111); // Header nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 12); // MSB nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble + } // if-then-else value fits in 4 or 5 nibbles + } // if-then-else value fits in 3 nibbles + } // if-then-else value fits in 2 nibbles + } // if-then-else value fits in 1 nibble +} // of method "WriteDataToArray()" +int16_t readDataFromArray(uint8_t dataArray[], uint16_t &nibbleIndex) { + /************************************************************************************************ + ** Function "readDataToArray()" returns a 2-Byte signed integer from "dataArray" starting at ** + ** "nibbleIndex" and expanding the Array's internal Huffmann-encoding values. See the descrip- ** + ** tion of writeDataToArray() for details ** + ************************************************************************************************/ + + int16_t outValue = 0; // Declare return variable + uint8_t controlBits = readNibble(dataArray, nibbleIndex++); // Read the header nibble + if (controlBits >> 3 == 0) // ----------------0xxx 3 bits data - 4 to 3 + { + outValue = controlBits & B111; // mask High Bit + if (outValue >> 2 & B1) { outValue |= 0xFFF8; } // If it is a negative number + } else { + if (controlBits >> 2 == B10) // ------------10xxxxxx 6 bits data - 16 to 15 + { + outValue = (controlBits & B11) << 4; // mask 2 High Bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB + if (outValue >> 5 & B1) { outValue |= 0xFFE0; } // If it is a negative number + } else { + if (controlBits >> 1 == B110) // --------110xxxxxxxxx 9 bits data - 256 to 255 + { + outValue = (controlBits & B1) << 8; // mask 2 High Bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB + if (outValue >> 8 & B1) { outValue |= 0xFE00; } // If it is a negative number + } else { + if (controlBits == B1110) // ----1110xxxxxxxxxxxx 12 bits data - 2048 to 2047 + { + outValue = readNibble(dataArray, nibbleIndex++) << 8; // move in 4 high bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits + if (outValue >> 11 & B1) { outValue |= 0xF000; } // If it is a negative number + } else { + if (controlBits == B1111) // 1111xxxxxxxxxxxxxxxx 16 bits data - 16384 to 16383 + { + outValue = readNibble(dataArray, nibbleIndex++) << 12; // move in 4 high bits + outValue |= readNibble(dataArray, nibbleIndex++) << 8; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits + } // if-then 5 nibbles + } // if-then-else 4 nibbles + } // if-then-else 3 nibbles + } // if-then-else 2 nibbles + } // if-then-else 1 nibble + return (outValue); +} // of method "readDataFromArray()" + +ISR(TIMER1_COMPA_vect) { + /********************************************************************************************** + ** Declare interrupt service routine for TIMER1, which is set to trigger once every second ** + **********************************************************************************************/ + static int16_t deltaBus, deltaShunt; // Difference value from last + static uint16_t arrayNibbleIndex = 0; // Array index in Nibbles + static int16_t lastBusRaw = 0; // Value from last reading + static int16_t lastShuntRaw = 0; // Value from last reading + static int16_t baseBusRaw = 0; // Base value for delta readings + static int16_t baseShuntRaw = 0; // Base value for delta readings + static uint16_t arrayReadings = 0; // Number of readings in array + static uint8_t dataArray[ARRAY_BYTES]; // Array for bus and shunt readings + if (arrayNibbleIndex == 0 && millis() < 3000) { // Skip first 3 seconds + baseBusRaw = (int16_t)(sumBusRaw / readings); // after startup to allow settings + lastBusRaw = baseBusRaw; // to settle + baseShuntRaw = (int16_t)(sumShuntRaw / readings); + lastShuntRaw = baseShuntRaw; + readings = 0; // then skip readings to let the + sumBusRaw = 0; // sensor settle down + sumShuntRaw = 0; // Reset values + return; + } // of if-then first second after startup + deltaBus = ((int16_t)(sumBusRaw / readings) - lastBusRaw); // Compute the delta bus + deltaShunt = ((int16_t)(sumShuntRaw / readings) - lastShuntRaw); // Compute the delta shunt + writeDataToArray(dataArray, arrayNibbleIndex, deltaBus); // Add bus reading to array + writeDataToArray(dataArray, arrayNibbleIndex, deltaShunt); // Add shunt reading to array + arrayReadings++; // increment the counter + lastBusRaw = sumBusRaw / readings; // Reset values + lastShuntRaw = sumShuntRaw / readings; // Reset values + readings = 0; // Reset values + sumBusRaw = 0; // Reset values + sumShuntRaw = 0; // Reset values + /***************************************************************************************************************** + ** Once the array could fill up on the next reading (2x max reading of 5 nibbles) then it is + *time to flush the ** + ** the accumulated readings. ** + *****************************************************************************************************************/ + if ((arrayNibbleIndex + 10) / 2 >= ARRAY_BYTES) // // + { // // + int16_t busValue = 0; // Contains current bus value // + int16_t shuntValue = 0; // Contains current shunt value // + uint16_t workNibbleIndex = 0; // Index into array for reading // + /*************************************************************************************************************** + ** If there is a FRAM memory board attached, then copy the array contents to it ** + ***************************************************************************************************************/ + if (chips_detected > 0) // Only execute if there is memory // + { // // + if ((framIndex + sizeof(dataArray) < + FRAM.totalBytes())) // Only write when space available // + { // // + cli(); // Enable interrupts temporarily // + Serial.print(millis() / 1000 / 60); // // + Serial.print(" "); // // + Serial.print(F("Writing ")); // // + Serial.print(sizeof(dataArray)); // // + Serial.print(" Bytes to memory @"); // // + Serial.print(framIndex); // // + Serial.print(".\n"); // // + sei(); // Disable interrupts again // + FRAM.write(framIndex, dataArray); // Write the whole array to FRAM // + framIndex += sizeof(dataArray); // set index to new location // + } // of if-then there is space in the EEPROM // // + } // of if-then we have at least one EEPROM attached to the I2C bus // // + for (uint16_t readingNo = 1; readingNo <= arrayReadings; + readingNo++) // Process every reading in array // + { // // + busValue = readDataFromArray(dataArray, workNibbleIndex); // Get next bus value from array // + baseBusRaw += busValue; // apply delta value to bus base // + shuntValue = + readDataFromArray(dataArray, workNibbleIndex); // Get shunt next value from array // + baseShuntRaw += shuntValue; // apply delta value to shunt base // + /************************************************************************************************************* + ** Insert code here to save data to static RAM or to a SD-Card or elsewhere ** + *************************************************************************************************************/ + + cli(); // Enable interrupts temporarily // + Serial.print(millis() / 1000); + Serial.print(" "); + Serial.print(readingNo); + Serial.print(" "); + Serial.print(baseBusRaw * 0.00125, 4); + Serial.print("V "); + Serial.print(0.0025 * baseShuntRaw); + Serial.println("mA"); + sei(); // Disable interrupts again // + + } // of for-next each array reading // // + arrayNibbleIndex = 0; // reset // + arrayReadings = 0; // reset // + } // of if-then the internal array is full // // +} // of ISR "TIMER1_COMPA_vect" // // + +/******************************************************************************************************************* +** Method Setup(). This is an Arduino IDE method which is called first upon initial boot or +*restart. It is only ** +** called one time and all of the variables and other initialization calls are done here prior to +*entering the ** +** main loop for data measurement. ** +*******************************************************************************************************************/ +void setup() // // +{ // // + pinMode(GREEN_LED_PIN, OUTPUT); // Define the green LED as an output// + digitalWrite(GREEN_LED_PIN, true); // Turn on the LED // + pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with pull-up resistor// + *digitalPinToPCMSK(INA_ALERT_PIN) |= + bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin // + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt // + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group // + Serial.begin(SERIAL_SPEED); // Start serial communications // +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, // + delay(2000); // wait 3 seconds for serial port // +#endif // interface to initialize // + Serial.print( + F("\n\nINA Data Logging with interrupts V1.0.3\n")); // Display program information // + uint8_t devicesFound = 0; // Number of INA2xx found on I2C // + while (deviceNumber == UINT8_MAX) // Loop until we find devices // + { // // + devicesFound = INA.begin(1, 100000); // ±1Amps maximum for 0.1Ω resistor // + for (uint8_t i = 0; i < devicesFound; i++) // the first INA226 device found // + { // Change "INA226" to "INA260" or // + // whichever INA2xx to measure // + if (strcmp(INA.getDeviceName(i), "INA226") == 0) // Set deviceNumber appropriately // + { // // + deviceNumber = i; // // + INA.reset(deviceNumber); // Reset device to default settings // + break; // // + } // of if-then we have found an INA226 // // + } // of for-next loop through all devices found // // + if (deviceNumber == UINT8_MAX) // Try again if no device found // + { // // + Serial.print(F("No INA226 found. Waiting 5s.\n")); // // + delay(5000); // // + } // of if-then no INA226 found // // + } // of if-then no device found // // + Serial.print(F("Found INA226 at device number ")); // // + Serial.println(deviceNumber); // // + Serial.println(); // // + INA.setAveraging(64, deviceNumber); // Average each reading 64 times // + INA.setAveraging(8, deviceNumber); // Average each reading 4 times // + INA.setBusConversion(82440, deviceNumber); // Maximum conversion time 8.244ms // + INA.setShuntConversion(82440, deviceNumber); // Maximum conversion time 8.244ms // + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously // + INA.AlertOnConversion(true, deviceNumber); // Make alert pin go low on finish // + chips_detected = FRAM.begin(); // return number of memories // + if (chips_detected > 0) { // // + Serial.print(F("Found ")); // // + Serial.print(chips_detected); // // + Serial.print(F(" FRAM with a total of ")); // // + uint32_t totalMemory = 0; // // + for (uint8_t i = 0; i < chips_detected; i++) { // // + totalMemory += FRAM.memSize(i); // Add memory of chip to total // + } // of for-next each memory // // + Serial.print(totalMemory / 1024); // // + Serial.println(F("KB memory.")); // // + } // if-then we have found a FRAM memory // // + cli(); // disable interrupts while setting // + TCCR1A = 0; // TCCR1A register reset // + TCCR1B = 0; // TCCR1B register reset // + TCNT1 = 0; // initialize counter // + OCR1A = 15624; // ((16*10^6) / (1*1024)) - 1 // + TCCR1B |= (1 << WGM12); // Enable CTC mode // + TCCR1B |= (1 << CS12) | (1 << CS10); // CS10 & CS12 for 1024 prescaler // + TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt // + sei(); // re-enable interrupts // +} // of method setup() // // + +/******************************************************************************************************************* +** This is the main program for the Arduino IDE, it is called in an infinite loop. The INA226 +*measurements are ** +** triggered by the interrupt handler each time a conversion is ready, and another interrupt is +*triggered every ** +** second to store the collected readings. Thus the main program is free to do other tasks. ** +*******************************************************************************************************************/ +void loop() // // +{ // // + delay(10000); +} // of method loop //----------------------------------// diff --git a/lib/INA/examples/DisplayReadings/DisplayReadings.ino b/lib/INA/examples/DisplayReadings/DisplayReadings.ino new file mode 100644 index 000000000..207060775 --- /dev/null +++ b/lib/INA/examples/DisplayReadings/DisplayReadings.ino @@ -0,0 +1,156 @@ +/*! + @file DisplayReadings.ino + + @brief Example program for the INA Library demonstrating reading an INA device and displaying + results + + @section DisplayReadings_section Description + + Program to demonstrate the INA library for the Arduino. When started, the library searches the + I2C bus for all INA2xx devices. Then the example program goes into an infinite loop and displays + the power measurements (bus voltage and current) for all devices.\n\n + + Detailed documentation can be found on the GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki \n\n This example is for a INA set up to measure a 5-Volt + load with a 0.1 Ohm resistor in place, this is the same setup that can be found in the Adafruit + INA219 breakout board. The complex calibration options are done at runtime using the 2 + parameters specified in the "begin()" call and the library has gone to great lengths to avoid the + use of floating point to conserve space and minimize runtime. This demo program uses floating + point only to convert and display the data conveniently. The INA devices have 15 bits of + precision, and even though the current and watt information is returned using 32-bit integers the + precision remains the same.\n\n + + The library supports multiple INA devices and multiple INA device types. The Atmel's EEPROM is + used to store the 96 bytes of static information per device using + https://www.arduino.cc/en/Reference/EEPROM function calls. Although up to 16 devices could + theoretically be present on the I2C bus the actual limit is determined by the available EEPROM - + ATmega328 UNO has 1024k so can support up to 10 devices but the ATmega168 only has 512 bytes + which limits it to supporting at most 5 INAs. Support has been added for the ESP32 based + Arduinos, these use the EEPROM calls differently and need specific code. + + @section DisplayReadings_license GNU General Public License v3.0 + + This program is free software : you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version.This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + have received a copy of the GNU General Public License along with this program(see + https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + . + + @section DisplayReadings_author Author + + Written by Arnd at https://www.github.com/SV-Zanshin + + @section DisplayReadings_versions Changelog + + | Version | Date | Developer | Comments | + | ------- | ---------- | -----------| ----------------------------------------------------------- | + | 1.0.8 | 2020-12-01 | SV-Zanshin | Issue #72. Allow dynamic RAM allocation instead of EEPROM | + | 1.0.7 | 2020-06-30 | SV-Zanshin | Issue #58. Changed formatting to use clang-format | + | 1.0.6 | 2020-06-29 | SV-Zanshin | Issue #57. Changed case of functions "Alert..." | + | 1.0.5 | 2020-05-03 | SV-Zanshin | Moved setting of maxAmps and shunt to constants | + | 1.0.4 | 2019-02-16 | SV-Zanshin | Reformatted and refactored for legibility and clarity | + | 1.0.3 | 2019-02-10 | SV-Zanshin | Issue #38. Made pretty-print columns line up | + | 1.0.3 | 2019-02-09 | SV-Zanshin | Issue #38. Added device number to display | + | 1.0.2 | 2018-12-29 | SV-Zanshin | Converted comments to doxygen format | + | 1.0.1 | 2018-09-22 | SV-Zanshin | Comments corrected, add INA wait loop, removed F("") calls | + | 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release | + | 1.0.0b | 2018-06-17 | SV-Zanshin | INA219 and INA226 completed, including testing | + | 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding | +*/ + +#if ARDUINO >= 100 // Arduino IDE versions before 100 need to use the older library + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include // Zanshin INA Library + +#if defined(_SAM3XA_) || defined(ARDUINO_ARCH_SAMD) + // The SAM3XA architecture needs to include this library, it is already included automatically on + // other platforms // + #include // Needed for the SAM3XA (Arduino Zero) +#endif + +/************************************************************************************************** +** Declare program constants, global variables and instantiate INA class ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED{115200}; ///< Use fast serial speed +const uint32_t SHUNT_MICRO_OHM{100000}; ///< Shunt resistance in Micro-Ohm, e.g. 100000 is 0.1 Ohm +const uint16_t MAXIMUM_AMPS{1}; ///< Max expected amps, clamped from 1A to a max of 1022A +uint8_t devicesFound{0}; ///< Number of INAs found +INA_Class INA; ///< INA class instantiation to use EEPROM +// INA_Class INA(0); ///< INA class instantiation to use EEPROM +// INA_Class INA(5); ///< INA class instantiation to use dynamic memory rather +// than EEPROM. Allocate storage for up to (n) devices + +void setup() { + /*! + * @brief Arduino method called once at startup to initialize the system + * @details This is an Arduino IDE method which is called first upon boot or restart. It is only + * called one time and then control goes to the "loop()" method, from which control + * never returns. The serial port is initialized and the INA.begin() method called to + * find all INA devices on the I2C bus and then the devices are initialized to given + * conversion and averaging rates. + * @return void + */ + Serial.begin(SERIAL_SPEED); +#ifdef __AVR_ATmega32U4__ // If a 32U4 processor, then wait 2 seconds to initialize serial port + delay(2000); +#endif + Serial.print("\n\nDisplay INA Readings V1.0.8\n"); + Serial.print(" - Searching & Initializing INA devices\n"); + /************************************************************************************************ + ** The INA.begin call initializes the device(s) found with an expected ±1 Amps maximum current ** + ** and for a 0.1Ohm resistor, and since no specific device is given as the 3rd parameter all ** + ** devices are initially set to these values. ** + ************************************************************************************************/ + devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance + while (devicesFound == 0) { + Serial.println(F("No INA device found, retrying in 10 seconds...")); + delay(10000); // Wait 10 seconds before retrying + devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance + } // while no devices detected + Serial.print(F(" - Detected ")); + Serial.print(devicesFound); + Serial.println(F(" INA devices on the I2C bus")); + INA.setBusConversion(8500); // Maximum conversion time 8.244ms + INA.setShuntConversion(8500); // Maximum conversion time 8.244ms + INA.setAveraging(128); // Average each reading n-times + INA.setMode(INA_MODE_CONTINUOUS_BOTH); // Bus/shunt measured continuously + INA.alertOnBusOverVoltage(true, 5000); // Trigger alert if over 5V on bus +} // method setup() + +void loop() { + /*! + * @brief Arduino method for the main program loop + * @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + * repeating. In order to format the output use is made of the "sprintf()" function, but in the + * Arduino implementation it has no support for floating point output, so the "dtostrf()" function + * is used to convert the floating point numbers into formatted strings. + * @return void + */ + static uint16_t loopCounter = 0; // Count the number of iterations + static char sprintfBuffer[100]; // Buffer to format output + static char busChar[8], shuntChar[10], busMAChar[10], busMWChar[10]; // Output buffers + + Serial.print(F("Nr Adr Type Bus Shunt Bus Bus\n")); + Serial.print(F("== === ====== ======== =========== =========== ===========\n")); + for (uint8_t i = 0; i < devicesFound; i++) // Loop through all devices + { + dtostrf(INA.getBusMilliVolts(i) / 1000.0, 7, 4, busChar); // Convert floating point to char + dtostrf(INA.getShuntMicroVolts(i) / 1000.0, 9, 4, shuntChar); // Convert floating point to char + dtostrf(INA.getBusMicroAmps(i) / 1000.0, 9, 4, busMAChar); // Convert floating point to char + dtostrf(INA.getBusMicroWatts(i) / 1000.0, 9, 4, busMWChar); // Convert floating point to char + sprintf(sprintfBuffer, "%2d %3d %s %sV %smV %smA %smW\n", i + 1, INA.getDeviceAddress(i), + INA.getDeviceName(i), busChar, shuntChar, busMAChar, busMWChar); + Serial.print(sprintfBuffer); + } // for-next each INA device loop + Serial.println(); + delay(10000); // Wait 10 seconds before next reading + Serial.print(F("Loop iteration ")); + Serial.print(++loopCounter); + Serial.print(F("\n\n")); +} // method loop() diff --git a/lib/INA/images/INA226.jpg b/lib/INA/images/INA226.jpg new file mode 100644 index 000000000..960916fe7 Binary files /dev/null and b/lib/INA/images/INA226.jpg differ diff --git a/lib/INA/images/horizontal.png b/lib/INA/images/horizontal.png new file mode 100644 index 000000000..c1aed7782 Binary files /dev/null and b/lib/INA/images/horizontal.png differ diff --git a/lib/INA/images/horizontal_narrow.png b/lib/INA/images/horizontal_narrow.png new file mode 100644 index 000000000..3365fdaae Binary files /dev/null and b/lib/INA/images/horizontal_narrow.png differ diff --git a/lib/INA/images/horizontal_narrow_small.png b/lib/INA/images/horizontal_narrow_small.png new file mode 100644 index 000000000..8b63bcd5a Binary files /dev/null and b/lib/INA/images/horizontal_narrow_small.png differ diff --git a/lib/INA/images/icon.png b/lib/INA/images/icon.png new file mode 100644 index 000000000..3d70a9be7 Binary files /dev/null and b/lib/INA/images/icon.png differ diff --git a/lib/INA/images/ina.ai b/lib/INA/images/ina.ai new file mode 100644 index 000000000..5da4d3de2 --- /dev/null +++ b/lib/INA/images/ina.ai @@ -0,0 +1,1188 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + ina + + + + + Adobe Illustrator CS6 (Windows) + 2018-07-11T13:50:29+04:00 + 2018-07-11T13:50:29+03:00 + 2018-07-11T13:50:29+03:00 + + + + 256 + 120 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAeAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A4PHGhRfhFaDtmtJdmAKT hPJ3mh1Dpod8yMKqwtZiCD4HjkuGXcUcce8N/wCC/Nf/AFYb/wD6RJv+aMeGXcV4494d/gvzX/1Y b/8A6RJv+aMeGXcV4494d/gvzX/1Yb//AKRJv+aMeGXcV4494d/gvzX/ANWG/wD+kSb/AJox4Zdx Xjj3h3+C/Nf/AFYb/wD6RJv+aMeGXcV4494d/gvzX/1Yb/8A6RJv+aMeGXcV4494WTeUfMsETzT6 LeRRRjk8j20qqoHckrQY8Mu4rxR7whtHRP0vY/CP96Iu3+WMETumQ2L9FM2LrHYqkH5ggHyF5lB6 foq9/wCod8EuSY8w+N/yx/KTVPP8t8LK4gs4NPWP1ppgWq8pPBQq79EY1zCx4zJzsmQR6M+/6FM1 3/q+Wf8AyJky38ue9q/MDuS/Sf8AnGmbV/rn6N8y2Nz+j7qWwveMMo9O5gp6kTVpuvIY/lz3r+YH c7Sf+caZtX+u/o3zLY3P6PupLC94Qy/u7mGnqRGtN15CtMfy571/MDuTD/oUzXf+r5Z/8iZMfy57 1/MDuefebfy61DyL5007Sb+WG5MzQXEE8QoGRpOO6ncEMhGVSgYyDbGYlEl6J/zldf31v560tLe4 lhQ6WhKxuygn6xNvQEZZqCQQ16cAgp3/AM4iXl3cv5t+sTyTcBp3H1HZqV+tVpUnwyWnN2x1IAp6 L/zkPNND+T+vSwu0cimy4uhKsK30A2IyzL9JasX1B8aLresowdb+4VlNVYSuCCO43zC4j3ufwjuf a35NfmJF548m297Kw/S9nS21WMUH75RtIB/LKvxD3qO2Z2OfEHAyQ4TTO8m1vj3/AJyR1HUIPzWv 44bqaKMW9rREkZVFYV7A5h5yeJzcEQYvpD8m5ZZfyu8tySu0kjWa8nYlidz1JzJx/SHFyfUWZZNg 8H/5yt83zafoWk+X7OZornUJmurho2KsIYBxUGnZ3ev+xyjPKhTkaeFm3zL+mNX/AOW64/5Gv/XM TiLl8I7n2v8Akf5qPmT8tNIu5X9S8tI/qN4T19S2+AFj3Lx8HPzzPxyuLgZY1Is7ybW/Pbyciv5o 0NHAZGvrUMp3BBmWozXx+oe92U/pPuffGbB1rsVYh+bPmLzJ5b8iahrfl23S71KyML/V3jeUvE0y JLwVCp5BGLd+nTFWV28yzwRzqGVZUV1VwVYBhUAqdwfbFWIWvmnzDdfm3feW4rZU8uadpMVzcXUk Th2vppjwSKUkIU9LrsdxirM8VdirTKrqVYBlYUZTuCD1BGKvgPTwBr9sBsBdpQf89BmuH1OyP0/B +h2bF1rsVSH8wP8AlA/Mn/bKvf8AqHfBLkmPMPCv+cSv95vM/wDr2X6p8o0/IuRqeYezWkXnCLzb fNcz2tx5VuLdHsUCtHd21ylFeM0BWWOQVfkSCDtSm+ZDjI7TdD0nTJr6awtkt5dSnN3fMlf3s7Kq GRqnqVQdMVdpGh6To8M8Om2yW0dzcS3dwqV+OeducsjEk7s2Ko7FXzP/AM5Jf+TQ8u/8wdv/ANRc uY2b6g5WD6Spf85bf8p5pX/bKT/qImyOo5hlpuRT7/nDz7fm/wCWm/8AY3ktP1Y6no9H/wCcjf8A yTXmD52X/UfBluX6S1YfqD4uit55VleKNnSBPVnYCoROax8m8BzkUfTmDTnks5/Jb8xX8j+c4Lud z+h76ltqsY6emx+GWm+8TfF8qjvlmKfCWvNDiD7fjkjkjWSNg8bgMjqQQQRUEEdQcznXvjb/AJyX /wDJtah/zD2v/Jlcws/1Odp/pfS35Lf+Sr8tf8wa/wDEjmVj+kOJk+os1ybB8S/n75p/xD+Z2quj 87TTSNOtt6gC3qJKfOYucwc0rk5+CNRQ35mflvN5OsPK87qwbWNNWe75V+G7Dc5Y/wDYJLGuOSHD S4snFb0f/nEnzT6Gq6x5YmeiXka31opNB6sNI5QB3LIyn5Llmnl0a9THkX05mU4j89/Jf/KV6D/z H2n/ACeTNfH6h73ZT+k+5975sHWsP/Rvn+z/ADG+vW99HqHk3U4eN5YXBCS2E0KUR7YqvxrK32lb vvXpirMMVdirsVdirsVdir4CsP8AlILf/mLT/k4M1/8AF8XZH6fg/Q7Ng612KpD+YH/KB+ZP+2Ve /wDUO+CXJMeYfPP/ADjXpM2r+XvN+nwajdaVNM9hw1CxZFnjKNK/wl1kWjceLArupIyjT8i5Gp5h 9EaRaXdnplra3l4+o3UMapNfSqkbzMBu7LGFQE+wzIcZDeY/M+g+W9O/SWu3iWFh6iQm5l5cA8h4 qCVBpU9ztiqZRyRyRrJGweNwGR1IIIIqCCOoOKrsVfM//OSX/k0PLv8AzB2//UXLmNm+oOVg+kqX /OW3/KeaV/2yk/6iJsjqOYZabkU+/wCcPPt+b/lpv/Y3ktP1Y6no9H/5yN/8k15g+dl/1HwZbl+k tWH6g8G/5xn0bTtb85a5pGoxCaxvtBuoZ4z/ACvc2oqD2Zeqnsd8x8AslyNQaAYF568n6j5P8032 g3wq9q9YZqUEsL/FHKv+sv3Go6jK5x4TTbCfELfSP/OMf5kHWvL7+VNRm5anoyA2RYktJZVCgf8A PFiF/wBUr4Zk4J2KcTPjo28i/wCcl/8AybWof8w9r/yZXKc/1N+n+l9Lfkt/5Kvy1/zBr/xI5lY/ pDiZPqKcee/Msflnydq+usRysbZ3hB6GYjhCp/1pGUZKRoWiMbNPiv8AK/y4/mv8xdG0yessVxc+ vfFv2oYazTcj/lKhHzOYOMcUnPyHhi+mv+clvK36a/Lae+iTldaJKl6hA+L0v7uYfLi/M/6uZWaN xcTBKpPlv8u/NEnlbzrpGuqxEdpcL9ZA728n7uYfTGzU98xMcqNuZkjcSH30rK6h0IZWFVYbgg9C Dmwda/PjyX/yleg/8x9p/wAnkzXx+oe92U/pPufYnnf81/LfkrUbW11621CK0uY/U/S0NpJNZRks VEcksfI+p8NeIU7U8c2DrWZg1FcVdirsVdirsVdirsVfAVh/ykFv/wAxaf8AJwZr/wCL4uyP0/B+ h2bB1rsVSH8wP+UD8yf9sq9/6h3wS5JjzD55/wCca9Im1fy75w02HULrSnuWsV+v2LKlzGAZWb03 dXClgONad9t8o0/IuRqeYfRGkad+jdLtdP8ArNxefVYli+t3cnq3EnAU5yyUHNz3PfMhxnavpGma xplzpeqWyXen3aGK5tpRVXU9j+sEbg7jFXj2manq35MatBoWuzy335Z30vp6JrclXk0uRz8NrdN/ vr+R+3yqFVeyx39jJIscdxE8jqHRFdSxUioYAHcEb1xV83f85Jf+TQ8u/wDMHb/9RcuY2b6g5WD6 Spf85bf8p5pX/bKT/qImyOo5hlpuRT7/AJw8+35v+Wm/9jeS0/Vjqej0f/nI3/yTXmD52X/UfBlu X6S1YfqDxT/nEz/yZOof9saf/qKtco0/Nv1PIPUf+ckvy2PmPywPMGnxctY0NGd1UfFLZ/akT3Mf 21/2Xc5bmhYacGThNPlzyj5n1Lyv5jsdd05qXNjIH4EkLInR43p+y6kqcxIyo25s48QplP55+YNO 8xefG1vTX52V/ZWc0RP2hWEBkanRkYFWHiMnmNm2vAKFPqj8lv8AyVflr/mDX/iRzLx/SHDyfUXn P/OWfmn6r5e0vy1C9JdRmN1dKDv6NvsgYeDSPUf6mV6iW1NunjZt5T+RXn7yh5H1zUNY16K6muJL dbaxFrFHIFDvylZi8kfE/AoFPE5ThmI827NAy2D2PUf+cn/ys1DT7mwurLVHtbyJ4J09CH4o5VKM P7/uDl/jxaPAk+UXCB2CEsgJ4sRQkdiRvmGXND7d/InzT/iL8stInkfndWKHT7rx521FQn3aLgx+ eZ2OVxdfljUi+NvJf/KV6D/zH2n/ACeTMOP1D3udP6T7n3vmwda7FXYqw/8AMz8zdH/L3SrLVNXt bm4sru6W0Z7VVYxFlZ+bhmXaiHpvirJdI1fTNY0y21TS7lLvT7tBLbXMRqrqe4/UQdwdjiqLxV2K uxV8BWH/ACkFv/zFp/ycGa/+L4uyP0/B+h2bB1rsVSH8wP8AlA/Mn/bKvf8AqHfBLkmPMPCv+cSv 95vM/wDr2X6p8o0/IuRqeYfQWZDjOxVjv5jWbXnkDzJbpaJfzPpl2YLORBIskyws0S8T1PqAU98V eO2H5d/k7bfkXD5ktnigvYdNW7i8zpKyXkeoJDUBZA/JXWYcBCD/AJNK4qxP837zU73zB5AvNWUr qlzoemTX6leBFxJIzSgr2+MnbMbN9QcrB9JTP/nLb/lPNK/7ZSf9RE2R1HMMtNyKff8AOHn2/N/y 03/sbyWn6sdT0ej/APORv/kmvMHzsv8AqPgy3L9JasP1B4p/ziZ/5MnUP+2NP/1FWuUafm36nkH1 oQCCCKg7EHMtw3xT+ev5bt5L84yG0iK6FqnK501gDxTf95BU942O3+SVzBzQoufhycQ83m9Mqbn3 T+S3/kq/LX/MGv8AxI5sMf0h1uT6i+W/z/8AM51/80NVZX5W2mEabbCtQBb1En3zFzmJmlcnMwRq LKtA/wCcV/Muq6JYao+sWtob63jufqzxyM0YlUOFYjbkAd8mNPtzYHUgHkj/APoUTzF/1MFn/wAi pf64fy/mj8z5POfzS/KnV/y9vbG3vbmO9h1CJ5IbmJWVeUbAOnxd1DKfpyrJj4W3Hl4no/8AziV5 p+r61q/lmZ6R30S3topP+7YDwkA92RwfkmW6eXRq1MeReLeS/wDlK9B/5j7T/k8mUx+oe9vn9J9z 7E87aD+ZeralbR+WfM1v5d0gRf6W/wBTW7umm5H7PqkIEK08CD49tg61mQrQV3PcjbFW8VQOuaHp Ou6Tc6Rq9sl5p14hjuLeQVVlP4gg7gjcHcYq+cYpfNP/ADjx5pEE5n1f8rdXn/dyfaktJG+4LKoG 42WVRUUYfCq+kdI1fTNY0y21TS7lLvT7tBLbXMRqrqe4/UQdwdjiqLxV2KvgKw/5SC3/AOYtP+Tg zX/xfF2R+n4P0OzYOtdiqQfmCQPIXmUnp+ir3/qHfBLkmPMPjf8ALH829U8gSX/1K2gvINQEfrRT EqQ0XLgysvs7bZhY8hi52TGJdWff9DZ67/1Y7P8A5HSZb+YPc1flx3u/6Gz13/qx2f8AyOkx/MHu X8uO93/Q2eu/9WOz/wCR0mP5g9y/lx3sOP5p+Tjrw17/AABo/wClBL6/q85Qnq1r6no19LnXflxr Xfrj+YPcv5cd6W+a/wAw9R88edbDVtQSK39NoLe3t4j8KRrJypyO5JZicqlMykG2MBGJD0r/AJyq 0fVr3zxpctnZXFzGumIrPDE8ihvrExoSoO++WZ4kkNenkADad/8AOJGl6lYv5s+u2k1r6g0/0/Wj ePlx+tV48gK0qMlpwRbHUEGqeh/85B2tzdflDr0FrC887mz4RRKXc0voCaKtTsBXLMo9JasR9QeN f84r6Pq9l+Yt/LeWNxbRHSJ0Ek0TxqWNzbECrACtAcpwRILfqJAjZ9U5lOIw382fIFv548m3elUV dQi/0jS5j+xcoDxFdqLICUb2Ne2QnDiFM8c+E2+KJfKnmiGV4pNIvVkjYo6m3l2ZTQj7OYPAe52H HHvfYXkvUbzy5+RNhftaym90/SmkjtDG5kaYBvTQxgcviYjM2O0XAlvL4vlvyR5D1/zF530rT9Qs LtYL27Vr+eWF1HpA+pOSzKFqUVvpzFhAmW7mTmBHYvusAAAAUA2AGZzr3Yq8r/5yQ8oS+YPy7kuL SFp9Q0eZLuBI1LSNGf3cyqBU/Zfmf9XKs0bi24ZVJ80/l7/ifyx510fXBpd6I7O5Q3FLeWpgf93M Ps942bMbGCDdOXkMTEi0h8l/8pXoP/Mfaf8AJ5MjH6h72U/pPufe+bB1rsVdirsVQOuaHpOu6Tc6 Rq9sl5p14hjuLeQVVlP4gg7gjcHcYq+cYpfNP/OPHmkQTmfV/wArdXn/AHcn2pLSRvuCyqBuNllU VFGHwqsxP/OTlnNFJqOm+SvMOoeXYieetRWv7nipozg7pT/Wce9MVem+SvPHlrzpoceteX7oXNo5 KSKRxlikHWOVDurD8RuKjFXxBYf8pBb/APMWn/JwZr/4vi7I/T8H6HZsHWuxV2KuxV2KuxV2KuxV 2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV+e/kv/lK9B/5j7T/k8ma+P1D3uyn9J9z73zYOtdir sVdirsVeI/8AOTPp3UnkPRtRkMXlvU9ehj1lw3BSgKqqu1RQcXc17Ur2xV7Ta2ttaW0VraxJBbQI scMMahURFFFVVGwAGwAxV4j+W9rZaP8A85HeetG0ECLQ5dPhvLy2ioIYr4tCeKqNh/fSGnbcYq+d bD/lILf/AJi0/wCTgzX/AMXxdkfp+D9Ds2DrXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq/PTyjNFB5l0WeZxHFFeWzyO2wVVlUkn5DNfH6h73ZS+k+598qyuoZSGVhVWG4IP Qg5sHWt4q7FXYq7FWOef/Imh+efLNz5f1lCbeajwzpT1IZkB4Sxk/tLU/MEg7HFXmVt5D/5yY0ux GiaZ500q40qJRDbajdwv9fSIbDYwzKWA/mkY++Ks0/LP8tNF/LjRL2WW9a+1S+Y3Wu67dfC8zrVq nkzcUXkx3YmpJJ3xV8faewbXrZgag3SEH29QZrh9Tsj9Pwfodmxda7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FX51po+r8F/0G46D/dT/ANM1xiXZiQ71Yafr4FBbXYA6DhJ/ TGpIuPk76h5g/wCWe7/4CT+mH1ea3Hyd9Q8wf8s93/wEn9MfV5rcfJ31DzB/yz3f/ASf0x9Xmtx8 nfUPMH/LPd/8BJ/TH1ea3Hyd9Q8wf8s93/wEn9MfV5rcfJ31DzB/yz3f/ASf0x9Xmtx8nNp+vMCG trog9QUkp+rBUluPkr6Po+rDVrImyuABcRVPpP8Azj2wxibWUhR3foPmwda7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXiX52/nn5m8hebbXRtLsrK5t57CK8aS6WUuHeaaMg cJIxxpCO2U5cpiW/FiEg9F/LHzVfebPIul+YL+KKG7vllaWKAMIx6czxjiHZ26IO+WQlYtqnGjTx Lzl/zk5500PzbrOjW2m6bJb6dez2sMkiTl2SKQopbjKorQb0GUTzkGnIhgBF29p/K/z/AGfnnyja 6zEFivBWHUbVSf3Vwn2gK78WFGX2PjXLoS4hbROHCaTXzhrFxovlLW9ZtkSS40ywuryGOSpRngha RQ3EqeJK70OSJoMYiy81/I385vMPn/VNTs9Vs7S2SygSaJrVZVJLPxIb1HkyrFk4m3LiEXsOXNLx H86fz81fyR5og0PRLWzu3W2We9a5ErFHkY8EHpvH+wA30jKcmXhNN+LDxC1T8k/z41bzz5iu9F1q 1tLSYWxuLJrYSLzMbASIfUeSp4sGFKdDjiy8RpcuHhFvasuaHYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXyX/zln/5MnT/APtjQf8AUVdZiajm5mm5F7l/zj9/5KDy7/qX H/UVLl+L6Q4+X6i+UfzJtbm7/NLzLbW0TTXE2rXaRRIKszGdqKo7k+GYkxci5mM1AJ9+Qn5kjyZ5 wSK9l46Fq/G3v614xtX91P8A7BjRv8kn2yWGdFGaHEH1b+Z3/ktvNn/bG1D/AKhZMy5ci4UOYeCf 84if8pF5g/5g4v8Ak6cx9P1cnU9H0/JIkaNJIwREBZ3Y0AA3JJOZTiPie0sp/wA2fzlnUyvFb6vd yytMd2is4FPAdtxFGqD3zCA45ucTwQSTyLrl15K/MTTr+6BhfS70wajH0Ij5GG5XfuELde+RgeGT OY4ovvQEEAg1B3BGZ7rnYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXy X/zln/5MnT/+2NB/1FXWYmo5uZpuRe5f84/f+Sg8u/6lx/1FS5fi+kOPl+ovnX/15T/waf8AsdzH /wAp8XJ/yfwX/wDOQv5bDyl5uOoWMXHQ9aLz2wUfDFPWs0O2wFW5J7GnbBmhRtODJYruei/l9+ZA 8zfkV5r0O+l5a1oWh30RLUrLafVZFhk9ylODfQT9rLoTuJacmPhmO5I/+cRP+Ui8wf8AMHF/ydOQ 0/Vnqej178+vNH+Hvyx1WWN+F1qKjTrU/wCVcgiT5EQhyPcZdllUWjFG5B5j/wA4j+V6nW/NMq9O Om2jbf5M0/v/AL6/HKtPHq3amXIME/5yR8r/AKE/My7uok42utRpfx03HqNVJh8zIhb/AGWV541J s08ri+j/AMjvNH+I/wAs9HunfldWcf1C78fUtvgUn3aPg305lY5XFxcsakWeZNrdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdir5L/5yz/8mTp//bGg/wCoq6zE1HNzNNyL3L/n H7/yUHl3/UuP+oqXL8X0hx8v1F86/wDryn/g0/8AY7mP/lPi5P8Ak/g+pfzI8kWfnXyhe6FcEJNI vq2M7f7quY6mJ9gTSvwtT9knMqceIU4kJcJt8PRXOu+V9U1Kz+K0vvRu9L1CBx+xPG0E0bDv1qPc A5gAmJdgQJB7R/ziJ/ykXmD/AJg4v+Tpy/T9WjU9Fv8Azlp5pFzr+leWoXBj06Fru6Ar/fXBoit7 rGlR/r46iXRdNHmWF6F5d/P3S9OSDRLXXbKwk/fJFamaKMlwDy4qQKkUyAjMcmZljPNL/OPl784Z 7Ean5utNXns9PBpdX/qyJCJWVTQuTx5NxwTjPqyhKF7PSv8AnEnzT6Gq6x5YmeiXka31opNB6sNI 5QB3LIyn5Llmnl0atTHkX05mU4jsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVfMH/OT3lLzXrH5g2N1pGiahqVsmkwRPPZ2s9xGJBc3LFC8SMoYBgae+YueJJ2crTzAG72X8jd P1DTvyr0Gy1C1msryFJxLbXEbwyoTcysOSOFYVBB3GX4xUQ0ZTci8F/wZ5x/6GD/AEp+gdS/Rn+J PrH1/wCp3H1f0frfL1fV4cOHHflWlMo4Tx25HGPDq+j60zKcR86/85LflLqWoahbebPLmnz311cc bbVbS0ieaVmUUinCRhmPwjg23ZffMfNjvcOTgyVsVn/OLXlfzPo2va5LrGj32mRy2sSxPeW01urs JCSFMqrU/LHBEi7XUSBqmHf4K86+evzlGoav5e1S10nUdS9Wea7tJ4Y0sYTVUaSVFRT6EYQe/TIc BlOyGfGIwoHd9fZluGlnmfQrbX/LupaLc09HUbeS3ZiK8TIpCuB4q1GGAixSQaNvjryF5Z/Mnyn5 70rV/wDDGsFLC7UXLR2F0ytAxMc/FhHQho2ahzDhGUZcnNnOMo832xma4LsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdirsVf/2Q== + + + + + + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + xmp.did:46EC1C38F884E81185F0AF3C69EC7F21 + uuid:0498f244-3cf1-449e-bd7f-9a050063864a + proof:pdf + + uuid:be7b52e5-b3f7-084b-afd9-c4a7b8bb0acb + xmp.did:01801174072068118083917CE3B5B58A + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + proof:pdf + + + + + saved + xmp.iid:46EC1C38F884E81185F0AF3C69EC7F21 + 2018-07-11T13:50:26+03:00 + Adobe Illustrator CS6 (Windows) + / + + + + + + Basic RGB + Document + + + 1 + False + False + + 2272.000000 + 1424.000000 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 0 + 0 + 0 + + + RGB Red + RGB + PROCESS + 255 + 0 + 0 + + + RGB Yellow + RGB + PROCESS + 255 + 255 + 0 + + + RGB Green + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blue + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + + + + Cold + 1 + + + + C=56 M=0 Y=20 K=0 + RGB + PROCESS + 101 + 200 + 208 + + + C=51 M=43 Y=0 K=0 + RGB + PROCESS + 131 + 139 + 197 + + + C=26 M=41 Y=0 K=0 + RGB + PROCESS + 186 + 155 + 201 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + + + + Adobe PDF library 10.01 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 12 0 R/TrimBox[0.0 0.0 2272.0 1424.0]/Type/Page>> endobj 8 0 obj <>stream +HWn\7 ߯,Rmܢ(},iC>f< zaYHQ/}p~wCp];~ERts?/w?A+׬KU|Rqcߗw˧%8IjUw7q 'jM''MhP\c +=~X=͝oPvkgrҬq=Q|e_MMEv:vhRj6QWNʪ۴33l_a;.l?_߼r yo&:/#KS'$ߥO'xP`h(>lNo&H]MKNU 8z`Aŷ>aHib?p}|f }VQ{5[z + | D[rnsȁ`*81cԆD;| nrAACP9d=>kGԋޙ{=i3o cie|HCLBzoH|1VM;A9[X>E4 i"yj o7 jAMIX,T"ȴ.s۔r!En@[gp\v,]i.rq)f'ȼB4hȽ.T)phFD zXQ,$Tc1BG\\M<Ǿ!ϵN; +aŴ J86CAݸi4KUX;$!ŲMDzm&MIeLg9ַP\/zsTt2̮ +d^:H!V6E$JҾ.u1 {lQ)a)hdӬt] :ކTe μ,$Q$Rlp`r6l\eBEDЊ ƚQhs" RñeMu6T_UX1X6F`Ĝ*1'P悅ed7!.D%&O) 3=[Cx 6$ÌFO֫M3(uk,;^X6Nx5ܠ#P(5:p)O /ě;ɝ7R c +ljӑUa[1΃@?,}QO2%(P!#m".BN$UH(Y +vƬ(8K̓jE6$QąUhVLjɈV5+jW5fEjiAd.(NIYH֤T;hۨJSd(94d^nPPvIaG#, +ac%A֎t2ۑaao6^G:'R_0W=ɏ/. , G ҭXCϻ䉜J؄%sWj?3nDvyU>;8YGבhNw3|+3GE˟| +endstream endobj 12 0 obj <>stream +8;Z]">EZoI#Xql)?S2OARf0aeM1A3m+]F^&kUK0#-e#=`>QGoqD;2L9gU>utGVl=L +Hs9pdAhGLipo5IDL]DrKK1ZLW;#>M=`KAI`NM??5TMT,F5Rd/eMWfZuiCN_/,W3rj +38i^q7M1IY+SC7-\>87KkcK4?C1!U+a<6Y\8Nk5q#'*#'-j%*X\gT=+Rj&CI3ct&p +(I_WHJ;mEQfbC!>jf!u*3RS5h.&iqfS2s(]0d=]d&-X%"cq#n?bI`Olm1ee4pFhDE +jq0k:PX`np"F]/"1#c4l7f$>k.]G19m%fYH77N`F,LG=?`:0iY/@@>>%I7\pFk<$I +7P*&FW?9HZ2@%mi]@;t*AEntn#K[&7_i#.??)])gl_mm\1L,p!QZq+%)i!p*ah#Zk +>3\Z5eQVg1+u@8*YNTD_W,JefcYLM%NbSeorlj%qd@gUO07RS"2rAb"egn>`;3$fH +&X+=W.rs7-.8]4BI2&Hg,$e,3ah!&N#?aP68,%&NUFc\8I@JHC:tVftafLa:KZeG+ +]9Tde.d3CrQY-AP:kgSf"8P$c=ci"9\=O%@I2+''#R(?S-!L^#4Y74;!g5[L#GH`o +DcV$ +endstream endobj 13 0 obj [/Indexed/DeviceRGB 255 14 0 R] endobj 14 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 5 0 obj <> endobj 15 0 obj [/View/Design] endobj 16 0 obj <>>> endobj 11 0 obj <> endobj 10 0 obj [/ICCBased 17 0 R] endobj 17 0 obj <>stream +HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  + 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 +V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= +x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- +ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 +N')].uJr + wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 +n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! +zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km +endstream endobj 9 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 16.0 +%%AI8_CreatorVersion: 16.0.0 +%%For: (Yakamoz) () +%%Title: (Untitled-1) +%%CreationDate: 7/11/2018 1:50 PM +%%Canvassize: 16383 +%%BoundingBox: -303 -683 1419 117 +%%HiResBoundingBox: -302.249 -682.5898 1418.4443 116.5898 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 12.0 +%AI12_BuildNumber: 682 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%RGBProcessColor: 0 0 0 ([Registration]) +%AI3_Cropmarks: -578 -995 1694 429 +%AI3_TemplateBox: 421.5 -298.5 421.5 -298.5 +%AI3_TileBox: 137.04 -580.6602 978.96 14.6602 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 1 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -4546 1965 0.125 1280 575 18 0 0 32 120 0 0 0 1 1 0 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:21 -598 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 20 0 obj <>stream +%%BoundingBox: -303 -683 1419 117 +%%HiResBoundingBox: -302.249 -682.5898 1418.4443 116.5898 +%AI7_Thumbnail: 128 60 8 +%%BeginData: 9128 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45272627204B2627204B2627204B2627204B2627204B2627204B2627 +%204B2627204B2627204B26264BFD0BFF7D262627204B2026204B2026204B +%2026204B2026204B26264BFFA14B4B514BFD05FF4B514B5151FD09FF5251 +%277DFD09FFA84B514B5127A8FD07FF204A2026204A2026204A2026204A20 +%26204A2026204A2026204A2026204A2026204A2026204A2051FD0BFF7D20 +%4A20262026202620262026202620262026202620262051FFA120262027A8 +%FFFFFFA826202620207CFD08FF5120204BFD09FF76202026202051FD07FF +%26204B262620262026202620262026202620262026202620262026202620 +%2620262026204A20264BFD0BFF7D26207DA8A87DA8A8A8A1A8A8A87DA8A8 +%A87DA8A8A84B264BFF7DFD0426FD05FF2626204B204BA8FD07FF4B262076 +%FD09FF51204A204B204BA8FD06FF202620264B7651765276517652765176 +%527651765276517652765176527651765276525120262051FD0BFF7D204B +%A8FD05FF537DFFFF527DFD07FF7DF851FF7DF8262026A8FFFFFFA8272026 +%20262052FD07FF51202651FD08FFA820262026202620A7FD06FF27264A52 +%FD1FFFA84B204A4BFD0BFF7D2027FD06FF7D52FFFF7D7DFD07FF7D264BFF +%7D26264A26FD05FF264A204B264B20A8FD06FF4B4A2076FD08FF7626264B +%204B262652FD06FF20262076FFFFA8FFFFFFA8FFFFFFA87DA8FFA8FFFF7D +%52FFFFFFA8FFFFFFA8FFFFFFA8FF27262051FD0BFFA1204BA8FFA8277DFF +%FFFFA8FFA8FFA8FFA8FFA8FFFF7DF851FF7D204A2027A8FFFFFFA84B2026 +%204A202027FD06FF51202651FD08FF4B20264B52204A204BA8FD05FF4B26 +%2652FD0AFFA8F8A8FD04FF2752FD0CFFA85120264BFD0BFF7D264BFFFFFF +%7D7DFD0EFF7D204BFF7D26204A26FD05FF264A2626204B26267DFD05FF4B +%262076FD07FFA8204A20A1A826204B20A8FD05FF20262076AFFFA8FFA8FF +%A8FFA8FFA8527DFFA8FFA8527DFFA8FFA8FFA8FFA8FFA8FFA8FF26262051 +%FD0BFF7DF84BA8FFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFFF7DF851FFA1 +%20262027A8FFFFFFA8262026272720262026A8FD04FF5120204BFD07FF52 +%202026A8FF2026202052FD05FF4B262652FD05FF7DFD19FFA85120264BFD +%0BFF7D264BFF7DFD11FF7D204BFF7D26204B26FD05FF264B20A17626264B +%207CFD04FF4B262076FD07FF5120264BFFFF76204B204BFD05FF204A2076 +%FFFFA8FFA82752FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%FFFF26262051FD0BFF7DF84BA85252FFA8FFA8FFFFFFA8FFA8FFA8FFA8FF +%FF7D2051FFA12026204BA8FFFFFFA82720207DFF2026202620A8FFFFFF51 +%20204BFD06FFA82026207DFFFF7D20202620A1FD04FF26202652FD05FFA8 +%52A8FD18FF4B20264BFD0BFF7D2027FD0BFFA8FD07FF7D204BFF7DFD0426 +%FD05FF2626207DFF7D204B26264BFFFFFF4B262076FD06FF7C262026A8FF +%FFFF2626202652FD04FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7D204BA8FFFFFF +%A8FFA8FF7D27A8FFA8FFA8FFA8FFFF7DF851FF7DF8262026A8FFFFFFA827 +%202076FFFF4B202620207DFFFF51202651FD05FFAF4B202626FD04FF5220 +%26204BA8FFFFFF27262652FD20FF4B204A4BFD0BFF7D2027FF7D7DFD06FF +%5227A8FD07FF7D264BFF7D26264A26FD05FF264A207DFFFFA826264B204B +%FFFF4B4A2076FD05FFCA204B207DFD04FF7D26204B20A8FFFFFF20262076 +%FD04FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FF27262051FD0BFFA1204BA8527DFFA8FFA8FFA8FF27F8A8FFA8FFA8FFFF +%7DF851FF7D204A2027A8FFFFFFA84B202076FFFFFF51262026207CFF5120 +%2651FD05FF76202026A8FD05FF2626202651FFFFFF4B262652FFA87DA8FD +%1BFFA85120264BFD0BFF7D264BFD0BFF5227A8FD05FF7D204BFF7D26204A +%26FD05FF264A207DFD04FF204A264A20A851262076FD05FF51204B267D7D +%A17DA17D51204A204BCAFFFF20262076FFA8F827A8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7DF84BA8FF +%A8FFA8FFA8FFA8FFA8FF27F8A8A87DFFFF7DF851FFA120262027A8FFFFFF +%A826202076FD04FF7DF8262020275120204BFD04FFA8202620262020F8FD +%042026202620A1FFFF4B262652FFFFFFA8FD1BFFA85120264BFD0BFF7D26 +%4BFD0DFF52F8275227FF7D204BFF7D26204B26FD05FF264B207DFD05FF51 +%204B264B264B2076FD04FF7C26204B264A204B264A204B264B202652FFFF +%204A2076FD04FFA8FFA8FFA8FFA8FFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFFFFF26262051FD0BFF7DF84BA8FFA8FFA8FFA8FFA8FFA8FFFF7D27 +%FFFF5252A72051FFA12026204BA8FFFFFFA827202076FD05FF7D26202620 +%4A20204BFD04FF512026202620262026202620262026204BA8FF26202652 +%FD0EFF7D52FD10FF4B20264BFD0BFF7D2027FD0DFF527DFFFF7D7D7D204B +%FF7DFD0426FD05FF2626207DFD06FF5126264A204B2076FFFFFFA8204B20 +%52FD08A87626264A20A8FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8FF27 +%F852FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7D204BA8FFA8 +%FFA8FFA8FFA8FFA8FFFF7DF8A8A8277DA1F851FF7DF8262026A8FFFFFFA8 +%27202076FD06FFA820262026202651FFFFFF762020207DFD09FF20262020 +%4BFF27262652FFFFFFA8FD0BFF27F852FD0EFF4B204A4BFD0BFF7D2027FD +%0EFF7D27527DFF7D264BFF7D26264A26FD05FF264A207DFD07FF7D204B20 +%4B2076FFFFFF51204B27FD0AFF76204B204BFF20262076FF7DF827A8FFA8 +%FFA8FFA8FFA8FFFFFF27F852FFFFFFA8FFA8FFA8FFA8FFA8FF27262051FD +%0BFFA1202676A87DA87DA87DA87DA87DA87DA8A8A87DA87D4B2051FF7D20 +%4A2027A8FFFFFFA84B202076FD07FFAF4B2026202651FFFFA820262076FD +%0AFF7D26202620A74B262652FFA87DA8FD0DFF27F852FD0BFFA85120264B +%FD0BFF7D262026202620262026202620262026204A2026202620264BFF7D +%20202620FD05FF2026207DFD08FF7D2620262052FFFF762020207DFD0BFF +%202620204B20262076A9FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FF27F852 +%FFFFFFA8FFA8FFA8FFA8FF26262051FD0BFF7D2027202620272026202720 +%26202720262027202620272051FFA84B524B52A8FFFFFFA8524B517DFD09 +%FF5252515176FFFF7D4B5251FD0CFF7D4B5251524B262652FD13FF27F852 +%FD09FFA85120264BFD58FF204A2076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFFFFF27F852FD09FF26262051FD58FF26202652FD15FF27F8527D27 +%2727A8FFFFFF4B20264BFD58FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFA8FFA8FFFFFF27F8F8525227F87DFFFF26262051FD58FF27262652 +%FD16FF7DF8A8FFFFFF52F8FFFF4B204A4BFD58FF20262076FFFFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFFFFFF852FFFFA8FFA8F852FF27262051 +%FD58FF4B262652FD16FF2752FD05FF2752FF5120264BFD58FF20262076AF +%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF2727FFFFA8FFA8F87D +%FF26262051FD58FF4B262652FD16FFA8F87DFFFFA827F8FFFF5120264BFD +%58FF204A2076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFF7D +%F82727F8F8A8FFFF26262051FD58FF26202652FD18FFA852527DFD04FF4B +%20264BFD58FF20262051A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA9FFFFFFA8FFA8A820262051FD58FF27264B264B27514B4B2751 +%4B4B27514B4B27514B4B27514B4B27514B4B27514B4B2751264A204A4BFD +%58FF2026204A202620262026202620262026202620262026202620262026 +%202620262026202620262051FD58FF262026202620262026202620262026 +%2026202620262026202620262026202620262026202620204BFD58FF2751 +%4B5127514B5127514B5127514B5127514B5127514B5127514B5127514B51 +%27514B5127512776FDD9FFA87D7D7DFFFFFFA87D7D7DA8FD06FFA87CA1A8 +%FD07FF7D7D7DA1FD5FFF7620204BFFFFFF7DF8262051FD06FF762020A8FD +%06FF7D202026F8A8FD5EFF7DF82626FFFFFF7D202026207DFD05FF7DF826 +%7DFD06FF522026202051FD5EFF7C26204BFFFFFFA1204B264A26FD05FF7D +%2020A8FD06FF264B204B204BFD5EFF7DF82627FFFFFF7D20204A202052FD +%04FF7D2026A1FD05FF7D202051262620A8FD5DFF7D202051FFFFFFA1204B +%204B2626A8FFFFFF7C2620A8FD05FF76204BA851202652FD5DFF7D202626 +%FFFFFF7D2020522026204BFFFFFF7DF826A1FD04FFCA20204BFF51202027 +%A8FD5CFF7D262051FFFFFFA720267D7D204B20A7FFFF7C2626A8FD04FF7D +%26207DFFA8204B20A8FD5CFF7D202626FFFFFF7D2620A1A827202620FFFF +%7DF826A7FD04FF522026A8FFA84B202051FD5CFF7C26204BFFFFFFA12026 +%7DFF7C26262652FF7D2020A8FD04FF26264BFFFFFF5226204BA8FD5BFF7D +%F82626FFFFFF7D20F87DFFFF26262020A87DF8267DFFFFFF7D20207DFFFF +%FFA8202620A7FD5BFF7C26204BFFFFFFA120267DFFFFA8204B207C7D2620 +%A8FFFFFF76204A52A87DA77D4B262652FD5BFF7DF82627FFFFFF7D2020A1 +%FFFFFF522026204B2026A1FFFFCF2026FD07204A204BA8FD5AFF7D202051 +%FFFFFFA120267DFFFFFFA84B204A204B20A8FFFF7D262026202620262026 +%204B20A8FD5AFF7D202626FFFFFF7D20207DFD04FF522020262026A1FFFF +%52202676A87DA77DA87D27202051FD5AFF7D262051FFFFFFA720267DFD05 +%FF4B26204B26A8FFFF262627FD07FF5226264BA8FD59FF7D202626FFFFFF +%7D26207DFD05FFA820262026A7FF7D26207CFD07FFA7202620A1FD59FF76 +%20204BFFFFFF7DF8207DFD06FF76202020A8FF51F820A8FD08FF27F8204B +%FD59FFA8527D7DFFFFFFA87C52A8FD06FFA87D767DA8FF767C76FD09FF7D +%7C527DFD57FFFF +%%EndData + +endstream endobj 21 0 obj <>stream +%AI12_CompressedDatax^q%_ޡ`3S`"Gk,HcРٴ1lw7?)ÛW&^|?OMYn~ nRF7ox7|Vr>MiޤqW~z\u|8aZT{pw/|嫯~xvoo~77՛7͋l}+{/_|Imq?/7_+k3s^oٕ3i_n&_ܟŤ7Aֆq?k}|6g/zcΆ9ݶKl/S[v߷|35#Gׯg7WFw?Z]Y_կy߼}[j_;l~/wo9]#o_?䯾zW>gU{6ekoqw_~}'RKS _?bC߿xiװqA{n[;_gqIWo}ux߾xw/߼x ~š!N5O1On;8;Ň2-_듇\׿޽yg;>݇W[=o~:ut:?ux߼?~FrwE/ꟺWf}b=)oֿ_/?c.{/AzW0n~O/G><>|^m17֢[rfK{nſ2s[?vEmOg_{͞|瞟`{ggcYO?zV샱xxs"so;LX ?t~vXϡ_ +?6 +d{xn'ʟۣ=qv_t==~wwnZ^:uZv6uxinц3{{s  j%ۀcn m ջ +*6 Է]Ͻ*^!4[yXg#>10l=G fIn[}4nڼJڴ9YY01;=n97#v|>M'Yui}vo}цu^ֻ޾^~hc$و(62Z4Ǔm>ue5튚1_K2-ӈwd4jw}_rt7n619M ۨ.6g6'Gr1|fn!nGky8|wzcѦ G,kBmeZṍ6[=m?6xMm|*W#{ǟɝ5߃VhW}f~4OIbmdQIyܹi_ZV-ZQNZh%=[u.W^{>=zNYgYrx3c#x[arOu_=qp߯vcYf}u̳sO'zJ􉞪_}SOS解OSV_Eol ҌSJqK׎~۽߮3U]?e+m+~!vq0֠40ʎlycj0ƓYuG#ړGE{` +>{Dz$=[tOghОh"`?l-aGZӹc433~GgK|;EhpֵF*32;SPJ3{GVW ++ucC,wXwZj)?}C_v8{Fvm[\ +/,ʧ=o;un'Ss w;N97pI22 ~oW<\5Vd\?”vvm }^Gn<|qtξm+K y45Q?j|45ߎd%8ǺֆO?tO4%+#`Qܭ +cL}00.S" ? V[ s\33NC t3 \PWlJlu ]E|nۃqw} 0TJJ1U$\J'-g eQZHc+=NCG蟿w;~g;(g8;6=W&SI;hRٔ +P'~~>*0.HK؇{.fw~{gD~]?NĽ;(?$g$gwkwP|]??$$x89iؘ8uŨ!%dcf8ynm֟6M-S݆=V! ?tS9wH<<<3~{̍t={3|3s(CO׫(NFų61ØaL0&~瘸y[Ödosc*Uh~/Q^rkm79[XE{_7%avŏy֯p@*.|%//`yƧK?{ x7?oݻ77?7#b7YoUJ߶[s7BRbN!U0lP`012~Y!<ͦM& :tߦ  &DoJA.7/y^ii݆־mMstž$O 3yi{AoFͮ3{!DmvcLj͞)MF^>ϧmtuMJ`>%(x^mJ`go[u߭d 4{:nfhw|g1–oMŻuO:)Icܔdpey [:.IӵMy:-a6=E}iz&~^MLXV Rm7#mHrK۸5ERo`^nS6G)ize=y#~Kpy! 4/팔n7dqyW3Ik?5v)NojӅӌazAt^$ J&_`K=6#e h{f3O;ڑݭB<=6}>y(Úl:ҡs{>&wnn.zI]#(Sa+ֆVj>7]gRS EaA3Q>.W^l4-8Nyݺ^.* ?6f,KPhj z)Ѧ@k6*B*3CbZ9VٖD .⌁_ k ^{ H{6UX!fF4AΛX&UCe`DžVv=7怶2:SjL39wShsV;V{:uК& C+kTF@?Y!Dn29c:P0VkR@k>@sltk^n].hlwn^͉h!6AKh"A7$]=Ē#"C;$3J`왔7KD~grz`)CS|dy&-_lu^ja P#:0u]x(d9P6v5CFɀR!`EaC9vwٔb9Pq0W +s Ҍރ`e2"{uz}1mF͏,HLFʞ 钂G +Z K ^*3{ b昙3ylESkpl}͋`PgXZ]5+Yzë̈=/vV[$nZ +OraS>}=5<ȸR*v_J +# igp)7n6ecps i<)~zb $!p0/X>t*W5Mx8v8C1$,\=AZp3JZs-Eβi0|`n + +NaCKWN+c~/#N ^IZ4M`q2 ײ7)0\ϛN6s1qqBai. nB{e lK:q}U0=X3̉I)oi4z4sOyb(THŋ$Uu&-Dk^.rA`1=v+⃘:6R/Fs7 ŊKM[L7qr&/S75f +*% K3@ l+;ju))G L +{r}gnb -R»èk=CNA@EŒ/] +Q94 ZZav('hDc@ڀq#zg;D;C&>ltˈKΎ i\Af $A+ vɬȰR<]6-3[7A!?cyFB&hXgy;H`P;4FKKk0mdyt<}orL +@t;g0W:MfQSgDG c G )mf,)|P< 6Y{@ cBijJ@{@ +k>g~, 0F4MnY7{0a#Ʃ1 +aYqp4,u!U];B@5fM)hle,:W*z08 By X7frNz OƳ<&6+X0knًsؗ>O̓O% ~G:oieP`^^9<:`5B^Va"{~_ZVW8YQ`Ӵ78C adp^ Fy6+* +9$r!e:1$e!9Z$m ӃUBG2 52U*ր'e1TdAQw81BJi9h;X&,2Z"7Ll-AEdy-/!|h{mOu)Vސ5S"gCm\9)QP TVpCpİ-)~JtOG,ω(ԴQ21R V-i26H&0m +=!XLfwgTNUcN\pڶzĭ2+5500bg_n5Sc1q%k*OqfD,Qzhp,@~Q .+%>Cӑ},0: K!:#BY4BN_.[>-Z\) ]2|M^lvRolͅ`q$sl;2z/3E_.)!LlJ0Ls`')C f0,Pnp%Pk,źݡ!MGNi-e@@X@ 7e#H~U,E"t=1in (])6q挆tq^ <"l)i+Y+ঔqqLE RnPd(_W +`#su883\1D<՟Z&޷3;C6RªD.S Rn09W&3E!u^+92Nu sQS6sػ9sE0+a3<5%iSxCͭЂ~aIs.y-C(HmrsD.u `v$2ƌswI[L l.6C w +.F3mZ sG8& M3!ETgy86:Hi6]Of M2d[!u!. 7Co"p +tv\\~ O_ @e!kИ%=G+]*MN$ Dw-C_n_[($ҒtM9mŐv @}y^gxL(]|Оt@`m#5ۇ: TmMOdÄzf(]䓉-F +^JI@0}5J!X1 P2Ty`atY(Oc~ +`SMRx}W;M +JQ:qk+QM^;gHA`}~zŻCG`l@ 09 ~!KHBaI )ik+ ;4bUʏ}Sd'2OYAW#32K[HGfTO>8Y뷿˗|wM3Ҝ +#yYŗ!FbZ])%lbxvA#bJvQl^ڑ$6yWPYN! 0qC9]ngvU}=ir9Q4E6u^aSE%@ 7kq@\"l37-C }z$U@B}=o ; +X4gx%߹b4ۆRvK6//[X1)Pݵ17j{ 47apZbhe1$wHʢq{X.0q|sVq#iYs+Σ5Bxtd!n9~nOo6Y/)H0້ 1:vnU\^fc TQL9N@~0ِ5v@$u`w%݌1zĊJ +gJ,.VAITXC f0Vq"ˇ<`[O9 CEiLBhAp$ +g I6ܨ2K zwmx$¬!T75/]Q|)'ǯX.kY]*QЬE"#А+Sxe[V(,[B~#EMW @rX(m֏Q.X%k֡d,D4XKgiټ.WJ^PV@qCP<.Q?H~ZHJJMqG9n'@ ,F-%*{wbvZQy#z,3ep|'4<PX.O;a+IJd6)e'c*.282jMoUGn矗]9 5%_u(*/<y*58tt6G;eqi| Kի4&o9ʗIRFtNl쉢ӳӎjՙ>8٢Bu)7xzM{(`JXpFdhBCPQ3~^VR XKx^Ȉ(1%o~ӛ[1w4޹+1foBМ9qK~eg>M ʙ~(cõ;/c`YB&ᡓ;68ƙ'$` WAp܅wODX# +G7 0}Od@Z1 +F7DIaTg)I|+ : v/<5h@9EEuW[9F!Es|*R!g9f"a i31"!LD<08hB1Oȍ)6eȺƸ[fㆄտ,߂H'μj]ƻ/V}8i _8kB1i/>+.<Sg@ .#sGB\,鬋_+Twa.AÉirԠ-KEaBW`,)Ǩ.|br]1[19:ubxS7"* Q~%"FH!il9F3M(;6?NQR ĸYKә!`)fe3T5930*K{j- ˀ83g9XaV  F38}' b r30 aZHp}= ` V Eگ nFVBP xc.Dð{`n0t S'N3ߣ3.,M41Ln0 kS*J. - 8!GP Afad@N, xLy +WߗB+LV0PN#J`( 0H,Qt( PN30psE(L0Yty)/؄%sJ2 DHcJY<¥U.4eqs"Xv 0,#tAxH( ^R@K[k}`[?7S`oFwa څWdW `bXaYӕ/Y/`IxYxUEU0*` +^t FU ITt +LY0Eu3?pENb!w!d 4A ѡ= Z<7ʸRcڹ?m^S"@Vfh+30(%A6n/0=f+fD~9``h +&5ٹ 1Xteޜ/()(5VA TJCm6F.tXpo +6`01X**}/f7 wX"YF[LAm +Ya67XBt+$;0p,cEV!{ XM=clA[= TGnC$#Rޙ 4PB1:8}B 8LKcBfOĿBN۰tԹxyXz1`b +,sMœˋ1_JMu\x)&<{!QV&x $( vq/h)\hS o3)CC0W_2 d$_ IɶÃX Nhf."` Ur(,/# _f6_v*ĜmJj0wfeV'%,DN$b/I E콰̂Kj /Oli)!1h!aƕ|>(˾/iӼr_9k/`(g JBFi3lQp뜿<b-ř vp2:ū(mw=gr$C"-gE4Ryp͢ߑdѹ~#wrE{/s7M@9D|o|]8<}䊢LgE$5gwXg_Ȋyu_Ҟn6gl _9"cQ4g_5e/[Qkec7_{Y 1~)pfW)ïr43/_fqpaS _a!_S ܟqq_ bw_#0{q4wM*8~)tg_7 r_,X+.$s䗇0I~SP]x~!e^#x~)Dqe(`+{R-4/v_m0EV:B ),AK+DZS// +L'/_nb_U Y]f_E􋍈/PŹy_b-Guew3/J4j<_AijĒp['A_HR@?\x~!+x~!y~)a|Nkj2h~Ӌ_j|3bKt-AU4h 1A\h~wb1h~SmD4, +$h5B8/wRΜi~)4c?Pϯ%r[<лR;ϯܯT/9- +_(+/':)>{=[B91cXg_J02Cys1:{}%؝׉{p;~e8ۖuܾd;|u^ٲJL۽z$gV9=Hh YQ&qy k`G)E"o ޽` B8?b+`a`nitW]̺'F?h;t^]}c7wqV.ʁ@$}ԅԅ]f]̨ ;Nfw~], T(?`Y.#`EI-2QpNߙ)tW&ޙJHXIj8A 5|gjy{h2! ݈pCŋcCw:bPN _t Fl  wQg]t)Dj<.Y.3.}FB7Ůg +b{TN ]V, z%c.Ρ,`w]kr@N^ #Gfѝv-)t-Au?9Ns->1saefU@kwWQ97.\E|:v1G̞K U +]v5€ˀa`GG# + RP.hŝK .ܹ<$s0';w}p2LU);*2^ipBΥ,NS%,A?w `1(b=sP.Bs. b/̹r\8s.fׅ9xr~7T8>݅9)ݩs+ԹT#_s騢9g0*Y9`U=ŝKV58w.t;W)_s@*s)(ݪg"ڕ99=5xl̹8y) TC2(q)'&]O^ eQ2[ĹȽp\s!e:O:Xhs.D<]s5s!`,3s)ra%RUZxR#Pp\j3sn".Ŝrv .fsEbs7Y ;8څ9RΙsSi^̹û0BJ]003` v3t1qC칸\tUKcf^xa%!^S<;_W\[BO-\J P$ \" \Hc;si2'̹Y ̹őĜ9+-mՑh/ҿ]s!r\R]̹E\|EuZ ;Z;8Z|3g-&wNA-=zgv8Ym+Y 2v,Z.Z Gm`esWZ?i{4x,vZAm);AqܴXY!ys"@v ޲XANȾGM~ }dmoTL 9- +BMTUAMhL VӢDl6/@BAMDHf)eq,t0E. JH=eYە^L3E. nn\,,j>X/A heL VYްJ*$3Ke){\L);"録CiSt ^ +B& > ɞy0 "Y0l{:xdI5A#1ra{MXEqpȚ@\| + +Yx0 %8BHL{dؙCk${r\_ t>B' =$y0bJ/ƫCtX}a&=<5҅;wl w,܃ͩcWu"ZS- +1m5 }c( w捭.|8% 8E!hŸK[agDς4,ا^3qr.tM;,8F"L% BVgb=$GCUk>N4R7mC#?=3i <  0JA8XՕ}qp#{Id¢I<^sG<Gy)$d`:{!AR톳+J;;2BG]v+/hF +OtQ½kіM6M,pJ/1A& +n6}_6` $$GѺڿ#<(0kk0g_ QT 7` OlyB iokLp֓ua3yqwvY L1[*Ǹbn8wwYqQN|Wj܃ڑ`B +QH#3Y ^W-njg|^ hŚ(,):kZMMU$&y(`]et 1a8jzpTz2-/< 3^ Ʊ1Lו {voZ u.|0^CE1wibn%! ",NK,G=+|ߓ񀒤2U=gY8 +Ђ3E*v|3`!fJZQ +w1wQFJGryZ,MJDS&LBMypA*[2vJ\Kp9H3Ss,d}p{?HzL # -xF!bd= ˧ idiv<| +Hsn:%dW$03eY4߀sxST h%g5{g )Cc(5/)[_9!b`=S]/bi+Ѡ*l$TPLHX"k>.׋Sx]1W}rG(q-@lN+`e_Qz7J!<˾ )K?`9v@gI2k'f)7-SDa@ Xuhηz -[Z, P\}Zl r[\d,`]֊oS},slAfnMSg?<jx: -Bp;JqeZvKy(M >T[}ِgNk~=T~`ou+Z{@3nCb`mFd@[dzS79 ͐[LŜ_z{,θx rC.iAX@[t KB)S C75ST?0cn{JwY|2.؜Vz +eV 01 %bE.=6=۸ ז0mFID p,G 8u Mv=M:Ep7Gn8 9.zƍCJ{p|?5,FBh7yu׉b|TVu|ᦁ +[^J[7F6U؟ym)k<%a Cu&  +L`>bI2%AyV jr3%>;tvV, isFn . i.},.aBW ul#nMB0<*ݹQwS= 8h?TMnEHu2 8M% $2*)SYdS uy-dpC(}8*S-Uda |"3dms6`Yn üLG)CZn`SYUA0 0)F6U&R ќb ^ޔcq$.ks?&ש H##ぐ\ߋ_;@WA I)T4&BZb@"K$Ϝ B7+∃xsC8=uCInD?F^!߂l+]=JDܭamWAWa3 4^bW54_7݃S +Wf4r?t=7ɨN +? N S:|su: +`d$%"rjoI=Ϋ Qp"j첃7"8(Č7Rp=!Q($dp[Lz1f|o&ƴV> +"JwF +Y+!aCEH oHX؟YQq&[2R$DtC[(Y2S\P:#SUm PJN*$dh,Z7'J/ਙKaЮgy?rlt]6x:A}GvI>2űDa3DJ)Řqִ }GAc=☮HUS2;f* &D}@d&+6r͉xbq H43X{Xx׆+ENxNI=>ܻ;itcJ +ɼ#X~'MȄvZGou9mܠTx54 +ĄPL$Щ(VBW]ʑN^ +-[F[V cO($$Uƃo1nM 3pPuW  S}ǩ } v t7|S8mL ++D +^RQ+J(X] btKt!y6IDG2& Jz,M=6\}=įhQ7΁tǁ3J ݒ:u.=8,tL.uvqc5J=QŸpЧbn|4{aTLSZi,tHx0sN;)4|fDw?N(,({8Ueij&&nAaFP* m&@EEh^VD> B^)(8֨6{fd:Ni_00IU UE!eH +1n$nSQyvMoĴn6 NklyLt]T^a:HTT9˲E|M4g6ɋ6{.b?]?JPp=,.C̜ +ּ`i=v8@Z kSv$=wehF ^]$=JUB[{I\8q UD/j-pfdtb_g&BDc"t!1ؔbkx:B;L93.XyXDӁ^oɟJu"{]U!BBdKu簮 !?37#/`qqxGQyqK Db0':f$[e6+%[O%Y6xrIx+JB^U }G:E`v֦KbL1 %G'9ڊ=U-Z=Jb5+U+b1H1 +xq*(Nͳ*nZP+hqGz`;nYDe+{@z*ä5!LqX"ݨXjlyńYfJB:ctlq 8}ZvB)Eb*M ks7]O Pte~C܏St'h(ŗ`"Y{] gu!aѪXh~i*J/T+ZymYmX =&(fC8u8!* 5b򹐒.z+%)I66=~M4Yn}~^RryCx $Ŧ!޽p$G>`8 +GQS%x+@@MQ 2 9UIiգ5t9tWnr$_;ؓV/qB1D2]P⦌ǩ{PCϪ3oʜTr"c@ $x]@@#qő{Udp?3XZS^H fχҴ7߲/D,nAh8L,Yc0cMAP>9V0`!Bwv v/KQJs⮩)xiO  +# %J|8RKZFYe2;8耪d19cX\@𭫹M)"SEy (b^=X_F`SAqBl{E TN L` +pTVӂ-M/sorbfy$( /Ȅ)7m8҅+B|q}c@wUO"d\VHEL`@ (bE-k~[Ry:@֬\IJjő&r8u9 "}[ɚm)f1*DUAdK4'MjVY]tBJ`E/#Vg=[ix q~I̡#(|2t'@+l'91Rɰqr"4J0 Qme I<E#Y+D8k`VXrP\ JG2(΋$ٹ(6Q8VlEk`(>Y-(.LdM)3 1KrhfFdN` ţ W(l7Pd NpWQ](gYS-F$_ fDƉe *4,"#B.h uY8,츰W< AkP*$m(`42yyA9܈kQ@ZU2kqh''2C[H #>pIϨ}G|QwwMt&TD&5X Htd#qU.m`H +C20}=F"!(.`DaAvKz$U ,ڴbHt"' .yRDNU9E"G9HLnM"'2|^ry}!P +CN(It{!䪔ye7 '*?N&"I;Ia-ꂳHǩ5`\5Ժv,IaCIkbTf-pdz2T2qK1_' *q/JA r1Ǔr< 492N fdBkHyq833.#V8X+'&gE 8 w(_5Ild Xo%9T@i7_yȃn%Qӛ"6bޘ'FV0b,{$b7=c¨x'qHHXu;4amays(oS$oDBt0@~fԬx k +wjbW [Չ1 ț-Ձ?-*7G򦳋Cf@ Cg*7;ly0gD|׀i1x*` ppxS,W,C6#*pD2Wqp)7 p̐΂㍅yA_x#㍘#Ng|AtUF-oƛb$r㍠}foeQ7mP!^x#XۗME7cMŶ휔2ob'x#7Sa+i{~7V8ޠpcǛms 7"Jx+Y}MV΁,sxAM Ż%o@=Mqq3?1Pjv jMόٍ"cԤ*f725Em;R*xȜ. PAd7Rn| (\uMmHJٍ (d7 I A`bvS)f7&3N bC+HбF}P$JAA᪚snrHvB/$Yvs-}UQ`tQ)D/%p7r)D=`ʁ Rn.)6톉Ƀjvp~Z&JdWv sn&#H'2r7m]ݬ8@Os?cDPn9=~.|݈P#"ݠYw,ιf!ۍo2='ӊR34 !"; q"7Er)p7Jpz4͆n73MNGPYSnkW._.I\-D'pwNe!ݴ`5nic Rn&菢 n!**n7S4pok 39 MH.TnWTip9!3)۵! +M_w9W`\IJPp7StwGwM.EHp"EKa |Tn wB%$-N 5MiMI,_{Eڝe:AAvgeMX%`wJMuXa2i2VʨJ  A*7ap<ͱK{ [_tAKHZ&$VKn &d>D:snzM#D$[]zs +׊pn|a@967И#`VDA.N+DıT\n"DR ʊ=P4#% 3\`rgY l@#WHxMuW@@ZU`*@qwXY5&/nl "_q#45ln]s\pBxk;k*bpSqFi*7M5p[ ~Y y;|J8+vQ'mwX oT%9 vb]Rvf kMሩ,!/ P)Qqjp*ƷnSZ6u;\)$eTSp어`c,sll +FBc&  ̠:`pFCab!MqN}M.PӆԀ6v&hA4,0܀Έbb;mĞ(Da\,vN)(6Q< b\ņCAtuط%a7aI䘤.@n M@$a8QJ%xd 8lhM 1aa# T$N;+̃7ÆV@ÆJG + sDkU\ѹ6(5oRAaТY-^n`HaJT.0l,MƉQ0l:(``$EN֙a+?+6Q(me`zLP+ .2Ca , }(lg%f +/`s(lP`3 6!RpPG톄MjX@aCF5,v ̽ R6k&e_ +(L6†!x' + ͊~6\3#?2pa6FAF lo B#rϡ}* Pe +I[@a†UkU9EI]@†51 dRV +l_[@a3nT +|  +d-9`b@;ZX']@a& EN$†Xs(lvTNa@ac`!Pd96 wNAiMdZ(l<3"Ph †bP75JQUje,p 6(5LP\0&M WLg9 +6TB%8lݾK4l'&GhhP l1ʈLPH`s`k&)6ʼk#@l+lw W* 3gQ 楊"&M Ǯ"aR6LmP(lPc(l8 +bU]rMm]@aB, +4'sHR$.{؂Wo†%XDaaR,lP^F R,laP$]a6$`/5A&XF ]̡Yq6歆Mds +MR(l<6X2ft6b ,<`{3ÆGȋh=a'g`mFb\d%6.76Q|T#,UPHcS~}3?;``iQAP[`g1vA`WX"`W1:$[p]a囿Ƙņ}]%f}Mysk\` _ _+!_SI{I ^^6uH*u +u +Ίr T@3Ύ1V ' k"ӫACᚼa5y5X+DI6_MFuP5y ךdT\Zg ԚBbZSð- )Ԣ 8<#PoКxT{^)Y/J98ARkE0֤4 + P"*5:7 k2X"*5z)$<i -Il !F.s^@n%^jQ]MTdv*r(puVLn5J=LnAT@S\ +ZMOT$B j`֐"Iad2ZU +WM@6)Z5}|-FUM"inujrާ8C0AB(7 +H5e0`T;QdsjCyRj"(.A" eP$TxS>R8R1\X N6װA&pǸcfT.5qe(.5PaJ]Rw9 :LI%gtqԴ\Q'ȯ$fiA&k 4g[u:E]8%v ߦ'QKB*]i=VB]BM,U@EVltт>5n>9dOKPaOp zϤ-WNp7p)X +;M[4>4 •bN{rmӔ9CI%%ph 7'D6́-% i2rLM# uRg!UDQjMzX3Ц lZ k:yK_@M=8iBʴM'̓]Ld&,v +4<1ԣ.EDBLT НbWpT^4xH@M~K7%4Q'MK(4"wf,ݜTT`iJGc۠7@0 >E"lh +M"hј]@*T49IYC)%PBN4u + +!/a "yPQ)H]?ѕcaM A-ЄFi% +x T44K[C&\~BI a3V%)'SPhr?s &4UPh#,PU``G&1ΒpdQk(+;7Pc0%(4s%LHoy BB:|gФ%)TsG 8hȬA +030,!^ - X֠!97U36$hιA ()bM /P ],- u'/]Wͼyjs~ʟ9:yO/i/Ox_^^_+_\_^}F15aO;=w/Tii7 | rY~@{WO^ B"‹\]~wM߀B~m/{~kk<8}ʿxZ}G?-o=ߝ*>_V/K_G#r/LAIYwK_(B<@/%* +\&X~ w~<s!0p_[#SGIg:B'kwr(.(6y8v.-PЙv미T0ğwatFdR)Ye~"='*OSOTʓc:b٘I*IJ92K)u&u]:zoFEX'\gi]Д6u$+A8F~wcD?:TiS^]]?NKb}NCaΆӈm7NMaAJ 9Du]\T6Qe(ֺ6u/Nj뛧Ns_kMlڽD-=U'I$F%[@'5՝}8(4VDF2cHJ|kZ8rx&殰LHҽSzW_x E +1;h}\EQar)üI7na;6G]ˇl 1j#| g 1Q&o!I 38S:p#S*48yq*! Z3 @|Wut<.s#O'?(D P[("^%̅&=!"@w-4_J"ͺ씧f%bR [B遄ѥIOFv܂utVKѱ d[ >VxrU7]Bn1'@lDHt~TOm3ZC֖sqJLnugmTlc0U~ޔSL;b}5}x*p Ӎ4ٵuO S.0R(Fߔñ]6ZFĀ?i-r "PNЁSFfc]Qd7'z>̦ID|zؠ݃XyJy,nXyrB_=3_cdZ{ڙ3Ҋ,DlcMc"eU,APn!D">5啹jC+3ƚ=p6n62لmx늀3~X5Z{'{][a .`#og^}+ 0 s<|h~Ɨɩ0^|tJ&;e3S75BSmpB،FUZ<4{ *>\fĵQof$8ITvkT4J&*g1BHrd EOAy(wvp6prd) #L1b炌sEǗLqΤ>l=ggBIzZZz藜3r$=wybm21=@Jt|B8U̩LL7>Y 2Tá̈|(; ˻N%k ВuS-$p@h3vVa$@CTby RumU#J96Xsam)N8ѠQ.[13w.dCƅңwP`%8[[,GR͐M'QHƪGsSqPr$G3XB2@)pKܑ{Lp&Ҧ3b-fhSex*@GT3\h`p>b$Վ**} 2w*$ɩt]`Tlidm̻dVN=^j&ZJIsOVכa7Χd, ###j6Z՛CY0ipCcSUrF]h90,FGCS Tm\MMS2ݒ\.nAoE/$w@ܝUz~"4;{ЛjS2CLo +`W^ú6 Aē|83p΍\ލv(nܾq˅ҳ|NbSĞ yK ΀Ջ|h .#38:`?e(,”&%-sÖʂ T3~"f3,&Wv-E:5m$eHxomNB~FFfC0SSY=XT*b|Ȍ(_f v\h!/}KXa`H;G1| YČbBYf鉀S >BGcr 73rudZ˔ʗ`cHY:%܈YL[ܢG>[YweCMNjk<YךK/.nY.YT݇TX;(v("律BN0,# Z6{o!A}QlTB!,]"L"M&p2Zg3~7UQ RDFazǾ pc5˷))uE&LVs]Y3>b>X&-άQT84]s(ĉ⶙8 +j@S93C[oxy=͎{bT!`Ht1'"X+<3{Ln/:3pы 3U]:}w/YPS݋_$; h :4L!ZB^ ++Wxn\(=HHS!:ֹ*љDkTY=_0xx>ONIDQNai !>,ql}&PVk}3E/bt$&%Ni.Լ㣭Ѧ2/rg'nzT-뇤 8H>70AJgO3_*B< 6I,iȃ8n's2$He+ӗXOFhrt/2<GYGߒ]PRօ*=I2Ri^cLDJ /g:)q0D$Gzv dꂅR^(ey*_ fM;mB'![ ZVv)Rr0-z])$-h9˜ql: a[HU&>IE! GJOQB}cOrB9ZPE"H@`NHٛQuW8,:]RJ;9 ~T~`bZv@]}dhJAqX& a6hO1i6g&JT0J:lmiI?5 Zr&EIn$_LK&S(%9ސH)< ݄sF8`R@9N*DvRɴbr%-CYa[gz-Wv.[ ؚ|%%tt5P5U_efrT'-*oȲ8) o!P:a2Sdюٖmv$L+$ +diDNJY400 +EIedERڬ [TZ!dsujuA4whtq>WC5w\0aU2YQ^(eu^]]X?x!Mj%a/k D4NiZ΄[@4 ХQLk!ZUFXHA;tA' #zITJbg FʂOʥȻ/3| &zѿ(>JdI\^(W N?U'gXXBY3L Q~--+řLfk #C.HV{; 0A$](5D4mv;Ѯ_,BĊ̦Ɣ NZHEYtR^=́Cւ?TX*Ut1^0m8=j AH@ZH^UJ'.67iNV/vIzd$C[P!+XnllS8NBĈ2&)Kc-Dk'=n!ǘ3[u"֤񠚢Gc4s8 ("NjnPD4zϢPtɼ49QQ"n)T&LLZ*\*G117rm-]=4XJĪlfr3v$>Ŭ$npDᔅ;i%1_+C;f`Pf~Ir%ik3C ^͠/g۰ / ' ٕ*(N5X.-*eWqR +X4h.Nc&A)xOIAY$IԱ\-%o|du?S +/̀N;F 6n/jʱC4%ӂKtUY7*-_n¿jS<3 B)]^lꯛ< Z8Z$%Y%XV\QcJ)Y[d')@7Zȗ-)B6ģAiT_R,YR7CF*!H1kO|2i:"# +Kˎ:"mT˦ &w~X jG:F*,:@vzpQ$@&mH+xjsuP T}BFhLiurWy? B,ᘢ&#ah{;S] r_G6l q:?ŒpCBڂEJ]3CҥE#BQb*-2jbBG_G{Ӑ8tMS49sצ9 !Q)"LkN3Es!#]69z0rlY_cd7!螚fwpx=k$`s܉*VMJ!(S?C3'SX)rԦ+RƨIzdjG=!ΑTSTۍ'MXL nYO 3S5 eFo(Y@l&vAd+:)#B/׬A孃&S fZ+>wZ:mRvA}X|aD6-gK`iՊ1X8jƺeb%ԉGGtwm{ԹF^d-6fiSU &x !Bn[h'kH5,2&u!(g2Ya<9BȁHAq曌@:9 p!鷦bJl +i:1 DBW'#ΠTk{W<1 { X$Ŵ)h*T#(JDVg-D=)(pzQ7,6CCL :Ƶ)jtGjҒUNs_Ւ_+AUI}栣'MC P> FD-8Qc_bb1Sq`1ѐ,e9 +tJX1 +ۚ0&u8FޢAU6ZKNYbr9kbβx),_M,aYj&lQXc/Y>".$;Ng-qHf?}L#,.fAf2ԫ:RXd[ޛ7f҉o$ /xOL $)O9p6x1 63ԑLtK˥赪TXn|)S _E7[zt.y%& &d75&S6rg&.n2ۜ^:~ +3٠NHtwJۇʔzw0Ҁ%SwY Y@Sb61S`E rhf-T;IܯC&&JDxX"ѬfQ &6<1BһH7GJӸX'Dad8)6 }UKb#*%0m1%D eVACB +1zZz2DѩjԕM(vK24[+,]T0I$BU'nЅJ0V1,$J&5~jS2׺"լ6!EnSR\3Mpz)f_l`IH/ɔQ +nAahHFQGuH"MY[T?jP+KULtXEFIl`"* uNP s_DNȔD%9_59=&99DإrP< f )-Uifى$Iɢ?Y ybS&Dʀ|:3qiK3DICcuK5"=E$ md7X8g-3~cM"7W6΄ qs4rx< uj8DN bKsMuL!S8 ~$uykx|5ID 6\Gf>jcTRz ,|j;s! +~P-^YP,h,Sb$ǔ?'PmX*I 5^ꍬ߼1jmRؔ{$H)Xdbihču(aYkfPE2YϧT;?W,!LTJeqhvXgb˚4vk[F[ק!Im\: _Ib7/C_Y%sbƘ+LB3=kFhAS+s#z /Qds`ʡGzo뺰*:BVpWE;eFYb(u 8"dHtG͍-c-+nF$f1p# i +N +YBC&H X˯vDL,)=$Aۉ[&b !r,"q3K"RHTE_yUöZjhYeb.|Y˚mYA2]"r25mAI^KTt)O(-%}v>4 鏉۪.̎#{CfN: If3WT(S/21Y*Dg VU1S6^TF!M,j㖢҅̚FVu~ev)hݟ& ̂|@- yO>$4Ou;7$,?x4ʨrCF4fHbKIRO(,9-$ \7džP:vWg@/Oa;x9:y mH)%%Ŧ.6r řE$?iYJ SxHqlF}Qzڇ$KqP U?".Tk302]dR.05395 # ɑ VkAf nU-h4f]psY5͓f,0;jRdy@yAW7cOiSe[ ]W>jY\qdmټ{"6.4d$qOgn+':gbp_'k7JkaAM)[@l"@j"H݋ knl.g㞍XAuTT54Y$cr6V%}dZrj!`B͎QL?ɂTVfDWptgL9z2r9m9']LXEtD))|69(x0"Y:rɆ'i5?6? eeG{)D/7Ha7<.OCa2(JRD6ͽ G|2ԬiE,ScC< NaKPyEylLl!Ȅ\ `)q ,5nCȤ_T| HyC<}`5f"AF7_'ZHTphB +_JvIw1<+D.j! M&$jHCͦ-h>X0ĉ^ Է% +,  8&}85yܷI ZA&0Űh( +"d1Y@ݐAE-)|^XL2lPShbaCz5&&BgpHG=sx@B@uHG]ʷ[/S[c +5TS65RJҜ>&6aه9> +}1* PI\B~?7Q ig%Ē8X.=uY.lweEr +BU zoa{ihka`=?$@>dj{k2Pj$x +~I:FE3E76់45*-8 V^E7T\hfUYE2ɨFQ!DkJ5!3['[V"lՐXdFK;Tb!_PF AHb$l,L?F,?# 5*Au$> ,EF# X +Vg} +*hMH pMc%G5, ;aVYBo |^lOH7Sך9#{G,jؐ[Be~$WM yY*h#gѳ9irg +$b4·jQ&ܙ`lHc?10X-б7+V[cp'lB+ +ձ1-F +P3T"& qbЬb e!gH=_Of ʓM`0[qQOD6"T pXeL$hMӉ +X"@<Rk v;Yt"5X.Ր}2`Ԇ@iLi `ZaFa;ՐR7ۭ~" ъuj z +6wkA]d :j4Yhl 7sԕ%5Y8kASKvx-Vo>s}˗ϯ߾yP~{v/W wOOIS?zVn5~w|_wZ/}e#RE(Kb1m.AT;#MT^?̇~ V +}<D@VMC|6h ajѧHm2f]{I~@L]ܞEgB4C)}FQɷzJQ-y ~L(!!nuGt +e;Hmt3tnlS?,fSC=_,{pi;t4'q2 L;|s|C|;SI6;㬊}ݟlt@SPN;>m>v;HnwGh0kyzhj1/hm4ww? דF5d;>b;m +xα77%%w7ɽFыwv2sNXlҭG_IXcxnw{<7x}ld3N([b:[%~]g_e;_"OMòyOo>;;9/on?~dz~Ϗr?/?}./zϾy{sSu}ק<~?_ތ?n~><;T<>+'I#?pp/>= ^}CjLnb?{yzwbhOϾ}}7}zrW9A޼|m=6{g->Pt!˛>0wɳ)mfx7s6No7xwWon:_?Sg *m܏vj^9}14ӽrWNQN_]w/WgozIpovN-k{t'n5P/RNtm#]پ9N:0)=9cs%Ny&1M X7oΎ/w>n~E{}/īWޜ޼]vlh[e/P0ۊC|HVWl]f$PPJׂpQӓ}j0ڃ j 7o_~ur՞\`Jô0.~{~|}wuyz w^]9ю7wTi:Sg6y@kjP;bJیikC +:_\]me7_>e.|YdWMۮޮ]{70oG=Po\Qan8>{7wծO;C[c?ϱcO=yekװH?kgAGd861+b|D; vxuu6w=P0}ү!e=-G0%ɪO9obDx<;?"eϯ{}u~#ԡ2O|On {Ns]*.`T[m?~7[I;vr6_]&~$/nlQ$yw6]>l$fU3DOQ$| HǗ_leCͽٛΏON/N/o>?n6-3V~t7^h? +suWn-aqwƸ8mC2CJ|a>nf:q-ծpWkW8 c;{dŌ +ma)j{D?뫋--wÍ6߀>9j `~uv~U m`6@_0MGC rviKrn.<إm.]zB>vi]zDKWttcarccKn6jW8>vi_mC۟̇\'M~y~Ylvnu~<{g{4;e/,犧Lq:)))=l/x¼q{Y'OZٟ43?iI__bU<mbpg<E綞'4=yriRII\^\fGA6IGsxˠu}GGG"=._:+z|6U>Ik߼W\IwەTeYwWo:g%;va;v#<}|NC=D67$=&ѳ{|{l>-FvbD[0<$2~(&]Aծg7/NownKg<v,k{wj={l_Xqg^ݢZͮq'vvln"8J֋5Nϯxg߾t]/ׯ2Ֆko= |qA.^|lӵ*>ROު|oOO/?'] >]|r}yhn6ǽqos)nsv=EGN_x7ym; 6_}vG-H]> o;=wSNyV.j1WbbrlI6o/o-QW{KBdwbK`[%q-Q{Kuk4{KՎ[o}ak_Ҷ7톁ig +q<3d =юl19?}6ޙiܝ{w nw aJ\[T;p'bh#;:Dz}Tmq7vTqQhN_w-b,ի77.ק/;iMX هϝ,ؘb]G[RZrb[z{Kxoq[>eՐPxF':}rPZyӭ"6fgDNjC;7임^ƛ&[,x,coovjoƛc!6aM(W.mB& [}v%_ζW7}am?^'[((k=H@|{׾gC ꫛzogیtҎ^,Okb YO?ܸ6tsz1=y'==x{=zh~豟 ߼=\ R*X6JAI-šv#XpLa^ a<nz m.=:7?Y͆ +_`P_vO^?c{觛J{tGO.맿hRv[A|{uԽWP +ϺܓSQ(yu-NPP^C}p.aGR6D82AwIAw_m CrŸa]:8:oΏOɊIWXp\A?Yv._Ev"iۑ 0nN7?.sccۖJ5$[Jfn.j߿&} .p{nӯNRwo=lqA.^{l o9x#^x @fWX V.9H.˙R{/k>Ys/oW~s/=Vm/峟?_ɡ h!=́Y]Ba$K[xϣ5gjm~=ŝ{P# +;1-?|q|>do㭽X;s? 볛۔N ܴ#w#ɏN0rW字o%]4kKp[3[6aALq?y_La%V]?eeŏ[>PO7;Ā]"/{sc|J qs_ESF9;x|_=YkGk04> i1#Oͮ#HΝd''? +="m/??]?]/ro-&eG~?4J/^z`fǷ'_2zYYM_N~'>ʝ~m,c~F>|jR9t`؅ͪ >;N>/HvQ=@vcސݎ:aU~/hH/ruEoWB~/︝ꇋo1wOO?hZ}QJѸߝMW_|}6MSZ8_~ 3AOU *_=z7m SGQ^ҟE\g9U͇u`~M:t.ԝ>Z>g'{ee/x:;3*=_!7(> r˳.߮vE }nW?YJȫҦbdIVO6mFm־:&G?֗x],gS:z,Vf@xзvoV]cqyme/ 4_v @!̛T5(Zw?p;6k 7o^ fyE\><=><귝{/=zz}پ:?c\t}rpo^$!fO9k8o^?\g/ AĻ5E ׿b]*1锔M~fG/saZ'vIwE/޿V *uL_iOcSm}o۷pcM|Mܭw8˂ȺVSpm8򳳝S}VpK;ɮtQWi:({})w|w?{wow~w>{MLϲSI}+RcӰ;qkOGXj?r>Wy:=>yw%~{W{I}|6~yb)t2O诮Y!y(S? 7#q\V͗'W/{ym֩[?IWW۩ﶗWߜEWTr|tL-nuC8߇>˪}%[z\4x5 +OtvSY ZL=? - ˆDr5E85GO[i!NIϦR2.>5@/.]kg +_HiCo'r( +}Q+ő7C>з$};>>$v4|k笕t> +rttg_Ϛc6λZH%o{yhIF_z/Y!wlMp#~WAy%GcpwBl L9_ɭ:W'M9?dwR>ɱShtm/ YyuMh5V7w]cx-.}UbKZv@?G]}V2E&IK RS*o-r!@ioC>z8}UַR1ajX>e HK)ƸxwߊO'!N~d䊅Oy?5eb.\@h=#DNuZbob +#1 l9:5{:A!RJr^'CS[u˘Fcjj(N +4YӪ6edV~VSJFC!d#PiNQ6Α6f>FEؿoM)ϺL蹥3J_"O5GTi~PTk2"gά6HH5J]_[kXJ<:r4kjmny__2J~;w(}˛(akICc`۟^R%zk/]?:ߩ{J W* +Ӝl$Nrɦa Dd*!2zQz3ÉT LrӞ$~ީN4b]PI2t+h鼤(ԥ٩W w1jSgt#ӡ +=][H8%E\E`SS]>II 'uuH?~OlA:^z UY;Pc\&ޯ2 }rBp+O"UmFr$2H}C?Rg(%߷'CZwY o1ƒ$^7UBee}7^oP2MX[46}/tmYWbm,T_Re!jTՙEbU!QE_Xq_Dt +!A oAZǴ'G&վt_^6"o@;M׻\rSf3=W[j_sQaGLfe\v;{tok_ixSS7i#XC`wL!@ho5:6HcӹoǫV5O-M?.VD7fi@ &|z\smԯ-n]wm P?` DtFDΑ܉?dȽ/&6uQץ dr%1d];b0 +!tv96IdyyNnXzQZqqѬOvr&P'RFu}\ds]D~W4uWCY:oω)A"j,u&זލ@-Ht\@Kn +jo\l\snY Ȝ,TguBOq`ŋ4n*Y7Nɘ[G]dI #mŅI&5}hܻ}DC+ lӍHR]~Nܖs/4~6۷@u!ِ9:nWbmo+O(>%yNM|ru'C*"I;C'"6hgO9m[Ag %tT#Hb8]%!K]3Q?w_.f9_H|bLS !=GR&?{(ku)'϶~{$BV@mxx7Dv7A߻:{-m}'RC}wcW4.嘮<ooٜ}?tM˵Vw`*SŲ'Z_52DӭBX@r BI#F,xu7֬7']*ITFֵc_}W )Nx[v|wn)^;=Z46qpG*78Oap2~;ͧקy"aJO鬊To{"D|^@wG +*y/ 9#ۑvY}d\}qvwyauq_C6ҟ/{{v'c ᷱ;~_B$_Myql:9C+OOn>o|׫Ϗ/=^g{I(~ dLL4؄&L|M' wι;ZR$J*5ShS{fGrNM;_?u-\!|Ë)rh1Z3/hlm8ٍ:@đnOMWڧ~fNh/6&7"! wo83Lg F=>yp@זcmfWf}'&36t0&;d_޵}%~!Q>?߈7t?pL=YRkvrЕ5=:l +r3չaɭxákJIyB!&p>ЇjЦO-ä(o` SEߠ?=`xTfsOvR7)˭jRsz8QPoI.JE@%O*  P>w+B>Ua-gdn_0%2x`(`@5t hY(P>e8ҭ 9Ր&#f&ރR TmA <9WӏXi`Z”!EVbF", ȰAily@Dy$yMGز-PďnM$Ž}!kqŏ/ίlͶX>ݾM1<39=W8ʀ`~7_`n/nCDEx }{q\.9Bbm 3н^dMF_!CC`:pPd?ΛhGaf @~Bb" @(7aT&x$ă8a0!5l OHK/ `b|>/TPqǂp2 [gaa7a3`ztp8@ As²|~DG?70seԏKGl3(>;z ы&Lbv |tne3PG0\#82+17F $bGQD3pf.\_?yfD, Xs887`@h B1x)Qw#.L 0 +fVaR܋ *;F$ypCAdx/ &#Un2%iaa xa:5L'O<я^">dA("הAF)Q#$J1xaZ!K݋4VA}~ju`AtU(3:;ŅOXņfx@@ڀ7x=`L} yEY6*S]L4@ +@@Y^۔ 4A@a%PYh GF0O)gՁ@XMp&BȂ Hd##B<y40߃P1!^y& ښ Q}H յF~RPn/H Ǒ;IRZ`dQxLg%M $.ACeLKhcZ +`ДAHuMВ;O` J <n_BY L ?{(+ۇ4ژ 7<BaJt$ 0W~RPĸ>}D:lq!DKhȾ qR(8^Ar7a_YCmX8P!!o(Nz$Z$ڍA e20 Z n@S γo0-ɓ%8^(x-u!rUs;ip +]IMb{ʍN#feL<[ +̝p%K=~Y[ё)Ӑ<~OnKhz7YSMdb:/SaAig4 7'SNŚ\_HV l`x8 ح6oe(9'dPuS ^ߵ??@v5s[Ra:JAvjt榘c>3_xo2,o^T46b|H EB[BJ`-ȆȎlMUKKq(Mto2Vbaw܌Y@rM(2b`vֈ<G;Pn?ڃyַhM&i ϾmP(;1"\3k¹xqT6DLVk1{oO{B*aT-524ZN3<6Y?3ӈ"n|v~ ]sgGfp{TkM  ay\ ;=aF{ð䎪aUUX:qM{(?0]rJ#d<-[p6èW(:=_`6F I-D8XNn;EqAZ#ASip4(,$i=YՖrG`8P<,&Dz;g%>&ɛU| /Fg1 +$ t' / +X/U%[*DYC3rZ >dA{8\2\xb:%L~4ᄿWyL2;;`;4<"b. :@{[Bw:Ur`iK@bq0GR JހscF^3ZdOU] zP^m]XҌ0BH,e哇[g-Uٝ[ ޫ kBq&D؟פHh,ؒ%B'ب\xR=|\0aCUNFA),=X]6f"q}7ݽ|pmb]v3.\k8s7Q_v:;]ݖn5@Nc|W6o@&NK0 W tPOP7 ihBzrN$8zwr} XƛOB@^ +0l'* +?RnfE#ҽ֛Hh10 =Jx +rнA[/][N",AM#vk^W{qxzψ +0] \Zw{-ļv!| +oHE_r"xdzϬKzI17Yz +Гrŭ8]40D1mP<>0+3/|Eo1bd/7gt緪.)/?IqwyMV: ^#鐺2* 5!+h-GB/qŸ^"* Bt2?HP:9ŘŚ&/USLhl;W@XMR%"-V بAQڂ= +&׬f*j" ;o%{$Vʸ6bd xlnk+2zҌ\-h,pl^mbA.nw#I쎜P#f'/WW=hVWٍN3~F]6p͓Y2Sngp0Gz>4+/ $;u5s\$򢧅,q<]ՕN3R נ}&"?`B@og~lfcz=<Gڨ_reݗ3G_rw\7_^x:h8=#c4@Z;Џu{ RWK! +[\Ci2/-fotkd==? *7^YEa8ۀ!H05n~. W'2Q:7uxZΓ*U#y:h*m+tۙf)t$6Iw BY!><ՙ#ڼ{ceȏ19]g`>W=W-\fZ3838){'\( +]ۥg‘fŴrq:rr3ׇ?ד≥g/6KoL6zôǬ^T9 ? zAoF٩Mkߌ.]kG 2ެ7?z37ccbsŊz]o˷wz{5;^ 7]{=eBm/z/"~ɤ>hW8ԇ_G}8U#@I\'>4O\zLԞgߍ_hЗ{:a7xY2N/r=b]}cпyWb|vCC :^M4Y/v~eJMtp0'dЗGgq`]v fp>:,9oD+jEe7!h a5`(׆GahLCk5tOa@z+ .1? f ќ,]hhMFsia-}^ l cx3"̘jsϕ +Z06?^Oemgdo07_1n[م4&Cif8scX\Xw7lk1iM-'0ѢkFQiV.LolڟG}hͯϵG}mS6'3جX\,Մrp:$rMT]bl,dŦ%va8f52\LQ?޾ +endstream endobj 22 0 obj <>stream +A6OmbꙪ}%O:c2&rY _O.Wv|>~>5Ө=H(zRߒj[fO뇟L秃y??"c~2&Sc0S4fljɺlWKfۧS%;{9y 5\v>tn]o=2 m>_7CbELqDQ[tŲ)~>NŃv,ajkWE>YSM6O>ɖ"cv=WQiwEHVwx-bsyYA-ږm^}\fNwdbgGhxN~z! 1_pv6[ǹu^K34"Gem-?V/yjm0wEOk6l;ON3jmtٸk{5}k^Nl ^}fOߖi~z>q̿ϟ׆{꓁>/-goE6y[;#U|>D}\}|O |ƞKC7pwnl& Ƿ-P>g4Sѹ|Z75G5 ?zͤMz'jy4ŝ;N7w`Axl5Yzf)獶k"we[4=b:[lҍ/5hI*b^OW\:"u<^^bcx$7x{ +lv;g}UJn1N>a7_C_hd/ GMS*Y$={P" E-( +i[EP e逭ljKyzTVF%Ryv*N&ުAgMH. :Z !t }W|"PpUBQͯƥe +"hJ2c:)nO`iTekQNu?uRDAwL +DV SыV($SLؿK{Kw8usF-2Fq3FT}j.M"mAr-p^|rlFc|hr=۞ӵpv$1;62ҽH0=FP>-1BeZMvK;AG4"GԐ\đFH6QL[fڻba*+';PYY(m>N*D@G-$"} +Ɇ/U]wۭcF' _?~aSk>Hki,־@֌A{BSoBjO1.nybZyNYMS;7ԵǬo$OoPR<Ȟg7a|Vl8"f$`[Q"DR! ˕¬0¦aL][M Xy9Rh9rD",%t$Us=k0TJj\YNGK7Z^wU}Aɕۂ"Rv)i*+AjȄH*JkL]~Zs2DCdVk. l"E=К빆kh.蹤'$r +VZݔ?v[E):qLC RE,ㅴBVsM n\;]bH7bݕaE%*eўRwaP)d͟+%qʵܝ ] H3uQ ϓnGXGo S:IiL7 YDZrZ'0\FCvw^ Bl +SgxWnK}aXנ]?гo֖Ƞ߲^10%0;%6zuV+:7|N%I-qc4-&끧hROϓP!難1N̔ Tܹ'SdhRȴ2K-2nWm:Cc4V<~>373H0ŧ#υ\-}&7*cIhPd꽼;YwO+]3_>SQҽq䜏炵84^97`ktYЛW@osnR'K:Mf|2`> +mqKuu4=4c_E+O?͎̳)t YlO*El2৥#?-(^3 +rXf֏OqN-,`p }ۉeHc#LD,IBB"c2IVȬC 5A1-Ǎ3q;nL#ޛG}c'>!%qN;_TʆhMkXyFqsAx;B48|0DPPő,O~J:4W9ɼӗ{'h@D<6yd{ 52A h]&Z@"7k o$8r3O, >Dt#if!uL`O_^G/xM7 +Wv{b㼐ٽP5qænB6RahM-!%( Nt 9#v# %$.rɠiAhԊ鯊Mܱ};;o>iz"~nEռexkw_:쐰i秐QS`um`pF[vȓؤJ|}<4Kw9ijIwS6H.SXYnn/6>等C<th_4;berxtFߩx)M{@+>wS4&uDԔgjdlnݴ߬7!kgl-?%Jћqfߒ  f\̢7W] uCB(ʵ|_o-T'$U C7`;._$@W.ZA Eܨa]EKa&h,Q!'aw,jEXQcnAL0IyQ{HĒ]lbbUVfDuDH4)iǮ똑v-ѢDM>& N-,Df>@{k ZP eLnrOV3x6FԔmGmwnMA]]'&lm0cgFs-W2!etkkgoB4>c미뻰?l`'Ύ';}.қs=_L.d@U6unvw|_8.fv5^s+YM2i3RgRyz1-f%đ*CVR7E9Q7;m<Dyn"NrْRN>Fo3Admo-P8tO2C+,1"em)l~G[|ZЀ +PSMCq/`ĠX#/@Ô}@S9W,3ޫ;boӭn;8_03N,? +-%xfcb\׭fѥl=gR CUz&1bh[![y[杻7/Ao,|7'a/I[ ʼy_lI QLC t3iPPˬypZDPֵ g 94s.46YI1{daK$&Pt~HJaٙ8Ξ`Ϙ@/ic](Tq3F:ࡾ):si}+@x4?9 - SCORv>5-!sj;_vZadΧb]`^yjxO@$^SՐVteo"fy7ĝ%<`d? ͔b#hF{ّH 9z 84FI'ٛ)nܜ2j< ߟmm\[g'&?*oO_)qwxNrNr ȋvx4<rҜOwG*SnNH>Y{Lc=ϑ蕱;93;L:,Cx% Z$G}fh/l$ݨ|)tw2'XGvu5hw?>dl.?mLf[ڰ/,Moy <~C<ɑvP=% +v_q! ob*\n;Ei)lx!Tqx3u}gN%#VA (ݖI 뗙;0LDKDuG|  l1Q9;Z‚畦9P!R.H^Q^^솔FH0ir7~lInbXO,5 X(單›<y?kd^nQiW>+'rjeBt.;+R)$jY+3<-Aی(sS$+s%,Ӎ ttdHV_(eDŸ\n]{Bܝu{u@,Cd7-J" c_݂N;bBn='ϓ^yCɫ + zq藤]mukqWIb V0LZnWڲjJA`I Q@픁*pQ2D:oH(NDgqތZ JvGÿ>ThNs6W UEhH22E|!LT(1vl<ِHv- }<ϲ8"lgi9ڕh%53:W~;eބ{nZo /b>t#9zcq]?񗨟}~Bb)E$H/%.U\o{~蝋%x=sh[﬿.<곶#wUg0oPpO3Cdbv+jLK񾻰{ Ǐ7,Vt +tbⵧC[Ⱥ- _ .Z1Fvg@©Vflj" #xHf4tjnr+ C|-냜"[ ϰD9Z|[(|Q;g%5,4րͥj( a_פ2Ք Vhm !V=9(KE\WGkR|##y9YmSk^]0T Xk0sXy M!:~.*XO-9>ђ%MM +#&9pn ezK5% +󐩵5#gAfqg導'-54@X1OFuಊH +oqry!V+@CJo\>8qm$7VPk?#+4pˇhܒڎYXDۏIaE _6pICD<٬X{\k{=FX^uZ?v xBb +%4D*N2D~o`jNk=|~Jbl+7a\>7H/Š ⯲k+Kcۡ:b"52S.(Ax_/o-[mdGX}$k G +@ρ+3'h'hѻšxB<$<ݗBX X2'o%}<~S4[EXx+&z<+Қ)Qذ#;e-1…*u]TŻ=]`3RA3˼2!Bxաķ ëTJB6٧a-t1Z8]6Ho٧5'tܘŸbƆa2.ɼ.K.I=Yѻ]ǒ1[g^Chqj@Icu}{O?rK4ߏÐIϩ/>]|%6Br(:K,( $_8َ5zs.4 ٧ߓ@gEeKX>agFDO~u/g]2l hYljyҢq -k+HI̎|> edЬU .o3?}cwZF + >K颥; ooV6hoexlv_@,"4z *Cxv qVH+nwi,VS~b;?\ +uD딏5І;is:5V7`E{vŵm@mqDNd Ve{!F0OYd@+bh`!=ܸޭfŊ +0'z2lٷFKyH0̿6T4r#FfD"n ZrN>tr{Cxɽ-gAZddAHF9 Fyω)2Hqw4d38/ZU@f< "ii` "ߕ':m_=QV=-ych4d]tX%@싶QRZEʢnu;}'w9ա#%m0yŽ9 yyBH?&_.QAT^}P{ g 5z]yw8.xIk*5hYrK#dE%4^>:FT[8&写ˆSsa]N oSXw\N˴fiB0Cj* $8'Y57WLrtQ%)EԖW}b>*ڳѢ{}7飴i `sXln8P4C1<5A*k;ʛkɷn'DJ+;lCNj)Ir߀6re^z5Ƴpy=NS'Y'.5:h~=ֽ gu $YMoK" nԧ*I*E=a3*@SOVȢ"x=w5/A[wlSE >ɱ +ʐW`qQdHJRVEu[_k2' +4Gh@uګJx57s伴R.Zz!祅ЮX*x8)@Ppy]wv +ЭK89( H@4^VpZhEx"\A@h>'2 k&0jM,ȕB+1&LڤJ.'-5t*gP<9)vn^ + B).pXS ϲxZSgO % :֣\V,yb˂8Ijyz$.< q 71[̉d: m,gUlꀄgRG=ܔr#t CϮ[ -Mk_Տ-vqRW58s{$tσ*$aKAᶸ2GeH QG]B(b 4 퐵 +§S-Nnv:uLbDw+M%&B#eִa=9=QdقR]gbMu.TѶԠ|שl|h_ͧ٘ch/&{ùh~SZ3-t!g˭sKAYH_-a1 MQ"\2XJnI;svmI[Nׁ9a,D ~ -릏RGT1iE٫޷jIJ#~EtRM9K)Z]F;=I2+vKѥ'=44(5t]:>M2zwfeT[SK\^JLf*NGRK*VEg^1DUۥEyfmЄ^M~ +W팩)9C5Jz}@Yxu9>@/qNWȣJЫ(k& (q>99yENX5(q'((׭TΨGkK^O9|КS>(q($B[;7M:_A4[V&j&'~G4ؗyAT%Q8DB63U|c|Kש'!`e)i ߾V\% AnS4\DB=.`?ej0 1tLSF W@>ī)oLE{SAޛ[laN +ULQ /P{XBFȇ"uv ̇yoDƭ\U3!mA0[,< -D)t3bյè#3$FXZ"2j^o`&/4`Et%aQƘ@?^22줪xoNp7l}2{)g)33O2캹wf-9Wd)/3o\2 2VK{g=R;TWfzi]fnM\!^PKYSKeo)RU ++A\h TTedB4 eJ ;.x|dB-E2(zݲ')^#)d`u=*)%NW#@-#-.t̜GZS$߱U`rݗ^&fNqmK̂SJulf^oNx57~hKCZ$ّaŷvx$ُÝd!$¼ۓd!$B@.%cB +%az|y=ddڽ<X{$Iݓnu +ſfyCRߟF[<@b JW ٰX-ߋ촬[eߤ5 +jsBjp)7L-}Ћ;)[bL~[%}eb9='' 'O*Ok}R^zf{>wM2yQI{Zľ>- +oVD&I͡TՍ}bP'ku7 +ݒ'uq~ľ>L%d};O +Ds>qh{>;%i7H"TWbjB}CFJw#="h?ivYSҜ}uPYvr& U.tBD`rh#aT0ITԈtlnU(Q +mOh[WթVtlQ BjԚp䭐-٫TWvjMxwtiSOVvpor er4\Csn`̎SKS?u'$F,<ڎ{z'ŸU +?WHPT5RTU(+1jU n\*-TP[5-Ta8\9PREwZ,O-TVlzzU"lp+|_,F{_f򗢡͵u*` wր:»S@V_X6cbj=̻-T6AC:z-tl[]'F؄0VlSDzsOv'vSR ϒ4(E<<3Icd47h=[t@&3&x<G" -J rj>?d +{p+Rrl6#r3aȑ%]ڬOk) $ %p$NDSo`ō7s+NRZ4X/7)^$Hcm)UX݂0p 6Nz'&obːWiwEM)H TQJ⭷Jw Na$OH֎ +WxP6G a3t*OnAS^E}PBYLp +7Rd6`\g%]Rr6U^bI3-*UMuAx7o^2i-}ѥVZeyӢzf~,s4BB_h"-s#TSxz+TK +]GZCE)Zo4#*kHIԚ(ᇾ&%Qk>" D)~Q؅^^5)I%ȃ0ڔDNW)2}G{R= +X!4bK"u)VHg(-+|ǣxbnKB+|Tb.-Jjc(xPXPۭV7< Z<2J9!UPم$a4 *jM8G׍UVv蛛 *NuCAO. +|] +ʩā4쁤zT+x|v- +K}C!ߘln*x(Ek]ob2I<x/R+w(xk)^ࡲ#iW!M/]jzd +*Gگ *Q=P.x(17jD N!ѮO{PbR(*x'R dׅƥ}ewL[-6$K gO/XfZӃ婧^6cZo7zKj-lmц'dzvp0mLund]`;?)+UZ9$I,;e;X9u'Fk{Vs_R,,ɇQ_@rOk_WN_B{pʓB#| +^8:Utwuz8/IO,B?C./l}64R_">ҥ*bYioc .¬Ӭf[yk[<-S^隗 C>~ G:wj*\-܋ RhVd +7WG2EWzoVw~SnP9*"( !Yo띐`ϘMKgҺ}?V.*۫{_+kxD|Q @ّF9O ghFhȏ9nzTR>gzjNt#ͳu8On6"?Ԗ/^gpygO?_)*-NxUt>H)t8_!y.>ӣ#*I ?@Ս f"0Yݘ$_`dP^zXp ð7h26K/̡ob5 Z+%LFuy Zy}eH ZɉaM8A.DGh+0LzzaC=E``ޛgކ% Xy [S[ΩY"8u38 ti"hό (~8XxRJ 2"w(-7B wea-!% +wOeí e0,bxt"d斺X&ޮ(_./͵~@7:yCҚo,fWiY9ЎBU搽Y>"뼞 +ֆ|Q +By tB|;wg<7PV!0eP*PsC [$bZF@c9׳(N?Rv#ʈ]??7C9O!hKAγ!)"dКRC#w% lK*fa9_x:#,>"s8)MwOBby*["~娰@a! \=sg=܊@ ݳ\޺u;kZGh|Xfg>iS#8XZKTq0r~H,T5Oq`{յIt'BҔBY~c1Ayc3R*biJ]F4F0#t8 !Fn>H ,QzsQq}1!a H\7 ,PuW)WdmmIݬ2.rDuWWvQ=myeDf xZmUVrǕZ|i\ŰwlL +[l +͡fwp͡fP{qsIB'CM97/nU"1?R4^lb9T0Hq37Xf|͡ʦt/nU6c/0PܤD~aG&4ͧΜ\jo\!V-!4JiZaKyuY[̊v^+L +E@i6pc[/A`T{ +)Ӽ`$ê!('m{v'ukbU5p6B*CP;\ +idZbɧiB#OVo֮6}(\ƗeZNѢ׻LŊ ̝H( Uxȣtv48ѷru8֕tq9=~%SRb`<axJot9-+hRo!>h. *v䍷厯$R1Av0>hRW?&5Ae}}$"~RyH4GIMPhc&+c&5A>>ѴAs MПkA߫i&5A#M_4 :i~=>h3>hJg\}$"8ImU& =>hRTic&z1As MPuk5eCJ5E:b߶6׌jYo)͗ W7)υFmtah1U]憚Ly+^~!"#[Y(HX؎6#"+t|oveI +[:+$o{)r0YMOVT@]).ȧžeP6Fߙ꬝zTLP/jl]ʗ\q#}9҂n\Q!#C֥G,E"xtdIՅNOm1QԈ؀ +OGSv:+Saa9uF{Ȃ-73F>Wd|&Mr=ǟ#*STۼf)ڭ]+9/[U7emtRT=hw2::إeFm^< ~,n/KۻFu b2m'6A2˦r. ~ݿ>zN f^Iͧݗ{!M%nǥ^T>ҥQn#tSwxxzw|dp9Hҟ&>Xj [[Ya +4Z&*P4u3KDJO߀Cf\][d3ci;R #q YMS- CRX)]LtvkN!v,`gA]qX]qn&Rmzr50$`#4+dP:P)΄ ,r=ǔaItfq̺e@áYE0T4g6pוlѠP51!tIBl- +\.A)6i9*' ;^T&:>4'Bg5#xK҆q߷l+`X#"p +hI z&%hBl mCW5gLF⹞BL~f`4tn5[' P x+b@04a G +s "308E#1&̂Ҙ R%1=HNBp,_']P 0Gll1z#$f*a7 uE "$8}nq4|aW@I'vABʼn2&e2|/IC<,|pc<)ڍӰ=&X|#1q9 r H5Q q r|Ra단8+L 2,n". + xPB *=f!`!FΫ(%s@@lJ ($AF ů N!Uap+sgRT^^ӎ.@T6{$hy-$ 5)R\S* )iyBlL"ZT",(4Sn}LD I6Ӻ%>(3H(pZ\6(%hPa8U:78*3ȉC?t +AQ&RH-ZkE֡&CmL*#YnQ` )/ +½ SǐK g&c6@t~"Prn0p1%Ȣ,D4!Vl<*<: <X$͌()?ewJIui)5hu1&2{hpĚ`Lq`2Q%Y5q"{<*8‘zp-\bEI01w6O7?m׺{Nk:%g\J:nz Ptu6܀ +endstream endobj 6 0 obj [5 0 R] endobj 23 0 obj <> endobj xref +0 24 +0000000000 65535 f +0000000016 00000 n +0000000144 00000 n +0000042781 00000 n +0000000000 00000 f +0000046539 00000 n +0000148486 00000 n +0000042832 00000 n +0000043197 00000 n +0000049522 00000 n +0000046838 00000 n +0000046725 00000 n +0000045160 00000 n +0000045977 00000 n +0000046025 00000 n +0000046609 00000 n +0000046640 00000 n +0000046873 00000 n +0000049595 00000 n +0000049791 00000 n +0000050853 00000 n +0000060193 00000 n +0000125782 00000 n +0000148509 00000 n +trailer +<<7107660B50FB86429E9715B7B157035B>]>> +startxref +148689 +%%EOF diff --git a/lib/INA/images/logo.svg b/lib/INA/images/logo.svg new file mode 100644 index 000000000..8dfe3dd70 --- /dev/null +++ b/lib/INA/images/logo.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/INA/images/vertical.png b/lib/INA/images/vertical.png new file mode 100644 index 000000000..35039daed Binary files /dev/null and b/lib/INA/images/vertical.png differ diff --git a/lib/INA/images/wiki.png b/lib/INA/images/wiki.png new file mode 100644 index 000000000..753272ca0 Binary files /dev/null and b/lib/INA/images/wiki.png differ diff --git a/lib/INA/keywords.txt b/lib/INA/keywords.txt new file mode 100644 index 000000000..92dfc91e6 --- /dev/null +++ b/lib/INA/keywords.txt @@ -0,0 +1,45 @@ +################################ +# Classes/Datatypes (KEYWORD1) # +################################ +INA_Class KEYWORD1 + +#################################### +# Methods and Functions (KEYWORD2) # +#################################### +begin KEYWORD2 +getBusMilliVolts KEYWORD2 +getShuntMicroVolts KEYWORD2 +getBusMicroAmps KEYWORD2 +getBusMicroWatts KEYWORD2 +getBusRaw KEYWORD2 +getShuntRaw KEYWORD2 +reset KEYWORD2 +setMode KEYWORD2 +setAveraging KEYWORD2 +setBusConversion KEYWORD2 +setShuntConversion KEYWORD2 +AlertOnConversion KEYWORD2 +waitForConversion KEYWORD2 +conversionFinished KEYWORD2 +AlertOnShuntOverVoltage KEYWORD2 +AlertOnShuntUnderVoltage KEYWORD2 +AlertOnBusOverVoltage KEYWORD2 +AlertOnBusUnderVoltage KEYWORD2 + +######################## +# Constants (LITERAL1) # +######################## +INA219 LITERAL1 +INA226 LITERAL1 +INA230 LITERAL1 +INA231 LITERAL1 +INA260 LITERAL1 +INA_MODE_SHUTDOWN LITERAL1 +INA_MODE_TRIGGERED_SHUNT LITERAL1 +INA_MODE_TRIGGERED_BOTH LITERAL1 +INA_MODE_POWER_DOWN LITERAL1 +INA_MODE_CONTINUOUS_SHUNT LITERAL1 +INA_MODE_CONTINUOUS_BOTH LITERAL1 +_EEPROM_offset LITERAL1 + + diff --git a/lib/INA/library.properties b/lib/INA/library.properties new file mode 100644 index 000000000..2079e7293 --- /dev/null +++ b/lib/INA/library.properties @@ -0,0 +1,9 @@ +name=INA2xx +version=1.1.0 +author=Arnd +maintainer=Arnd +sentence=Read current, voltage and power data from one or more INA2xx device(s) +paragraph=This library allows a number of INA2xx devices (mixed types allowed) to be read and controlled simultaneously. +category=Sensors +url=https://github.com/Zanduino/INA +architectures=* diff --git a/lib/INA/src/INA.cpp b/lib/INA/src/INA.cpp new file mode 100644 index 000000000..fb97b0a5f --- /dev/null +++ b/lib/INA/src/INA.cpp @@ -0,0 +1,1140 @@ +/*! + * @file INA.cpp + * + * @section INAcpp_intro_section Description + * + * Arduino Library for accessing the INA2xx Family of power measurement devices\n\n + * See main library header file "INA.h" for details and license information + * + */ +#include ///< Include the header definition +#include ///< I2C Library definition +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || \ + defined(STM32F1) + #if LIB_INA_EEPROM + #include ///< Include the EEPROM library for AVR-Boards + #endif // if LIB_INA_EEPROM +#endif +inaDet::inaDet() {} ///< constructor for INA Detail class +inaDet::inaDet(inaEEPROM &inaEE) { + /*! @brief INA Detail Class Constructor (Overloaded) + @details Construct the class using the saved EEPROM data structure + @param[in] inaEE Saved EEPROM Values */ + type = inaEE.type; + operatingMode = inaEE.operatingMode; + address = inaEE.address; + maxBusAmps = inaEE.maxBusAmps; + microOhmR = inaEE.microOhmR; + current_LSB = (uint64_t)maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA + power_LSB = (uint32_t)20 * current_LSB; // Default multiplier per device + switch (type) { + case INA219: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA219_SHUNT_VOLTAGE_REGISTER; + currentRegister = INA219_CURRENT_REGISTER; + busVoltage_LSB = INA219_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA219_SHUNT_VOLTAGE_LSB; + break; + case INA226: + case INA230: + case INA231: + power_LSB = (uint32_t)25 * current_LSB; // issue #66 corrected multiplier + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA226_SHUNT_VOLTAGE_REGISTER; + currentRegister = INA226_CURRENT_REGISTER; + busVoltage_LSB = INA226_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA226_SHUNT_VOLTAGE_LSB; + break; + + case INA228: + power_LSB = 0; // TODO + busVoltageRegister = INA228_BUS_VOLTAGE_REGISTER; + busVoltage_LSB = INA228_BUS_VOLTAGE_LSB; + shuntVoltageRegister = INA228_SHUNT_VOLTAGE_REGISTER; + + currentRegister = INA226_CURRENT_REGISTER; + shuntVoltage_LSB = INA226_SHUNT_VOLTAGE_LSB; + break; + + case INA260: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA260_SHUNT_VOLTAGE_REGISTER; // Register not present + currentRegister = INA260_CURRENT_REGISTER; + busVoltage_LSB = INA260_BUS_VOLTAGE_LSB; + current_LSB = 1250000; // Fixed LSB of 1.25mv + power_LSB = 10000000; // Fixed multiplier per device + break; + case INA3221_0: + case INA3221_1: + case INA3221_2: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA3221_SHUNT_VOLTAGE_REGISTER; + currentRegister = 0; // INA3221 has no current Reg + busVoltage_LSB = INA3221_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA3221_SHUNT_VOLTAGE_LSB; + current_LSB = 0; // INA3221 has no current reg. + power_LSB = 0; // INA3221 has no power reg. + if (type == INA3221_1) { + busVoltageRegister += 2; // Reg for 2nd bus voltage + shuntVoltageRegister += 2; // Reg for 2nd shunt voltage + } else { + if (type == INA3221_2) { + busVoltageRegister += 4; // Reg for 3rd bus voltage + shuntVoltageRegister += 4; // Reg for 3rd shunt voltage + } // of if-then INA322_2 + } // of if-then-else INA3221_1 + break; + } // of switch type +} // of constructor +INA_Class::INA_Class(uint8_t expectedDevices) : _expectedDevices(expectedDevices) { + /*! +@brief Class constructor +@details If called without a parameter or with a 0 value, then the constructor does nothing, + but if a value is passed then using EEPROM is disabled and each INA-Device found + has its data (inaEEPROM structure size) stored in a array dynamically allocated during + library instatiation here. If there is not enough space then the pointer isn't init- + ialized and the program will abort later on. No error checking can be done here +@param[in] expectedDevices Number of elements to initialize array to if non-zero +*/ + if (_expectedDevices) { + _DeviceArray = new inaEEPROM[_expectedDevices]; + } // if-then use memory rather than EEPROM +} // of class constructor +INA_Class::~INA_Class() { + /*! + @brief Class destructor + @details If dynamic memory has been allocated for device storage rather than the default EEPROM, + then that memory is freed here; otherwise the destructor does nothing + */ + if (_expectedDevices) { delete[] _DeviceArray; } // if-then use memory rather than EEPROM +} // of class destructor +int16_t INA_Class::readWord(const uint8_t addr, const uint8_t deviceAddress) const { + /*! @brief Read one word (2 bytes) from the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to get the return data ready + @param[in] addr I2C address to read from + @param[in] deviceAddress Address on the I2C device to read from + @return integer value read from the I2C device */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to read + Wire.endTransmission(); // Close transmission + delayMicroseconds(I2C_DELAY); // delay required for sync + Wire.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes + return ((uint16_t)Wire.read() << 8) | Wire.read(); +} // of method readWord() +int32_t INA_Class::read3Bytes(const uint8_t addr, const uint8_t deviceAddress) const { + /*! @brief Read 3 bytes from the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to get the return data ready + @param[in] addr I2C address to read from + @param[in] deviceAddress Address on the I2C device to read from + @return integer value read from the I2C device */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to read + Wire.endTransmission(); // Close transmission + delayMicroseconds(I2C_DELAY); // delay required for sync + Wire.requestFrom(deviceAddress, (uint8_t)3); // Request 3 consecutive bytes + return ((uint32_t)Wire.read() << 16) | ((uint32_t)Wire.read() << 8) | ((uint32_t)Wire.read()); +} // of method readWord() +void INA_Class::writeWord(const uint8_t addr, const uint16_t data, + const uint8_t deviceAddress) const { + /*! @brief Write 2 bytes to the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to process the data + @param[in] addr I2C address to write to + @param[in] data 2 Bytes to write to the device + @param[in] deviceAddress Address on the I2C device to write to */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to write + Wire.write((uint8_t)(data >> 8)); // Write the first (MSB) byte + Wire.write((uint8_t)data); // and then the second byte + Wire.endTransmission(); // Close transmission and actually send data + delayMicroseconds(I2C_DELAY); // delay required for sync +} // of method writeWord() +void INA_Class::readInafromEEPROM(const uint8_t deviceNumber) { + /*! @brief Read INA device information from EEPROM + @details Retrieve the stored information for a device from EEPROM. Since this method is + private and access is controlled, no range error checking is performed + @param[in] deviceNumber Index to device array */ + if (deviceNumber == _currentINA || deviceNumber > _DeviceCount) return; // Skip if correct device +#if LIB_INA_EEPROM + if (_expectedDevices == 0) { +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || (__STM32F1__) + #ifdef __STM32F1__ // STM32F1 has no built-in EEPROM + uint16_t e = deviceNumber * sizeof(inaEE); // it uses flash memory to emulate + uint16_t *ptr = (uint16_t *)&inaEE; // "EEPROM" calls are uint16_t type + for (uint8_t n = sizeof(inaEE) + _EEPROM_offset; n; --n) // Implement EEPROM.get template + { + EEPROM.read(e++, ptr++); // for ina (inaDet type) + } // of for-next each byte + #else + EEPROM.get(_EEPROM_offset + (deviceNumber * sizeof(inaEE)), inaEE); // Read EEPROM values + #endif +#else + inaEE = _EEPROMEmulation[deviceNumber]; +#endif + } else +#endif // if LIB_INA_EEPROM + { + inaEE = _DeviceArray[deviceNumber]; + } // if-then-else use EEPROM + _currentINA = deviceNumber; + ina = inaEE; // see inaDet constructor +} // of method readInafromEEPROM() +void INA_Class::writeInatoEEPROM(const uint8_t deviceNumber) { + /*! @brief Write INA device information to EEPROM + @details Write the stored information for a device from EEPROM. Since this method is + private and access is controlled, no range error checking is performed + @param[in] deviceNumber Index to device array */ + inaEE = ina; // only save relevant part of ina to EEPROM +#if LIB_INA_EEPROM + if (_expectedDevices == 0) { +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || (__STM32F1__) + #ifdef __STM32F1__ // STM32F1 has no built-in EEPROM + uint16_t e = deviceNumber * sizeof(inaEE); // it uses flash memory to emulate + const uint16_t *ptr = (const uint16_t *)&inaEE; // "EEPROM" calls are uint16_t type + for (uint8_t n = sizeof(inaEE) + _EEPROM_offset; n; --n) // Implement EEPROM.put template + { + EEPROM.update(e++, *ptr++); // for ina (inaDet type) + } // for-next + #else + EEPROM.put(_EEPROM_offset + (deviceNumber * sizeof(inaEE)), inaEE); // Write the structure + #ifdef ESP32 + EEPROM.commit(); // Force write to EEPROM when ESP32 + #endif + #endif +#else + _EEPROMEmulation[deviceNumber] = inaEE; +#endif + } else +#endif // if LIB_INA_EEPROM + { + _DeviceArray[deviceNumber] = inaEE; + } // if-then-else use EEPROM to store data +} // of method writeInatoEEPROM() +void INA_Class::setI2CSpeed(const uint32_t i2cSpeed) const { + /*! @brief Set a new I2C speed + @details I2C allows various bus speeds, see the enumerated type I2C_MODES for the standard + speeds. The valid speeds are 100KHz, 400KHz, 1MHz and 3.4MHz. Default to 100KHz + when not specified. No range checking is done. + @param[in] i2cSpeed [optional] changes the I2C speed to the rate specified in Herz */ + // Wire.setClock(i2cSpeed); // Ignore, I2C speed set on ESPEasy Hardware page +} // of method setI2CSpeed +uint8_t INA_Class::begin(const uint16_t maxBusAmps, const uint32_t microOhmR, + const uint8_t deviceNumber) { + /*! @brief Initializes the contents of the class + @details Searches for possible devices and sets the INA Configuration details, without + which meaningful readings cannot be made. If it is called without the optional + deviceNumber parameter then the settings are applied to all devices, otherwise + just that specific device is targeted. If the optional third parameter, devNo, is + specified that specific device gets the two specified values set for it. Can be + called multiple times, but the 3 parameter version will only function after the 2 + parameter version finds all devices.\n + @param[in] maxBusAmps Integer value holding the maximum expected bus amperage, this value is + used to compute a device's internal power register + @param[in] microOhmR Shunt resistance in micro-ohms, this value is used to compute a + device's internal power register + @param[in] deviceNumber Device number to explicitly set the maxBusAmps and microOhmR values, + by default all devices found get set to the same initial values for these 2 params + @return The integer number of INAxxxx devices found on the I2C bus + */ + uint16_t originalRegister, tempRegister; + if (_DeviceCount == 0) // Enumerate all devices on first call + { + uint16_t maxDevices = 32; +/*************************************************************************************************** +** The AVR devices need to use EEPROM to save memory, some other devices have emulation for EEPROM** +** functionality while some devices have no such function calls. This library caters for these ** +** differences, with specialized calls for those platforms which have EEPROM calls and it makes ** +** the assumption that if the platform has no EEPROM call then it has sufficient RAM available at ** +** runtime to allocate sufficient space for 32 devices. ** +***************************************************************************************************/ +#if defined(ESP32) || defined(ESP8266) + #if LIB_INA_EEPROM + EEPROM.begin(_EEPROM_size + _EEPROM_offset); // If ESP32 then allocate 512 Bytes + #endif // if LIB_INA_EEPROM + maxDevices = (_EEPROM_size) / sizeof(inaEE); // and compute number of devices +#elif defined(__STM32F1__) // Emulated EEPROM for STM32F1 + maxDevices = (EEPROM.maxcount() - _EEPROM_offset) / sizeof(inaEE); // Compute max possible +#elif defined(CORE_TEENSY) // TEENSY doesn't have EEPROM.length + maxDevices = (2048 - _EEPROM_offset) / sizeof(inaEE); // defined, so use 2Kb as value +#elif defined(__AVR__) + maxDevices = (EEPROM.length() - _EEPROM_offset) / sizeof(inaEE); // Compute max possible +#else + maxDevices = 32; +#endif + // Wire.begin(); // I2C is already started in ESPEasy core + + if (maxDevices > 255) // Limit number of devices to an 8-bit number + { + maxDevices = 255; + } // of if-then more than 255 devices possible + for (uint8_t deviceAddress = 0x40; deviceAddress <= 0x4F; + deviceAddress++) // Loop for each I2C addr + { + Wire.beginTransmission(deviceAddress); + uint8_t good = Wire.endTransmission(); + if (good == 0 && _DeviceCount < maxDevices) // If no error and EEPROM has space + { + originalRegister = readWord(INA_CONFIGURATION_REGISTER, deviceAddress); // Save settings + writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, deviceAddress); // Force reset + tempRegister = readWord(INA_CONFIGURATION_REGISTER, deviceAddress); // Read reset reg. + if (tempRegister == INA_RESET_DEVICE) // If the register wasn't reset then not an INA + { + writeWord(INA_CONFIGURATION_REGISTER, originalRegister, deviceAddress); // restore value + } else { + if (tempRegister == 0x399F) { + inaEE.type = INA219; + } else { + if (tempRegister == 0x4127) // INA226, INA230, INA231 + { + tempRegister = readWord(INA_DIE_ID_REGISTER, deviceAddress); // Read the INA high-reg + if (tempRegister == INA226_DIE_ID_VALUE) { + inaEE.type = INA226; + } else { + if (tempRegister != 0) { + inaEE.type = INA230; + } else { + inaEE.type = INA231; + } // of if-then-else a INA230 or INA231 + } // of if-then-else an INA226 + } else { + if (tempRegister == 0x6127) { + inaEE.type = INA260; + } else { + if (tempRegister == 0x7127) { + inaEE.type = INA3221_0; + } else { + if (tempRegister == 0x0) { + inaEE.type = INA228; + } else { + inaEE.type = INA_UNKNOWN; + } // of if-then-else it is an INA228 + } // of if-then-else it is an INA3221 + } // of if-then-else it is an INA260 + } // of if-then-else it is an INA226, INA230, INA231 + } // of if-then-else it is an INA209, INA219, INA220 + if (inaEE.type != INA_UNKNOWN) // Increment device if valid INA2xx + { + inaEE.address = deviceAddress; + inaEE.maxBusAmps = maxBusAmps > 1022 ? 1022 : maxBusAmps; // Clamp to maximum of 1022A + inaEE.microOhmR = microOhmR; + ina = inaEE; // see inaDet constructor + if (inaEE.type == INA3221_0) { + ina.type = INA3221_0; // Set to INA3221 1st channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + ina.type = INA3221_1; // Set to INA3221 2nd channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + ina.type = INA3221_2; // Set to INA3221 3rd channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + } else { + initDevice(_DeviceCount); // perform initialization on device + _DeviceCount = ((_DeviceCount + 1) % maxDevices); // start again at 0 if overflow + } // of if-then inaEE.type + } // of if-then we can add device + } // of if-then-else we have an INA-Type device + } // of if-then we have a device + } // for-next each possible I2C address + } else { + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + ina.maxBusAmps = maxBusAmps > 1022 ? 1022 : maxBusAmps; // Clamp to maximum of 1022A + ina.microOhmR = microOhmR; + initDevice(deviceNumber); + } // of if-then-else first call + _currentINA = UINT8_MAX; // Force read on next call + return _DeviceCount; +} // of method begin() +void INA_Class::initDevice(const uint8_t deviceNumber) { + /*! @brief Initializes the the given devices using the settings from the internal structure + @details This includes (re)computing the device's calibration values. + @param[in] deviceNumber Device number to explicitly initialize. */ + ina.operatingMode = INA_DEFAULT_OPERATING_MODE; // Default to continuous mode + writeInatoEEPROM(deviceNumber); // Store the structure to EEPROM + uint8_t programmableGain; // work variable for the programmable gain + uint16_t calibration, maxShuntmV, tempRegister; // Calibration temporary variables + switch (ina.type) { + case INA219: // Set up INA219 or INA220 + // Compute calibration register + calibration = (uint64_t)409600000 / + ((uint64_t)ina.current_LSB * (uint64_t)ina.microOhmR / (uint64_t)100000); + writeWord(INA_CALIBRATION_REGISTER, calibration, ina.address); // Write calibration + /* Determine optimal programmable gain with maximum accuracy so no chance of an overflow */ + maxShuntmV = ina.maxBusAmps * ina.microOhmR / 1000; // Compute maximum shunt mV + if (maxShuntmV <= 40) + programmableGain = 0; // gain x1 for +- 40mV + else if (maxShuntmV <= 80) + programmableGain = 1; // gain x2 for +- 80mV + else if (maxShuntmV <= 160) + programmableGain = 2; // gain x4 for +- 160mV + else + programmableGain = 3; // dflt gain x8 for +- 320mV + tempRegister = 0x399F & INA219_CONFIG_PG_MASK; // Zero programmable gain + tempRegister |= programmableGain << INA219_PG_FIRST_BIT; // Overwrite the new values + bitSet(tempRegister, INA219_BRNG_BIT); // set to 1 for 0-32 volts + writeWord(INA_CONFIGURATION_REGISTER, tempRegister, ina.address); // Write to config register + break; + case INA226: + case INA230: + case INA231: + // Compute calibration register + calibration = (uint64_t)51200000 / + ((uint64_t)ina.current_LSB * (uint64_t)ina.microOhmR / (uint64_t)100000); + writeWord(INA_CALIBRATION_REGISTER, calibration, ina.address); // Write calibration + break; + case INA260: + case INA3221_0: + case INA3221_1: + case INA3221_2: break; + } // of switch type +} // of method initDevice() +void INA_Class::setBusConversion(const uint32_t convTime, const uint8_t deviceNumber) { + /*! @brief specifies the conversion rate in microseconds, rounded to the nearest valid value + @details INA devices can have a conversion rate of up to 68100 microseconds + @param[in] convTime The conversion time in microseconds, invalid values are rounded to the + nearest valid value + @param[in] deviceNumber [optional] When specified, only that specified device number gets + changed, otherwise all devices are set to the same averaging rate + */ + uint16_t configRegister; + int16_t convRate; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber % _DeviceCount == i) // If device needs setting + { + readInafromEEPROM(i); // Load EEPROM values to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current register + switch (ina.type) { + case INA219: + if (convTime >= 68100) + convRate = 15; + else if (convTime >= 34050) + convRate = 14; + else if (convTime >= 17020) + convRate = 13; + else if (convTime >= 8510) + convRate = 12; + else if (convTime >= 4260) + convRate = 11; + else if (convTime >= 2130) + convRate = 10; + else if (convTime >= 1060) + convRate = 9; + else if (convTime >= 532) + convRate = 8; + else if (convTime >= 276) + convRate = 2; + else if (convTime >= 148) + convRate = 1; + else + convRate = 0; + configRegister &= ~INA219_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 7; // shift in the BADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (convTime >= 8244) + convRate = 7; + else if (convTime >= 4156) + convRate = 6; + else if (convTime >= 2116) + convRate = 5; + else if (convTime >= 1100) + convRate = 4; + else if (convTime >= 588) + convRate = 3; + else if (convTime >= 332) + convRate = 2; + else if (convTime >= 204) + convRate = 1; + else + convRate = 0; + if (ina.type == INA226 || ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) { + configRegister &= ~INA226_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 6; // shift in averages + } else { + configRegister &= ~INA260_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 7; // shift in the averages + } // of if-then an INA226 or INA260 + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, + ina.address); // Save new value to device + } // of if this device needs to be set + } // for-next each device loop +} // of method setBusConversion() +void INA_Class::setShuntConversion(const uint32_t convTime, const uint8_t deviceNumber) { + /*! @brief specifies the conversion rate in microseconds, rounded to the nearest valid value + @details INA devices can have a conversion rate of up to 68100 microseconds + @param[in] convTime Conversion time in microseconds. Out-of-Range values are set to the + closest valid value + @param[in] deviceNumber to return the device name for[optional] When specified, only that + specified device number gets changed, otherwise all devices are set to the same + averaging rate + */ + int16_t configRegister, convRate; + for (uint8_t i = 0; i < _DeviceCount; i++) { // Loop for each device found + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get register contents + switch (ina.type) { + case INA219: + if (convTime >= 68100) + convRate = 15; + else if (convTime >= 34050) + convRate = 14; + else if (convTime >= 17020) + convRate = 13; + else if (convTime >= 8510) + convRate = 12; + else if (convTime >= 4260) + convRate = 11; + else if (convTime >= 2130) + convRate = 10; + else if (convTime >= 1060) + convRate = 9; + else if (convTime >= 532) + convRate = 8; + else if (convTime >= 276) + convRate = 2; + else if (convTime >= 148) + convRate = 1; + else + convRate = 0; + configRegister &= ~INA219_CONFIG_SADC_MASK; // zero out the averages part + configRegister |= convRate << 3; // shift in the SADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (convTime >= 8244) + convRate = 7; + else if (convTime >= 4156) + convRate = 6; + else if (convTime >= 2116) + convRate = 5; + else if (convTime >= 1100) + convRate = 4; + else if (convTime >= 588) + convRate = 3; + else if (convTime >= 332) + convRate = 2; + else if (convTime >= 204) + convRate = 1; + else + convRate = 0; + if (ina.type == INA226 || ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) { + configRegister &= ~INA226_CONFIG_SADC_MASK; // zero out the averages part + } else { + configRegister &= ~INA260_CONFIG_SADC_MASK; // zero out the averages part + } // of if-then-else either INA226/INA3221 or a INA260 + configRegister |= convRate << 3; // shift in the averages to register + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, + ina.address); // Save new value to device + } // of if this device needs to be set + } // for-next each device loop +} // of method setShuntConversion() +const char *INA_Class::getDeviceName(const uint8_t deviceNumber) { + /*! @brief returns character buffer with the name of the device specified in the input param + @details See function definition for list of possible return values + @param[in] deviceNumber to return the device name of + @return device name */ + if (deviceNumber > _DeviceCount) return (""); + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + switch (ina.type) { + case INA219: return ("INA219"); + case INA226: return ("INA226"); + case INA228: return ("INA228"); + case INA230: return ("INA230"); + case INA231: return ("INA231"); + case INA260: return ("INA260"); + case INA3221_0: + case INA3221_1: + case INA3221_2: return ("INA3221"); + default: return ("UNKNOWN"); + } // of switch type +} // of method getDeviceName() +uint8_t INA_Class::getDeviceType(const uint8_t deviceNumber) { + /*! @brief returns uint8_t type of the device specified in the input param + @details See function definition for list of possible return values + @param[in] deviceNumber to return the device name of + @return device type */ + if (deviceNumber > _DeviceCount) return INA_UNKNOWN; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + return ina.type; +} // of method getDeviceType() +uint8_t INA_Class::getDeviceAddress(const uint8_t deviceNumber) { + /*! @brief returns a I2C address of the device specified in the input parameter + @details Return the I2C address of the specified device, if number is out of range return 0 + @param[in] deviceNumber to return the device name of + @return I2C address of the device. Returns 0 if value is out-of-range + */ + if (deviceNumber > _DeviceCount) return 0; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + return (ina.address); +} // of method getDeviceAddress() +uint16_t INA_Class::getBusMilliVolts(const uint8_t deviceNumber) { + /*! @brief returns the bus voltage in millivolts + @details The converted millivolt value is returned and if the device is in triggered mode + the next conversion is started + @param[in] deviceNumber to return the device bus millivolts for + @return uint16_t unsigned integer for the bus millivoltage */ + uint32_t busVoltage = getBusRaw(deviceNumber); // Get raw voltage from device + if (ina.type == INA228) { + // The accuracy is 20bits and 195.3125uv is the LSB + busVoltage = (uint64_t)busVoltage * 1953125 / 10000000; // conversion to get mV + } else { + busVoltage = busVoltage * ina.busVoltage_LSB / 100; // conversion to get mV + } // if-then-else an INA228 + return (busVoltage); +} // of method getBusMilliVolts() +uint32_t INA_Class::getBusRaw(const uint8_t deviceNumber) { + /*! @brief returns the raw unconverted bus voltage reading from the device + @details The raw measured value is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the raw device bus voltage reading + @return Raw bus measurement */ + readInafromEEPROM(deviceNumber); // Load EEPROM from EEPROM + uint32_t raw{0}; // define the return variable + if (ina.type == INA228) { + raw = read3Bytes(ina.busVoltageRegister, ina.address); // Get the raw value from register + raw = raw >> 4; + } else { + raw = readWord(ina.busVoltageRegister, ina.address); // Get the raw value from register + if (ina.type == INA3221_0 || ina.type == INA3221_1 || ina.type == INA3221_2 || + ina.type == INA219) { + raw = raw >> 3; // INA219 & INA3221 - the 3 LSB unused, so shift right + } // of if-then an INA219 or INA3221 + } // if-then a 3byte bus voltage buffer + if (!bitRead(ina.operatingMode, 2) && bitRead(ina.operatingMode, 1)) // Triggered & bus active + { + int16_t configRegister = + readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current value + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Write to trigger next + } // of if-then triggered mode enabled + return (raw); +} // of method getBusRaw() +int32_t INA_Class::getShuntMicroVolts(const uint8_t deviceNumber) { + /*! @brief returns the shunt reading converted to microvolts + @details The computed microvolts value is returned and if the device is in triggered mode + the next conversion is started + @param[in] deviceNumber to return the value for + @return int32_t signed integer for the shunt microvolts + */ + int32_t shuntVoltage = getShuntRaw(deviceNumber); + if (ina.type == INA260) // INA260 has a built-in shunt + { + int32_t busMicroAmps = getBusMicroAmps(deviceNumber); // Get the amps on the bus from device + shuntVoltage = busMicroAmps / 200; // 2mOhm resistor, convert with Ohm's law + } else { + if (ina.type == INA228) { + shuntVoltage = shuntVoltage * ina.shuntVoltage_LSB / 10; // Convert to microvolts + } else { + shuntVoltage = shuntVoltage * ina.shuntVoltage_LSB / 10; // Convert to microvolts + } // if-then a INA228 with 20 bit accuracy + } // of if-then-else an INA260 + return (shuntVoltage); +} // of method getShuntMicroVolts() +int32_t INA_Class::getShuntRaw(const uint8_t deviceNumber) { + /*! @brief Returns the raw shunt reading + @details The raw reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return Raw shunt reading */ + int32_t raw; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + if (ina.type == INA260) // INA260 has a built-in shunt + { + int32_t busMicroAmps = getBusMicroAmps(deviceNumber); // Get the amps on the bus + raw = busMicroAmps / 200 / 1000; // 2mOhm resistor, apply Ohm's law + } else { + if (ina.type == INA228) // INA228 has 24 bit accuracy + { + raw = read3Bytes(ina.shuntVoltageRegister, ina.address); // Get the raw value from register + // The number is two's complement, so if negative we need to pad when shifting // + if (raw & 0x800000) { + raw = (raw >> 4) | 0xFFF00000; // first 12 bits are "1" + } else { + raw = raw >> 4; + } // if-then negative + } else { + raw = readWord(ina.shuntVoltageRegister, ina.address); // Get the raw value from register + } // if-then a 24 bit register + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't use 3 LSB + { + raw = raw >> 3; // shift over 3 bits, datatype is "int" so shifts in sign bits + } // of if-then we need to shift INA3221 reading over + } // of if-then-else an INA260 with inbuilt shunt + if (!bitRead(ina.operatingMode, 2) && bitRead(ina.operatingMode, 0)) // Triggered & shunt active + { + int16_t configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current reg + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Write to trigger next + } // of if-then triggered mode enabled + return (raw); +} // of method getShuntMicroVolts() +int32_t INA_Class::getBusMicroAmps(const uint8_t deviceNumber) { + /*! @brief Returns the computed microamps measured on the bus for the specified device + @details The computed reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return int32_t signed integer for computed microamps on the bus */ + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + int32_t microAmps = 0; + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't compute Amps + { + microAmps = + (int64_t)getShuntMicroVolts(deviceNumber) * (int64_t)1000000 / (int64_t)ina.microOhmR; + } else { + microAmps = (int64_t)readWord(ina.currentRegister, ina.address) * (int64_t)ina.current_LSB / + (int64_t)1000; + } // of if-then-else an INA3221 + return (microAmps); +} // of method getBusMicroAmps() +int64_t INA_Class::getBusMicroWatts(const uint8_t deviceNumber) { + /*! + @brief returns the computed microwatts measured on the bus for the specified device + @details The computed reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return int64_t signed integer for computed microwatts on the bus + */ + int64_t microWatts = 0; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't compute Amps + { + microWatts = + ((int64_t)getShuntMicroVolts(deviceNumber) * (int64_t)1000000 / (int64_t)ina.microOhmR) * + (int64_t)getBusMilliVolts(deviceNumber) / (int64_t)1000; + } else { + microWatts = + (int64_t)readWord(INA_POWER_REGISTER, ina.address) * (int64_t)ina.power_LSB / (int64_t)1000; + if (getShuntRaw(deviceNumber) < 0) microWatts *= -1; // Invert if negative voltage + } // of if-then-else an INA3221 + return (microWatts); +} // of method getBusMicroWatts() +void INA_Class::reset(const uint8_t deviceNumber) { + /*! @brief performs a software reset for the specified device + @details If no device is specified, then all devices are reset + @param[in] deviceNumber to reset */ + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, ina.address); // Set MSB to reset + initDevice(i); // re-initialize device + } // of if this device needs to be set + } // for-next each device loop +} // of method reset +void INA_Class::setMode(const uint8_t mode, const uint8_t deviceNumber) { + /*! + @brief sets the operating mode from the list given in enum type "ina_Mode" for a device + @details If no device is specified, then all devices are set to the given mode + @param[in] mode Mode (see "ina_Mode" enumerated type for list of valid values + @param[in] deviceNumber to reset (Optional, when not set then all devices are mode changed) + */ + int16_t configRegister; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current config + configRegister &= ~INA_CONFIG_MODE_MASK; // zero out mode bits + ina.operatingMode = 0b00000111 & mode; // Mask off unused bits + writeInatoEEPROM(i); // Store back to EEPROM + configRegister |= ina.operatingMode; // shift mode settings + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Save new value + } // if-then this device needs to be set + } // for-next each device loop +} // of method setMode() +bool INA_Class::conversionFinished(const uint8_t deviceNumber) { + /*! + @brief Returns whether or not the conversion has completed + @details The device's conversion ready bit is read and returned. "true" denotes finished + conversion. + @param[in] deviceNumber to check + */ + if (_DeviceCount == 0) return false; // Return finished if invalid device. Issue #65 + readInafromEEPROM(deviceNumber % _DeviceCount); // Load EEPROM to ina structure + uint16_t cvBits = 0; + switch (ina.type) { + case INA219: + cvBits = readWord(INA_BUS_VOLTAGE_REGISTER, ina.address) & 2; // Bit 2 set denotes ready + readWord(INA_POWER_REGISTER, ina.address); // Resets the "ready" bit + break; + case INA226: + case INA230: + case INA231: + case INA260: cvBits = readWord(INA_MASK_ENABLE_REGISTER, ina.address) & (uint16_t)8; break; + case INA3221_0: + case INA3221_1: + case INA3221_2: cvBits = readWord(INA3221_MASK_REGISTER, ina.address) & (uint16_t)1; break; + default: cvBits = 1; + } // of switch type + return cvBits != 0; +} // of method "conversionFinished()" +void INA_Class::waitForConversion(const uint8_t deviceNumber) { + /*! + @brief will not return until the conversion for the specified device is finished + @details if no device number is specified it will wait until all devices have finished their + current conversion. If the conversion has completed already then the flag (and + interrupt pin, if activated) is also reset. + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + */ + uint16_t cvBits = 0; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + cvBits = 0; + while (cvBits == 0) // Loop until the value is set + { + switch (ina.type) { + case INA219: + cvBits = + readWord(INA_BUS_VOLTAGE_REGISTER, ina.address) & 2; // Bit 2 set denotes ready + readWord(INA_POWER_REGISTER, ina.address); // Resets the "ready" bit + break; + case INA226: + case INA230: + case INA231: + case INA260: + cvBits = readWord(INA_MASK_ENABLE_REGISTER, ina.address) & (uint16_t)8; + break; + case INA3221_0: + case INA3221_1: + case INA3221_2: + cvBits = readWord(INA3221_MASK_REGISTER, ina.address) & (uint16_t)1; + break; + default: cvBits = 1; + } // of switch type + } // of while the conversion hasn't finished + } // of if this device needs to be set + } // for-next each device loop +} // of method waitForConversion() +bool INA_Class::alertOnConversion(const bool alertState, const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when a conversion is complete + @details This call is ignored and returns false when called for an invalid device as the INA219 + doesn't have this pin it won't work for that device. + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = false; // Assume the worst + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + case INA260: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) bitSet(alertRegister, INA_ALERT_CONVERSION_RDY_BIT); // Turn on the bit + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write back + returnCode = true; + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnConversion +bool INA_Class::alertOnShuntOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the shunt current exceeds the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = false; // Assume the worst + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber == i) // If this device needs to be processed + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // If true, then also set threshold + { + bitSet(alertRegister, INA_ALERT_SHUNT_OVER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 1000 / ina.shuntVoltage_LSB; // Compute using LSB + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + returnCode = true; + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnShuntOverVoltage +bool INA_Class::alertOnShuntUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the shunt current goes below the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false */ + uint16_t alertRegister; + bool returnCode = true; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_SHUNT_UNDER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 1000 / ina.shuntVoltage_LSB; // Compute using LSB + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnShuntUnderVoltage +bool INA_Class::alertOnBusOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the bus voltage goes above the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = true; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: // Devices that have an alert pin + case INA230: + case INA231: + case INA260: + alertRegister = + readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get the current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_BUS_OVER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 100 / ina.busVoltage_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnBusOverVoltageConversion +bool INA_Class::alertOnBusUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin + low when the bus current goes above the value given in the parameter in millivolts. + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set then all devices have their alert + changed) + @return Returns "true" on success, otherwise false */ + uint16_t alertRegister; + bool returnCode = true; // Assume success + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: // Devices that have an alert pin + case INA230: + case INA231: + case INA260: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_BUS_UNDER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 100 / ina.busVoltage_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnBusUnderVoltage +bool INA_Class::alertOnPowerOverLimit(const bool alertState, const int32_t milliAmps, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin + low when the power exceeds the value set in the parameter in milliamps + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliAmps alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = true; // assume success + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + case INA260: // Devices with alert pin + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_POWER_OVER_WATT_BIT); // Turn on the bit + uint16_t threshold = milliAmps * 1000000 / ina.power_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnPowerOverLimit +void INA_Class::setAveraging(const uint16_t averages, const uint8_t deviceNumber) { + /*! + @brief sets the hardware averaging for one or all devices + @details Out-of-Range averaging is brought down to the highest allowed value + @param[in] averages Number of averages to set (0-128) + @param[in] deviceNumber to reset (Optional, when not set all devices have their averaging changed) + */ + uint16_t averageIndex; + int16_t configRegister; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to struct + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current register + switch (ina.type) { + case INA219: + if (averages >= 128) + averageIndex = 15; + else if (averages >= 64) + averageIndex = 14; + else if (averages >= 32) + averageIndex = 13; + else if (averages >= 16) + averageIndex = 12; + else if (averages >= 8) + averageIndex = 11; + else if (averages >= 4) + averageIndex = 10; + else if (averages >= 2) + averageIndex = 9; + else + averageIndex = 8; + configRegister &= ~INA219_CONFIG_AVG_MASK; // zero out the averages part + configRegister |= averageIndex << 3; // shift in the SADC averages + configRegister |= averageIndex << 7; // shift in the BADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (averages >= 1024) + averageIndex = 7; + else if (averages >= 512) + averageIndex = 6; + else if (averages >= 256) + averageIndex = 5; + else if (averages >= 128) + averageIndex = 4; + else if (averages >= 64) + averageIndex = 3; + else if (averages >= 16) + averageIndex = 2; + else if (averages >= 4) + averageIndex = 1; + else + averageIndex = 0; + configRegister &= ~INA226_CONFIG_AVG_MASK; // zero out the averages part + configRegister |= averageIndex << 9; // shift in the averages to reg + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Save new value + } // of if this device needs to be set + } // for-next each device loop +} // of method setAveraging() diff --git a/lib/INA/src/INA.h b/lib/INA/src/INA.h new file mode 100644 index 000000000..946ae6a7f --- /dev/null +++ b/lib/INA/src/INA.h @@ -0,0 +1,351 @@ +#pragma once + +// clang-format off +/*! + @file INA.h + + @brief INA Class library header file + + @mainpage Arduino library to support the INAxxx family of current sensors + + @section Library_intro_section Description + + Class definition header for the INA class. This library gives a common interface to various INA + power monitor devices, see https://github.com/Zanduino/INA/wiki or the code below for a full + list of currently supported devices. The INA devices have a 3-5V power supply and, depending + upon the model, can measure voltages up to 26V or 36V. They are devices with High-Side / Low-Side + Measurement, Bi-Directional Current and Power Monitor with I2C Compatible Interface. The device + documentation can be found at the following location:\n + http://www.ti.com/amplifier-circuit/current-sense/power-current-monitors/products.html\n\n + Detailed library descriptions are on the INA GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki\n\n The INA devices, apart from the INA250 and INA260, + require an external shunt of known resistance to be placed across the high-side or low-side + supply or ground line and they use the small current generated by the shunt to compute the + amperage passing across the circuit. This value, coupled with the voltage measurement, allows + the amperage and wattage to be computed by the INA device and these values can be read from the + devices using the industry standard I2C protocol. + + @section Style Programming + @subsection Coding Coding and comments + OK, I admit that I'm "old school" when it comes to programming style. I am used to using a full + monitor and keyboard for development and testing, plus I like to heavily document code as it + helps me remember what I did when I revisit code after several months (or years). I make use of + the full width (which I've limited to 112 characters here) and put my comments at the end of + lines. I prefer to use descriptive variable names, which means that they tend to be long. + + @subsection StyleGuide Style Guide + Different languages have different coding styles. For the Arduino c++ language I've opted to go + with one of the big players in the industry and have adopted the coding and style rules that + Google recommends and which are documented at [Google c++ Style + Guide](https://google.github.io/styleguide/cppguide.html). I have chosen to put braces on their + own lines and include braces for even 1-liners. End braces are always commented so that + convoluted code is more easily untangled. + + @subsection Documentation + The comments have been formatted for use with [Doxygen](doxygen.nl), one of the leading + documentation systems which is not only free but covers just about anything worth documenting. + The Doxygen system parses the source files of a package and creates documentation. The default + output is a set of HTML pages although it can produce single documents. + + @subsection comments Comment Format + This package uses [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax for formatting + comments, which makes for easy reading directly in the source code and well-formatted + pretty-print in postprocessing. + + @subsection ide IDE + I've opted for using Microsoft Visual Studio (version 16.2.5) for development, the community + version can be downloaded for free at [Microsoft + Downloads](https://visualstudio.microsoft.com/downloads/) and I use the fantastic [Visual + Micro](https://www.visualmicro.com/) package which give the Arduino IDE inside Visual Studio. The + base version is free or becomes only slightly annoying nagware after 90 days, but the software is + inexpensive and the price is well worth it for supporting continued development. + + @section doxygen doxygen configuration + + This library is built with the standard "Doxyfile", which is located at + https://github.com/Zanduino/Common/blob/main/Doxygen. As described on that page, there are only 5 + environment variables used, and these are set in the project's actions file, located at + https://github.com/Zanduino/INA/blob/master/.github/workflows/ci-doxygen.yml + Edit this file and set the 5 variables - PRETTYNAME, PROJECT_NAME, PROJECT_NUMBER, PROJECT_BRIEF + and PROJECT_LOGO so that these values are used in the doxygen documentation. + The local copy of the doxyfile should be in the project's root directory in order to do local + doxygen testing, but the file is ignored on upload to GitHub. + + @section clang-format + Part of the GitHub actions for CI is running every source file through "clang-format" to ensure + that coding formatting is done the same for all files. The configuration file ".clang-format" is + located at https://github.com/Zanduino/Common/tree/main/clang-format and this is used for CI tests + when pushing to GitHub. The local file, if present in the root directory, is ignored when + committing and uploading. + + @section license GNU General Public License v3.0 + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU + General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should + have received a copy of the GNU General Public License along with this program. If not, see + . + +@section author Author + +Written by Arnd at https://www.github.com/SV-Zanshin + +@section versions Changelog + +| Version | Date | Developer | Comments +| ------- | ---------- | ----------- | -------- +| | 2025-12-24 | tonhuisman | Disable reading/writing EEPROM for ESPEasy via compile flag LIB_INA_EEPROM +| 1.1.2 | 2022-01-16 | Oleg-Sob | Issue #87. getBusMicroWatts() only returns positive values +| 1.1.1 | 2021-03-12 | x3mEr | Issue #79. Documentation Update +| 1.0.14 | 2020-12-01 | SV-Zanshin | Issue #72. Allow INA structures to be in memory rather than EEPROM +| 1.0.14 | 2020-11-30 | johntaves | Issue #64. begin() does not set values on subsequent calls +| 1.0.14 | 2020-11-29 | SV-Zanshin | Issue #71. Optimize library, cleanup source code +| 1.0.14 | 2020-10-25 | gallium70 | Issue #66. Error in INA226/230/231 value for "power_LSB" +| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Added "_EEPROM_size" for ESP32 and ESP8266 +| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Incorrect "_EEPROM_offset" computation +| 1.0.12 | 2020-07-13 | SV-Zanshin | Issue #41. Added "_EEPROM_offset" variable +| 1.0.12 | 2020-07-03 | sages | Issue #60. Possible Overflow getBus(MicroAmps,MicroWatts) +| 1.0.11 | 2020-06-30 | SV-Zanshin | Issue #58, changed formatting to use clang-format +| 1.0.11 | 2020-06-29 | SV-Zanshin | Issue #57. "Alert..." functions should be "alert..." +| 1.0.11 | 2020-05-05 | oliverb68 | Issue #56. Limit of +/- 2kW on getBusMicroWatts +| 1.0.10 | 2020-05-03 | we9v | Issue #54. Limit of 127A maximum current changed to 1022A +| 1.0.10 | 2020-05-01 | nathancheek | Issue #53. Extraneous conversion on getShuntMicrovolts +| 1.0.10 | 2020-03-24 | nathancheek | Issue #52. Search for all 16 possible devices +| 1.0.10 | 2020-03-22 | alphaarea | Issue #50. Wiki fix for "begin()" - MaxBusAmps overflow +| 1.0.9 | 2019-12-15 | Steamerzone | Issue #49. Added ifdef for STM32F1 device support +| 1.0.9 | 2019-10-27 | SV-Zanshin | Cleaned up Doxygen formatting +| 1.0.9 | 2019-10-17 | nathancheek | Issue #47. Added EEPROM support for teensy +| 1.0.8 | 2019-09-03 | miky2k | Issue #43. Added new method "conversionFinished()" +| 1.0.8 | 2019-05-23 | avaldebe | Issue #42. Restrict I2C scan to possible devices +| 1.0.8 | 2019-03-24 | mattlogic | Issue #40. Corrected INA226_CONFIG_SADC_MASK value +| 1.0.8 | 2019-03-17 | SV-Zanshin | Issue #19. Corrected 4 value ranges in bus/shunt conversion +| 1.0.8 | 2019-02-16 | SV-Zanshin | Corrected and tested ESP32 implementation +| 1.0.8 | 2019-02-10 | SV-Zanshin | Issue #39. Allow non-AVR processors without EEPROM to run +| 1.0.8 | 2019-02-09 | SV-Zanshin | Cleaned up doxygen comment formatting in .h and .cpp files +| 1.0.8 | 2019-02-09 | SV-Zanshin | Issue #38. Add getDeviceAddress() function +| 1.0.7 | 2019-01-20 | SV-Zanshin | Issue #36&37. Changed for Travis-CI and automated doxygen +| 1.0.7 | 2018-12-27 | SV-Zanshin | Issue #33. Change program documentation to doxygen format +| 1.0.6 | 2018-12-13 | delboy711 | Issue #32. Incorrect ESP2866 rather than ESP8266 +| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #31. Use full 0-32V Range on INA219 all the time +| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #30. Added TEENSY support & support large EEPROM +| 1.0.6 | 2018-10-14 | SV-Zanshin | Added correct wire handling for ESP8266 and ESP32 +| 1.0.6 | 2018-10-07 | SV-Zanshin | Optimized getBusRaw() and getShuntRaw() functions +| 1.0.5 | 2018-10-04 | SV-Zanshin | Added getBusRaw() and getShuntRaw() functions +| 1.0.5 | 2018-09-29 | SV-Zanshin | Reformatted comments to different c++ coding style +| 1.0.4 | 2018-09-22 | SV-Zanshin | Issue #27. EEPROM Calls don't work with ESP32 +| 1.0.4 | 2018-09-19 | SV-Zanshin | Issue #28. Ovef error when >31Amps specified in begin() +| 1.0.3 | 2018-09-04 | delboy711 | Issue #26. Incorrect INA3221 negative current readings +| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #22. Reduce EEPROM Footprint +| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #21. Rename I2C Constants to avoid redefine STM32F1 +| 1.0.2 | 2018-07-22 | SV-Zanshin | Issue #11. Reduce EEPROM footprint. Removed "deviceName", 8B. Changed datatype in structure to bit-level length defs, saving additional 3 bytes +| 1.0.2 | 2018-07-21 | avaldeve | Issue #12. Incorrect const datatype for I2C Speeds +| 1.0.2 | 2018-07-12 | coelner | Issue #9. Esplora doesn't accept "Wire.begin()" +| 1.0.2 | 2018-07-08 | SV-Zanshin | Issue #2. Finished testing INA3221 across all functions +| 1.0.2 | 2018-07-07 | dnlwgnd | Issue #4. Guard code used incorrect label +| 1.0.2 | 2018-06-30 | SV-Zanshin | Issue #3. Adding faster I2C bus support +| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library +| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library +| 1.0.1 | 2018-06-24 | SV-Zanshin | Removed extraneous elements from ina struct, optimized code +| 1.0.1b | 2018-06-23 | SV-Zanshin | Fixed error on multiple devices with ina structure contents +| 1.0.1a | 2018-06-23 | SV-Zanshin | Removed debug mode and code +| 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release +| 1.0.0b | 2018-06-17 | SV-Zanshin | Continued coding, tested on INA219 and INA226 +| 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding began +*/ + +#ifndef LIB_INA_EEPROM +#define LIB_INA_EEPROM 0 // disable reading/writing EEPROM (via EEPROM.h functions) +#endif // ifndef LIB_INA_EEPROM +// #ifndef ARDUINO +// /*! Define macro if not defined yet */ +// #define ARDUINO 0 +// #endif +// #if ARDUINO >= 100 /* Use old library if IDE is prior to V1.0 */ + #include "Arduino.h" +// #else +// #include "WProgram.h" +// #endif + +#ifndef INA__Class_h +/*! Guard code definition to prevent multiple includes */ +#define INA__Class_h +/*! typedef contains a packed bit-level defs of information stored per device */ +typedef struct { + uint8_t type : 4; ///< 0-15 see enumerated "ina_Type" for details + uint8_t operatingMode : 4; ///< 0-15 Default to continuous mode + uint32_t address : 7; ///< 0-127 I2C Address of device + uint32_t maxBusAmps : 10; ///< 0-1023 Store initialization value + uint32_t microOhmR : 20; ///< 0-1,048,575 Store initialization value +} inaEEPROM; // of structure +/*! typedef contains a packed bit-level definition of information stored on a device */ +typedef struct inaDet : inaEEPROM { + uint8_t busVoltageRegister : 3; ///< 0- 7, Bus Voltage Register + uint8_t shuntVoltageRegister : 3; ///< 0- 7, Shunt Voltage Register + uint8_t currentRegister : 3; ///< 0- 7, Current Register + uint16_t shuntVoltage_LSB; ///< Device dependent LSB factor + uint16_t busVoltage_LSB; ///< Device dependent LSB factor + uint32_t current_LSB; ///< Amperage LSB + uint32_t power_LSB; ///< Wattage LSB + inaDet(); ///< struct constructor + inaDet(inaEEPROM& inaEE); ///< for ina = inaEE; assignment +} inaDet; // of structure +/*! Enumerated list detailing the names of all supported INA devices. The INA3221 is stored + as 3 distinct devices each with their own enumerated type. */ +enum ina_Type { + INA219, + INA226, + INA228, + INA230, + INA231, + INA260, + INA3221_0, + INA3221_1, + INA3221_2, + INA_UNKNOWN +}; +/*! Enumerated list detailing the operating modes of a given device */ +enum ina_Mode { + INA_MODE_SHUTDOWN, ///< Device powered down + INA_MODE_TRIGGERED_SHUNT, ///< Triggered shunt, no bus + INA_MODE_TRIGGERED_BUS, ///< Triggered bus, no shunt + INA_MODE_TRIGGERED_BOTH, ///< Triggered bus and shunt + INA_MODE_POWER_DOWN, ///< shutdown or power-down + INA_MODE_CONTINUOUS_SHUNT, ///< Continuous shunt, no bus + INA_MODE_CONTINUOUS_BUS, ///< Continuous bus, no shunt + INA_MODE_CONTINUOUS_BOTH ///< Both continuous, default value +}; // of enumerated type +/************************************************************************************************ +** Declare constants used in the class ** +************************************************************************************************/ +#ifndef INA_I2C_MODES // I2C related constants +#define INA_I2C_MODES ///< Guard code to prevent multiple defs +const uint32_t INA_I2C_STANDARD_MODE{100000}; ///< Default normal I2C 100KHz speed +const uint32_t INA_I2C_FAST_MODE{400000}; ///< Fast mode +const uint32_t INA_I2C_FAST_MODE_PLUS{1000000}; ///< Really fast mode +const uint32_t INA_I2C_HIGH_SPEED_MODE{3400000}; ///< Turbo mode +#endif +const uint8_t INA_CONFIGURATION_REGISTER{0}; ///< Configuration Register address +const uint8_t INA_BUS_VOLTAGE_REGISTER{2}; ///< Bus Voltage Register address +const uint8_t INA_POWER_REGISTER{3}; ///< Power Register address +const uint8_t INA_CALIBRATION_REGISTER{5}; ///< Calibration Register address +const uint8_t INA_MASK_ENABLE_REGISTER{6}; ///< Mask enable Register (some devices) +const uint8_t INA_ALERT_LIMIT_REGISTER{7}; ///< Alert Limit Register (some devices) +const uint8_t INA_MANUFACTURER_ID_REGISTER{0xFE}; ///< Mfgr ID Register (some devices) +const uint8_t INA_DIE_ID_REGISTER{0xFF}; ///< Die ID Register (some devices) +const uint16_t INA_RESET_DEVICE{0x8000}; ///< Write to config to reset device +const uint16_t INA_CONVERSION_READY_MASK{0x0080}; ///< Bit 4 +const uint16_t INA_CONFIG_MODE_MASK{0x0007}; ///< Bits 0-3 +const uint16_t INA_ALERT_MASK{0x03FF}; ///< Mask off bits 0-9 +const uint8_t INA_ALERT_SHUNT_OVER_VOLT_BIT{15}; ///< Register bit +const uint8_t INA_ALERT_SHUNT_UNDER_VOLT_BIT{14}; ///< Register bit +const uint8_t INA_ALERT_BUS_OVER_VOLT_BIT{13}; ///< Register bit +const uint8_t INA_ALERT_BUS_UNDER_VOLT_BIT{12}; ///< Register bit +const uint8_t INA_ALERT_POWER_OVER_WATT_BIT{11}; ///< Register bit +const uint8_t INA_ALERT_CONVERSION_RDY_BIT{10}; ///< Register bit +const uint8_t INA_DEFAULT_OPERATING_MODE{0b111}; ///< Default continuous mode +const uint8_t INA219_SHUNT_VOLTAGE_REGISTER{1}; ///< INA219 Shunt Voltage Register +const uint8_t INA219_CURRENT_REGISTER{4}; ///< INA219 Current Register +const uint16_t INA219_BUS_VOLTAGE_LSB{400}; ///< INA219 LSB in uV *100 4.00mV +const uint16_t INA219_SHUNT_VOLTAGE_LSB{100}; ///< INA219 LSB in uV *10 10.0uV +const uint16_t INA219_CONFIG_AVG_MASK{0x07F8}; ///< INA219 Bits 3-6, 7-10 +const uint16_t INA219_CONFIG_PG_MASK{0xE7FF}; ///< INA219 Bits 11-12 masked +const uint16_t INA219_CONFIG_BADC_MASK{0x0780}; ///< INA219 Bits 7-10 masked +const uint16_t INA219_CONFIG_SADC_MASK{0x0038}; ///< INA219 Bits 3-5 +const uint8_t INA219_BRNG_BIT{13}; ///< INA219 Bit for BRNG in config reg +const uint8_t INA219_PG_FIRST_BIT{11}; ///< INA219 1st bit of Programmable Gain +const uint8_t INA226_SHUNT_VOLTAGE_REGISTER{1}; ///< INA226 Shunt Voltage Register +const uint8_t INA226_CURRENT_REGISTER{4}; ///< INA226 Current Register +const uint16_t INA226_BUS_VOLTAGE_LSB{125}; ///< INA226 LSB in uV *100 1.25mV +const uint16_t INA226_SHUNT_VOLTAGE_LSB{25}; ///< INA226 LSB in uV *10 2.5uV +const uint16_t INA226_CONFIG_AVG_MASK{0x0E00}; ///< INA226 Bits 9-11 +const uint16_t INA226_DIE_ID_VALUE{0x2260}; ///< INA226 Hard-coded Die ID for INA226 +const uint16_t INA226_CONFIG_BADC_MASK{0x01C0}; ///< INA226 Bits 6-8 masked +const uint16_t INA226_CONFIG_SADC_MASK{0x0038}; ///< INA226 Bits 3-4 + +const uint8_t INA228_DIE_ID_REGISTER{0x3F}; ///< INA228 Device_ID Register +const uint16_t INA228_DIE_ID_VALUE{0x2280}; ///< INA228 Hard-coded Die ID for INA228 +const uint8_t INA228_BUS_VOLTAGE_REGISTER{0x5}; ///< INA228 Bus Voltage Register +const uint16_t INA228_BUS_VOLTAGE_LSB{195}; ///< INA228 LSB in uV *100 1953125uV, extra code +const uint8_t INA228_SHUNT_VOLTAGE_REGISTER{4}; ///< INA228 Shunt Voltage Register +const uint8_t xINA228_CURRENT_REGISTER{4}; ///< INA228 Current Register +const uint16_t xINA228_CONFIG_AVG_MASK{0x0E00}; ///< INA228 Bits 9-11 +const uint16_t xINA228_CONFIG_BADC_MASK{0x01C0}; ///< INA228 Bits 6-8 masked +const uint16_t xINA228_CONFIG_SADC_MASK{0x0038}; ///< INA228 Bits 3-4 + +const uint8_t INA260_SHUNT_VOLTAGE_REGISTER{0}; ///< INA260 Register doesn't exist +const uint8_t INA260_CURRENT_REGISTER{1}; ///< INA260 Current Register +const uint16_t INA260_BUS_VOLTAGE_LSB{125}; ///< INA260 LSB in uV *100 1.25mV +const uint16_t INA260_CONFIG_BADC_MASK{0x01C0}; ///< INA260 Bits 6-8 masked +const uint16_t INA260_CONFIG_SADC_MASK{0x0038}; ///< INA260 Bits 3-5 masked +const uint8_t INA3221_SHUNT_VOLTAGE_REGISTER{1}; ///< INA3221 Register number 1 +const uint16_t INA3221_BUS_VOLTAGE_LSB{800}; ///< INA3221 LSB in uV *100 8mV +const uint16_t INA3221_SHUNT_VOLTAGE_LSB{400}; ///< INA3221 LSB in uV *10 40uV +const uint16_t INA3221_CONFIG_BADC_MASK{0x01C0}; ///< INA3221 Bits 7-10 masked +const uint8_t INA3221_MASK_REGISTER{0xF}; ///< INA32219 Mask register +const uint8_t I2C_DELAY{10}; ///< Microsecond delay on I2C writes +// clang-format on + +class INA_Class { + /*! + * @class INA_Class + * @brief Forward definitions for the INA_Class + */ + public: + INA_Class(uint8_t expectedDevices = 0); + ~INA_Class(); + uint8_t begin(const uint16_t maxBusAmps, const uint32_t microOhmR, + const uint8_t deviceNumber = UINT8_MAX); + void setI2CSpeed(const uint32_t i2cSpeed = INA_I2C_STANDARD_MODE) const; + void setMode(const uint8_t mode, const uint8_t deviceNumber = UINT8_MAX); + void setAveraging(const uint16_t averages, const uint8_t deviceNumber = UINT8_MAX); + void setBusConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX); + void setShuntConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX); + uint16_t getBusMilliVolts(const uint8_t deviceNumber = 0); + uint32_t getBusRaw(const uint8_t deviceNumber = 0); + int32_t getShuntMicroVolts(const uint8_t deviceNumber = 0); + int32_t getShuntRaw(const uint8_t deviceNumber = 0); + int32_t getBusMicroAmps(const uint8_t deviceNumber = 0); + int64_t getBusMicroWatts(const uint8_t deviceNumber = 0); + const char* getDeviceName(const uint8_t deviceNumber = 0); + uint8_t getDeviceAddress(const uint8_t deviceNumber = 0); + uint8_t getDeviceType(const uint8_t deviceNumber = 0); // Added for ESPEasy + void reset(const uint8_t deviceNumber = 0); + bool conversionFinished(const uint8_t deviceNumber = 0); + void waitForConversion(const uint8_t deviceNumber = UINT8_MAX); + bool alertOnConversion(const bool alertState, const uint8_t deviceNumber = UINT8_MAX); + bool alertOnShuntOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnShuntUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnBusOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnBusUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnPowerOverLimit(const bool alertState, const int32_t milliAmps, + const uint8_t deviceNumber = UINT8_MAX); + uint16_t _EEPROM_offset = 0; ///< Offset to all EEPROM addresses, GitHub issue #41 + #if defined(ESP32) || defined(ESP8266) + uint16_t _EEPROM_size = 512; ///< Default EEPROM reserved space for ESP32 and ESP8266 + #endif + private: + int16_t readWord(const uint8_t addr, const uint8_t deviceAddress) const; + int32_t read3Bytes(const uint8_t addr, const uint8_t deviceAddress) const; + void writeWord(const uint8_t addr, const uint16_t data, const uint8_t deviceAddress) const; + void readInafromEEPROM(const uint8_t deviceNumber); + void writeInatoEEPROM(const uint8_t deviceNumber); + void initDevice(const uint8_t deviceNumber); + uint8_t _DeviceCount{0}; ///< Total number of devices detected + uint8_t _currentINA{UINT8_MAX}; ///< Stores current INA device number + uint8_t _expectedDevices{0}; ///< If 0 use EEPROM, otherwise use RAM for INA structures + inaEEPROM* _DeviceArray; ///< Pointer to dynamic array of devices if not using EEPROM + inaEEPROM inaEE; ///< INA device structure + inaDet ina; ///< INA device structure + #if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || \ + defined(__STM32F1__) + #else + inaEEPROM _EEPROMEmulation[32]; ///< Actual array of up to 32 devices + #endif +}; // of INA_Class definition +#endif diff --git a/lib/Itho/CC1101.cpp b/lib/Itho/CC1101.cpp index 1fb2de787..7a81ffbd1 100644 --- a/lib/Itho/CC1101.cpp +++ b/lib/Itho/CC1101.cpp @@ -5,7 +5,7 @@ #include "CC1101.h" // default constructor -CC1101::CC1101(int8_t CSpin, int8_t MISOpin) : _CSpin(CSpin), _MISOpin(MISOpin) +CC1101::CC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi) : _CSpin(CSpin), _MISOpin(MISOpin), _spi(spi) { // SPI.begin(); // Done already by ESPEasy pinMode(_CSpin, OUTPUT); @@ -51,7 +51,7 @@ void CC1101::reset() select(); spi_waitMiso(); - SPI.transfer(CC1101_SRES); + _spi.transfer(CC1101_SRES); delay(10); spi_waitMiso(); deselect(); @@ -63,7 +63,7 @@ uint8_t CC1101::writeCommand(uint8_t command) select(); spi_waitMiso(); - result = SPI.transfer(command); + result = _spi.transfer(command); deselect(); return result; @@ -73,8 +73,8 @@ void CC1101::writeRegister(uint8_t address, uint8_t data) { select(); spi_waitMiso(); - SPI.transfer(address); - SPI.transfer(data); + _spi.transfer(address); + _spi.transfer(data); deselect(); } @@ -84,8 +84,8 @@ uint8_t CC1101::readRegister(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val = SPI.transfer(0); + _spi.transfer(address); + val = _spi.transfer(0); deselect(); return val; @@ -97,12 +97,12 @@ uint8_t CC1101::readRegisterMedian3(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val1 = SPI.transfer(0); - SPI.transfer(address); - val2 = SPI.transfer(0); - SPI.transfer(address); - val3 = SPI.transfer(0); + _spi.transfer(address); + val1 = _spi.transfer(0); + _spi.transfer(address); + val2 = _spi.transfer(0); + _spi.transfer(address); + val3 = _spi.transfer(0); deselect(); // reverse sort (largest in val1) because this is te expected order for TX_BUFFER @@ -162,10 +162,10 @@ void CC1101::writeBurstRegister(uint8_t address, uint8_t *data, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_WRITE_BURST); + _spi.transfer(address | CC1101_WRITE_BURST); for (i = 0; i < length; i++) { - SPI.transfer(pgm_read_byte(&(data[i]))); + _spi.transfer(pgm_read_byte(&(data[i]))); } deselect(); } @@ -176,10 +176,10 @@ void CC1101::readBurstRegister(uint8_t *buffer, uint8_t address, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_READ_BURST); + _spi.transfer(address | CC1101_READ_BURST); for (i = 0; i < length; i++) { - buffer[i] = SPI.transfer(0x00); + buffer[i] = _spi.transfer(0x00); } deselect(); diff --git a/lib/Itho/CC1101.h b/lib/Itho/CC1101.h index 0fd489319..8c01d31f3 100644 --- a/lib/Itho/CC1101.h +++ b/lib/Itho/CC1101.h @@ -191,8 +191,9 @@ protected: public: - CC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO); + CC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI); virtual ~CC1101(); // spi @@ -230,6 +231,7 @@ private: int8_t _CSpin = PIN_SPI_SS; int8_t _MISOpin; + SPIClass& _spi = SPI; protected: diff --git a/lib/Itho/IthoCC1101.cpp b/lib/Itho/IthoCC1101.cpp index e9a27dd7f..dbd227072 100644 --- a/lib/Itho/IthoCC1101.cpp +++ b/lib/Itho/IthoCC1101.cpp @@ -42,7 +42,7 @@ #define MDMCFG2 0x02 // 16bit sync word / 16bit specific // default constructor -IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin) +IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin, spi) { this->outIthoPacket.counter = counter; this->sendTries = sendTries; diff --git a/lib/Itho/IthoCC1101.h b/lib/Itho/IthoCC1101.h index 399f279c2..17c9a9fab 100644 --- a/lib/Itho/IthoCC1101.h +++ b/lib/Itho/IthoCC1101.h @@ -78,10 +78,11 @@ private: public: - IthoCC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO, - uint8_t counter = 0, - uint8_t sendTries = 3); // set initial counter value + IthoCC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI, + uint8_t counter = 0, + uint8_t sendTries = 3); // set initial counter value ~IthoCC1101(); // init @@ -90,7 +91,8 @@ public: initReceive(); } // init,reset CC1101 - void initReceive(); + void initReceive(); + // uint8_t getLastCounter() const { // return outIthoPacket.counter; // } // counter is increased before sending a command @@ -104,7 +106,7 @@ public: } // receive - bool checkForNewPacket(); // check RX fifo for new data + bool checkForNewPacket(); // check RX fifo for new data // IthoPacket getLastPacket() const { // return inIthoPacket; // } // retrieve last received/parsed packet from remote @@ -120,14 +122,15 @@ public: // uint8_t ReadRSSI(); // bool checkID(const uint8_t *id) const; // int* getLastID(); - String getLastIDstr(bool ashex = true); + String getLastIDstr(bool ashex = true); + // String getLastMessagestr(bool ashex = true); // String LastMessageDecoded() const; // send - void sendCommand(IthoCommand command, - uint8_t srcId[3] = 0, - uint8_t destId[3] = 0); + void sendCommand(IthoCommand command, + uint8_t srcId[3] = 0, + uint8_t destId[3] = 0); void enableOrcon(bool state); diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp index ce160c911..4bfcb0335 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp @@ -16,7 +16,7 @@ #include "Adafruit_GFX.h" #include "LOLIN_EPD.h" -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { cs = CS; rst = RST; @@ -26,9 +26,10 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, i busy = BUSY; hwSPI = false; singleByteTxns = false; + _spi = spi; } -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { dc = DC; @@ -37,6 +38,7 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, in busy = BUSY; hwSPI = true; singleByteTxns = false; + _spi = spi; } /**************************************************************************/ @@ -90,9 +92,9 @@ void LOLIN_EPD::begin(bool reset) } else { - SPI.begin(); + // _spi.begin(); // Already done by ESPEasy #ifndef SPI_HAS_TRANSACTION - SPI.setClockDivider(4); + _spi.setClockDivider(4); #endif } @@ -194,12 +196,12 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) { uint8_t b; csLow(); - b = SPI.transfer(d); + b = _spi.transfer(d); csHigh(); return b; } else - return SPI.transfer(d); + return _spi.transfer(d); } else { @@ -234,7 +236,7 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) void LOLIN_EPD::csHigh() { #ifdef SPI_HAS_TRANSACTION - SPI.endTransaction(); + _spi.endTransaction(); #endif #ifdef HAVE_PORTREG *csport |= cspinmask; @@ -251,7 +253,7 @@ void LOLIN_EPD::csHigh() void LOLIN_EPD::csLow() { #ifdef SPI_HAS_TRANSACTION - SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); + _spi.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); #endif #ifdef HAVE_PORTREG *csport &= ~cspinmask; diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.h b/lib/LOLIN_EPD/src/LOLIN_EPD.h index dacd00071..677d82c94 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.h +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.h @@ -60,8 +60,8 @@ enum class LOLIN_EPD: public Adafruit_GFX { public: - LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); virtual ~LOLIN_EPD(); virtual void begin(bool reset = true); @@ -111,6 +111,7 @@ protected: void dcLow(); private: + SPIClass& _spi = SPI; }; #include "LOLIN_IL3897.h" diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp index 8cb34af91..764e6e3b0 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp @@ -184,7 +184,7 @@ const unsigned char LUT_DATA_part[] PROGMEM = { @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -202,7 +202,7 @@ LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_ red_bufsize = bw_bufsize; } -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.h b/lib/LOLIN_EPD/src/LOLIN_IL3897.h index a461f51e8..77847c7fb 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.h +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.h @@ -20,8 +20,8 @@ class LOLIN_IL3897 : public LOLIN_EPD { public: - LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset=true); diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp index f51bdf3ee..b9037a1df 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h index 9a7ebebcb..47eeafcd1 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h @@ -19,8 +19,8 @@ class LOLIN_SSD1680 : public LOLIN_EPD { public: - LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp index 737a22e43..cd7d48ea3 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h index f07603aa1..a3d188d87 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h @@ -19,8 +19,8 @@ class LOLIN_UC8151D : public LOLIN_EPD { public: - LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp index 12b73cee3..2c1656061 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp @@ -76,8 +76,8 @@ static const unsigned char EPD_2in7_gray_lut_bb[] = { @param BUSY the busy pin to use */ -Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) - : LOLIN_EPD(width, height, DC, RST, CS, BUSY) { +Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) + : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) { _height_8bit = (height / 8 + 1) * 8; } else { @@ -247,7 +247,7 @@ void Waveshare_2in7::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) { return; } - + // Corrections by @kretzp (Peter Kretz) 2022-03-27 // check rotation, move pixel around if necessary switch (getRotation()) { diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.h b/lib/LOLIN_EPD/src/Waveshare_2in7.h index d7f216dda..7d3eda4b3 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.h +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.h @@ -24,10 +24,10 @@ #define WS2IN7_DATA_STOP 0x11 #define WS2IN7_DISPLAY_REFRESH 0x12 #define WS2IN7_DATA_START_TRANSMISSION_2 0x13 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 #define WS2IN7_PARTIAL_DISPLAY_REFRESH 0x16 -#define WS2IN7_LUT_FOR_VCOM 0x20 +#define WS2IN7_LUT_FOR_VCOM 0x20 #define WS2IN7_LUT_WHITE_TO_WHITE 0x21 #define WS2IN7_LUT_BLACK_TO_WHITE 0x22 #define WS2IN7_LUT_WHITE_TO_BLACK 0x23 @@ -67,12 +67,13 @@ extern const unsigned char lut_wb[]; class Waveshare_2in7 : public LOLIN_EPD { public: - Waveshare_2in7(int width, - int height, - int8_t DC, - int8_t RST, - int8_t CS, - int8_t BUSY = -1); + Waveshare_2in7(int width, + int height, + int8_t DC, + int8_t RST, + int8_t CS, + int8_t BUSY = -1, + SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/MD_Parola/src/MD_Parola.h b/lib/MD_Parola/src/MD_Parola.h index f116b9e4a..92e812bed 100644 --- a/lib/MD_Parola/src/MD_Parola.h +++ b/lib/MD_Parola/src/MD_Parola.h @@ -1343,6 +1343,23 @@ public: _D(mod, csPin, numDevices), _numModules(numDevices) {} + /** + * Class constructor - SPI hardware interface. + * + * Instantiate a new instance of the class. The parameters passed are used to + * connect the software to the hardware using the MD_MAX72XX class. + * + * See documentation for the MD_MAX72XX library for detailed explanation of parameters. + * + * \param mod the hardware module type used in the application. One of the MD_MAX72XX::moduleType_t values. + * \param spi the SPI interface to use + * \param csPin output for selecting the device. + * \param numDevices number of devices connected. Default is 1 if not supplied. + */ + MD_Parola(MD_MAX72XX::moduleType_t mod, SPIClass &spi, uint8_t csPin, uint8_t numDevices = 1) : + _D(mod, spi, csPin, numDevices), _numModules(numDevices) + {} + /** * Initialize the object. * diff --git a/lib/MFRC522/MFRC522.cpp b/lib/MFRC522/MFRC522.cpp index 0d6d81669..748b754d4 100644 --- a/lib/MFRC522/MFRC522.cpp +++ b/lib/MFRC522/MFRC522.cpp @@ -29,11 +29,12 @@ MFRC522::MFRC522( uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC52 * Prepares the output pins. */ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) - uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). - ) { - _chipSelectPin = chipSelectPin; - _resetPowerDownPin = resetPowerDownPin; -} // End constructor + uint8_t resetPowerDownPin, ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). + SPIClass& spi) : + _chipSelectPin(chipSelectPin), + _resetPowerDownPin(resetPowerDownPin), + _spi(spi) +{} // End constructor ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 @@ -46,12 +47,12 @@ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522' void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. uint8_t value ///< The value to write. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. - SPI.transfer(value); + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(value); digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -62,14 +63,14 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t count, ///< The number of bytes to write to the register uint8_t *values ///< The values to write. Byte array. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. for (uint8_t index = 0; index < count; index++) { - SPI.transfer(values[index]); + _spi.transfer(values[index]); } digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -79,12 +80,12 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. - value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. + _spi.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + value = _spi.transfer(0); // Read the value back. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus return value; } // End PCD_ReadRegister() @@ -103,26 +104,26 @@ void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); uint8_t address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. uint8_t index = 0; // Index in values array. - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave count--; // One read is performed outside of the loop - SPI.transfer(address); // Tell MFRC522 which address we want to read + _spi.transfer(address); // Tell MFRC522 which address we want to read if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] // Create bit mask for bit positions rxAlign..7 uint8_t mask = (0xFF << rxAlign) & 0xFF; // Read value and tell that we want to read the same address again. - uint8_t value = SPI.transfer(address); + uint8_t value = _spi.transfer(address); // Apply mask to both current value of values[0] and the new data in value. values[0] = (values[0] & ~mask) | (value & mask); index++; } while (index < count) { - values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. + values[index] = _spi.transfer(address); // Read value and tell that we want to read the same address again. index++; } - values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. + values[index] = _spi.transfer(0); // Read the final byte. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_ReadRegister() /** diff --git a/lib/MFRC522/MFRC522.h b/lib/MFRC522/MFRC522.h index 0a7f0e426..15479d540 100644 --- a/lib/MFRC522/MFRC522.h +++ b/lib/MFRC522/MFRC522.h @@ -293,7 +293,7 @@ public: ///////////////////////////////////////////////////////////////////////////////////// MFRC522(); MFRC522(uint8_t resetPowerDownPin); - MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin); + MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin, SPIClass& spi = SPI); virtual ~MFRC522() {}; ///////////////////////////////////////////////////////////////////////////////////// @@ -389,6 +389,7 @@ public: protected: uint8_t _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) uint8_t _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + SPIClass& _spi = SPI; StatusCode MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, int32_t data); }; diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp index cdf22b23a..4ebfe0415 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp @@ -22,7 +22,6 @@ #include "Arduino.h" #include "SparkFun_ADXL345.h" #include -#include #define ADXL345_DEVICE_DEFAULT (0x53) // Device Address for ADXL345 #define ADXL345_DEVICE (_i2c_addr) // Device Address for ADXL345 @@ -38,7 +37,7 @@ ADXL345::ADXL345(uint8_t i2c_addr = ADXL345_DEVICE_DEFAULT) : _i2c_addr(i2c_addr I2C = true; } -ADXL345::ADXL345(int CS) { +ADXL345::ADXL345(int CS, SPIClass& spi) { status = ADXL345_OK; error_code = ADXL345_NO_ERROR; @@ -47,9 +46,12 @@ ADXL345::ADXL345(int CS) { gains[2] = 0.00349265; _CS = CS; I2C = false; - // tonhuisman: disabled as SPI is already initialized in ESPEasy core. + + // 2021-12-13 tonhuisman: disabled as SPI is already initialized in ESPEasy core. // SPI.begin(); // SPI.setDataMode(SPI_MODE3); + // 2025-08-13 tonhuisman: provide external SPI bus + _spi = spi; pinMode(_CS, OUTPUT); digitalWrite(_CS, HIGH); } @@ -168,8 +170,8 @@ void ADXL345::readFromI2C(byte address, int num, byte _buff[]) { /* Point to Destination; Write Value; Turn Off */ void ADXL345::writeToSPI(byte __reg_address, byte __val) { digitalWrite(_CS, LOW); - SPI.transfer(__reg_address); - SPI.transfer(__val); + _spi.transfer(__reg_address); + _spi.transfer(__val); digitalWrite(_CS, HIGH); } @@ -185,10 +187,10 @@ void ADXL345::readFromSPI(byte __reg_address, int num, byte _buff[]) { } digitalWrite(_CS, LOW); - SPI.transfer(_address); // Transfer Starting Reg Address To Be Read + _spi.transfer(_address); // Transfer Starting Reg Address To Be Read for (int i = 0; i < num; i++) { - _buff[i] = SPI.transfer(0x00); + _buff[i] = _spi.transfer(0x00); } digitalWrite(_CS, HIGH); } diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h index c8cfb7750..0de0b5261 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h @@ -21,6 +21,8 @@ #ifndef ADXL345_h # define ADXL345_h +# include + /*************************** REGISTER MAP ***************************/ # define ADXL345_DEVID 0x00 // Device ID # define ADXL345_RESERVED1 0x01 // Reserved. Do Not Access. @@ -115,7 +117,8 @@ public: double gains[3]; // Counts to Gs ADXL345(uint8_t i2c_addr); - ADXL345(int CS); + ADXL345(int CS, + SPIClass& spi = SPI); void powerOn(); void powerOff(); int getDevID(); @@ -266,6 +269,7 @@ private: bool I2C = true; unsigned long SPIfreq = 5000000; uint8_t _i2c_addr; + SPIClass& _spi = SPI; }; void print_byte(byte val); #endif // ifndef ADXL345_h diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp index 75ba44ee5..f884ffbfb 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp @@ -32,7 +32,7 @@ void isrPin(void); bool XPT2046_Touchscreen::begin() { - SPI.begin(); + // _spi.begin(); // ESPEasy already does this pinMode(csPin, OUTPUT); digitalWrite(csPin, HIGH); if (255 != tirqPin) { @@ -105,25 +105,25 @@ void XPT2046_Touchscreen::update() uint32_t now = millis(); if (now - msraw < MSEC_THRESHOLD) return; - SPI.beginTransaction(SPI_SETTING); + _spi.beginTransaction(SPI_SETTING); digitalWrite(csPin, LOW); - SPI.transfer(0xB1 /* Z1 */); - int16_t z1 = SPI.transfer16(0xC1 /* Z2 */) >> 3; + _spi.transfer(0xB1 /* Z1 */); + int16_t z1 = _spi.transfer16(0xC1 /* Z2 */) >> 3; int z = z1 + 4095; - int16_t z2 = SPI.transfer16(0x91 /* X */) >> 3; + int16_t z2 = _spi.transfer16(0x91 /* X */) >> 3; z -= z2; if (z >= Z_THRESHOLD) { - SPI.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy - data[0] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[1] = SPI.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements - data[2] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[3] = SPI.transfer16(0x91 /* X */) >> 3; + _spi.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy + data[0] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[1] = _spi.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements + data[2] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[3] = _spi.transfer16(0x91 /* X */) >> 3; } else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit. - data[4] = SPI.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down - data[5] = SPI.transfer16(0) >> 3; + data[4] = _spi.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down + data[5] = _spi.transfer16(0) >> 3; digitalWrite(csPin, HIGH); - SPI.endTransaction(); + _spi.endTransaction(); //Serial.printf("z=%d :: z1=%d, z2=%d ", z, z1, z2); if (z < 0) z = 0; if (z < Z_THRESHOLD) { // if ( !touched ) { diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h index d723ca134..878b15d7b 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h @@ -41,8 +41,8 @@ public: class XPT2046_Touchscreen { public: - constexpr XPT2046_Touchscreen(uint8_t cspin, uint8_t tirq=255) - : csPin(cspin), tirqPin(tirq) { } + constexpr XPT2046_Touchscreen(uint8_t cspin, SPIClass& spi = SPI, uint8_t tirq=255) + : csPin(cspin), _spi(spi), tirqPin(tirq) { } bool begin(); TS_Point getPoint(); bool tirqTouched(); @@ -57,8 +57,13 @@ public: private: void update(); - uint8_t csPin, tirqPin, rotation = 1; - int16_t xraw = 0, yraw = 0, zraw = 0; + uint8_t csPin; + SPIClass& _spi = SPI; + uint8_t tirqPin; + uint8_t rotation = 1; + int16_t xraw = 0; + int16_t yraw = 0; + int16_t zraw = 0; uint32_t msraw = 0x80000000; bool flipped = false; }; diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index d93cab796..fcad9e0a3 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -67,6 +67,7 @@ lib_ignore = ESP32_ping ; See: https://github.com/platformio/platform-espressif8266/issues/288 ; For "-fno-strict-aliasing" ; See: https://github.com/esp8266/Arduino/issues/8261 +; Suggestion to go for -Os: https://www.youtube.com/watch?v=cqHH2NXcf5E [esp82xx_2_7_x] build_flags = -DNDEBUG -mtarget-align @@ -83,7 +84,7 @@ build_flags = -DNDEBUG -DNO_GLOBAL_I2S -I$PROJECT_DIR/src/include -include "ESPEasy_config.h" - -O2 + -Os -s -DBEARSSL_SSL_BASIC -DCORE_POST_2_6_0 @@ -170,8 +171,11 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform = https://github.com/Jason2866/platform-espressif32.git#23aed73e4e570d3b6608c15677fea01d6a722d94 ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz -platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0401-1225-5.5/framework-arduinoespressif32-release_v5.5-ad03770f.tar.xz +;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1002-1125-5.5_gcc151/framework-arduinoespressif32-release_v5.5_gcc151-7eede06a.tar.xz + +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc152 +platform_packages = custom_remove_include = true @@ -208,8 +212,26 @@ build_flags = -DESP32_STAGE -include "ESPEasy_config.h" -include "esp32x_fixes.h" -DUPDATE_NOCRYPT - -O3 + -Os ; -Wnull-dereference + -DNO_GLOBAL_ESP_NOW + -DNO_GLOBAL_EEPROM + -DNO_GLOBAL_INSIGHTS + -DNO_GLOBAL_NETBIOS + -DNO_GLOBAL_OPENTHREAD + -DNO_GLOBAL_MATTER + -DNO_GLOBAL_ZIGBEE + -DNO_GLOBAL_SPIFFS + -DNO_GLOBAL_OPENTHREADCLI + -DNO_GLOBAL_WIFIPROV + + + + + + + + lib_ignore = BLE Zigbee ESP RainMaker diff --git a/platformio_esp32c6_envs.ini b/platformio_esp32c6_envs.ini index 37856d019..b73a2ac82 100644 --- a/platformio_esp32c6_envs.ini +++ b/platformio_esp32c6_envs.ini @@ -10,6 +10,9 @@ build_unflags = ${esp32_base_idf5_5.build_unflags} -fexceptions board_build.filesystem = littlefs lib_ignore = ${esp32_base_idf5_5.lib_ignore} + PPP + HeatpumpIR + IRremoteESP8266 board = esp32c6cdc @@ -78,6 +81,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags} -D ST7789_EXTRA_INIT=1 -D P116_EXTRA_ST7789=1 extra_scripts = ${esp32c6_common_LittleFS.extra_scripts} +lib_ignore = ${esp32_base_idf5_5.lib_ignore} [env:max_ESP32c6_16M8M] @@ -91,6 +95,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags} -D ST7789_EXTRA_INIT=1 -D P116_EXTRA_ST7789=1 extra_scripts = ${esp32c6_common_LittleFS.extra_scripts} +lib_ignore = ${esp32_base_idf5_5.lib_ignore} diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 40c4fd013..68f17ae75 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -246,11 +246,13 @@ lib_ignore = ESP32_ping extends = ir build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IR + -DFEATURE_I2C=1 [minimal_ir_extended] extends = ir build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IRext + -DFEATURE_I2C=1 [normal_ir] extends = ir @@ -275,12 +277,12 @@ build_flags = ${ir.build_flags} ; Keep optimization flag to -O2 ; See: https://github.com/platformio/platform-espressif8266/issues/288 - +; Suggestion to go for -Os: https://www.youtube.com/watch?v=cqHH2NXcf5E [esp82xx_1M] extends = esp82xx_common build_flags = -DBUILD_NO_DEBUG -DFEATURE_SD=0 - -O2 + -Os -s -DNO_GLOBAL_MDNS ${esp82xx_common.build_flags} @@ -305,6 +307,109 @@ build_flags = ${esp82xx_1M.build_flags} -DDISABLE_SC16IS752_Serial -DFEATURE_GPIO_USE_ESP8266_WAVEFORM=0 extra_scripts = ${esp82xx_1M.extra_scripts} +lib_ignore = ESP32_ping + ESP32WebServer + ESP32HTTPUpdateServer + ServoESP32 + TinyWireM + ESP8266SdFat + SD(esp8266) + SD + SDFS + LittleFS(esp8266) + LittleFS + ArduinoOTA + ESP8266mDNS + I2C AXP192 Power management + EspSoftwareSerial + AXP2101 Power management (I2C) + AM2320 + AS3935MI + AS5600 + AS_BH1750 + Adafruit BME680 Library + Adafruit BMP3XX Library + Adafruit GFX Library + Adafruit HMC5883 Unified + Adafruit ILI9341 ESPEasy + Adafruit MPR121 + Adafruit Motor Shield V2 Library + Adafruit NeoMatrix via NeoPixelBus + Adafruit PCD8544 Nokia 5110 LCD library + Adafruit SGP30 Sensor + Adafruit SI1145 Library + Adafruit TCS34725 + Adafruit TSL2591 Library + Adafruit Unified Sensor + Adafruit VEML7700 Library + Adafruit seesaw Library + Adafruit_ST77xx + AnyRtttl +; ArduinoJson + Blynk + CHT16K33 LED Matrix Library + CircularBuffer + ClimateGuard RadSens + DFRobot_GP8403_ESPEasy + DNSServer +; ESP8266HTTPClient +; ESP8266HTTPUpdateServer +; ESP8266WebServer +; ESP8266WiFi + ESP8266_SSD1306 +; ESPeasySerial + ESPping + ESP8266 and ESP32 Oled Driver for SSD1306 display + FrogmoreScd30 +; GPIO_DirectAccess + HLW8012 + HeatpumpIR + I2Cdevlib-Core + ILI9488 + INA2xx + IRremoteESP8266 + ITG3205 + Improv WiFi Library + IthoCC1101 + LOLIN_EPD + LiquidCrystal_I2C + MD_MAX72XX + MD_Parola + MFRC522 + MechInputs + Multi Channel Relay Arduino Library + NeoPixelBus + NeoPixelBus_wrapper + NewPing + Noiasca NeopixelDisplay + PZEM-004T-v30 +; PubSubClient3 + RAK12019_LTR390_UV_Light + RN2xx3 Arduino Library + RTClib + Regexp + SC16IS752 + SDM + SPI + Sensirion Gas Index Algorithm + SerialSensors + Servo + ShiftRegister74HC595_NonTemplate + SparkFun ADXL345 Arduino Library + SparkFun APDS9960 RGB and Gesture Sensor + SparkFun MAX1704x Fuel Gauge Arduino Library + SparkFun SCD4x Arduino Library + SparkFun VL53L1X 4m Laser Distance Sensor + SparkFun_AS7265x + TinyGPSPlus + VL53L0X + WakeOnLan + Wire + XPT2046_Touchscreen + bb_captouch + htcw_ip5306 + ld2410 + supertinycron ;lib_ignore = ${esp82xx_1M.lib_ignore} ; Adding the libs below to the lib_ignore will even increase build size ; Adafruit TCS34725 diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 251b82fb8..df323aead 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -375,21 +375,21 @@ build_flags = ${esp8266_1M_OTA.build_flags} extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_domoticz.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_274_ESP8266_1M_OTA_Domoticz_MQTT] extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_domoticz_MQTT.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_274_ESP8266_1M_OTA_FHEM_HA] extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_FHEM_HA.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_312_ESP8266_1M_OTA_Domoticz] @@ -397,6 +397,8 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_domoticz.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} + [env:minimal_core_312_ESP8266_1M_OTA_Domoticz_MQTT] @@ -404,6 +406,7 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_domoticz_MQTT.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_312_ESP8266_1M_OTA_FHEM_HA] @@ -411,6 +414,7 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_FHEM_HA.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 7ef077162..11bf1af84 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -540,6 +540,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // #define USES_P131 // NeoPixel Matrix // #define USES_P132 // INA3221 +// #define P132_EXTENDED 1 // Extend support with INA219, INA226, INA228, INA230, INA231 and INA260 // #define USES_P133 // LTR390 UV // #define USES_P134 // A02YYUW // #define USES_P135 // SCD4x diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp index 14074c7d2..0646aff49 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp @@ -59,6 +59,17 @@ NWPluginData_base::~NWPluginData_base() #endif // if FEATURE_STORE_NETWORK_INTERFACE_SETTINGS } +#ifdef ESP32 +bool NWPluginData_base::isDefaultRoute() const { + if (_netif) { + return _netif->isDefault(); + } + return false; +} + +#endif + + bool NWPluginData_base::hasPluginStats() const { #if FEATURE_NETWORK_STATS diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.h b/src/ESPEasy/net/DataStructs/NWPluginData_base.h index 8ea8a01db..dad8dfa36 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.h @@ -39,6 +39,10 @@ struct NWPluginData_base { virtual bool exit(EventStruct *event) = 0; +#ifdef ESP32 + virtual bool isDefaultRoute() const; +#endif + bool hasPluginStats() const; bool hasPeaks() const; diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h index 4641e8995..0e6c1d687 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h @@ -103,6 +103,8 @@ struct NWPluginData_static_runtime { void mark_disconnected(); void log_disconnected(); + void mark_connect_failed(); + // ============================================= // Keep track of connection durations // per host/interface diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp index 93fd941ab..bc2faf356 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp @@ -108,7 +108,7 @@ void NWPluginData_static_runtime::mark_start() # endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) # if NW_PLUGIN_LOG_EVENTS - if (loglevelActiveFor(LOG_LEVEL_INFO)) { + if (!_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Started"), _eventInterfaceName.c_str())); @@ -121,7 +121,7 @@ void NWPluginData_static_runtime::mark_stop() _startStopStats.setOff(); # if NW_PLUGIN_LOG_EVENTS - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Stopped"), _eventInterfaceName.c_str())); @@ -134,7 +134,7 @@ void NWPluginData_static_runtime::mark_got_IP() // Set OnOffTimer to off so we can also count how often we get new IP _gotIPStats.forceSet(true); - if (!_netif) { return; } + if (!_netif || _eventInterfaceName.isEmpty()) { return; } if (!_netif->isDefault()) { nonDefaultNetworkInterface_gotIP = true; @@ -200,7 +200,7 @@ void NWPluginData_static_runtime::mark_lost_IP() # endif # if NW_PLUGIN_LOG_EVENTS - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Lost IP"), _eventInterfaceName.c_str())); @@ -235,7 +235,7 @@ void NWPluginData_static_runtime::mark_connected() void NWPluginData_static_runtime::log_connected() { - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { if (_establishConnectStats.getCycleCount()) { // Log duration addLog(LOG_LEVEL_INFO, strformat( @@ -269,7 +269,7 @@ void NWPluginData_static_runtime::mark_disconnected() void NWPluginData_static_runtime::log_disconnected() { - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Disconnected. Connected for: %s"), _eventInterfaceName.c_str(), @@ -278,6 +278,13 @@ void NWPluginData_static_runtime::log_disconnected() } } + +void NWPluginData_static_runtime::mark_connect_failed() +{ + networkConnectionFailed = true; +} + + } // namespace net } // namespace ESPEasy diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index cbc21b166..b4034900e 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -3,6 +3,8 @@ #ifdef ESP8266 # include "../../../src/Helpers/StringConverter.h" +# include "../Globals/NetworkState.h" +# include "../ESPEasyNetwork.h" namespace ESPEasy { namespace net { @@ -71,6 +73,9 @@ void NWPluginData_static_runtime::mark_begin_establish_connection() _establishConnectStats.forceSet(true); _connectedStats.setOff(); _operationalStats.setOff(); + const String hostname = NetworkCreateRFCCompliantHostname(); + wifi_station_set_hostname(hostname.c_str()); + WiFi.hostname(hostname.c_str()); } void NWPluginData_static_runtime::mark_connected() @@ -115,6 +120,11 @@ void NWPluginData_static_runtime::log_disconnected() # endif // ifndef BUILD_NO_DEBUG } +void NWPluginData_static_runtime::mark_connect_failed() +{ + networkConnectionFailed = true; +} + } // namespace net } // namespace ESPEasy diff --git a/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp b/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp index 935ece720..131c37ba9 100644 --- a/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp +++ b/src/ESPEasy/net/ESPEasyNetwork_ESP8266.cpp @@ -72,7 +72,7 @@ IPAddress NetworkLocalIP() { return WiFi.localIP(); } IPAddress NetworkID() { const IPAddress subnet = NetworkSubnetMask(); - IPAddress networkID = NetworkLocalIP(); + IPAddress networkID = ESPEasy::net::NetworkLocalIP(); for (uint8_t i = 0; i < 4; ++i) { networkID[i] &= subnet[i]; @@ -83,7 +83,7 @@ IPAddress NetworkID() IPAddress NetworkBroadcast() { const IPAddress subnet = NetworkSubnetMask(); - IPAddress broadcast = NetworkLocalIP(); + IPAddress broadcast = ESPEasy::net::NetworkLocalIP(); for (uint8_t i = 0; i < 4; ++i) { broadcast[i] |= ~subnet[i]; diff --git a/src/ESPEasy/net/Globals/NWPlugins.cpp b/src/ESPEasy/net/Globals/NWPlugins.cpp index da640fb6e..ee5c7aa4f 100644 --- a/src/ESPEasy/net/Globals/NWPlugins.cpp +++ b/src/ESPEasy/net/Globals/NWPlugins.cpp @@ -84,9 +84,57 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (Settings.getNWPluginID_for_network(x) && checkedEnabled) { if (Function == NWPlugin::Function::NWPLUGIN_INIT_ALL) { - Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelayAtBoot(x), x); + if (!Settings.getNetworkInterface_isFallback(x)) { + Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + } + } + + if (Function == NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN && + Settings.getNetworkInterfaceSubnetBlockClientIP(x)) + { + // Skip check for this network interface as access to the web UI should be blocked anyway } else { event->NetworkIndex = x; + +#ifdef ESP32 + + if ((Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) && + Settings.getNetworkInterface_isFallback(x)) + { + // Deal with the fallback interface(s) + // TODO TD-er: What to do with the WiFi-AP as that's a special case of a fallback interface. + NetworkInterface *currentDefaultInterface = Network.getDefaultInterface(); + + if (!currentDefaultInterface) { + String dummy; + if (do_NWPluginCall( + getNetworkDriverIndex_from_NetworkIndex(x), + NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START, + event, + dummy)) + { + // No current default interface, thus we need to start this one. + Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + } + } else { + if (currentDefaultInterface->getRoutePrio() > Settings.getRoutePrio_for_network(x)) { + // There is some network active with higher route priority, so this one is no longer needed + Scheduler.setNetworkExitTimer(10, x); + } else { + // Check for the special case where there are multiple fallback interfaces with the same route prio. + // Then only stop the fallback interfaces which are not the default route. + auto *NW_data = ESPEasy::net::getNWPluginData(x); + + if (NW_data) { + if (!NW_data->isDefaultRoute() && + (currentDefaultInterface->getRoutePrio() == Settings.getRoutePrio_for_network(x))) { + Scheduler.setNetworkExitTimer(10, x); + } + } + } + } + } +#endif // ifdef ESP32 String command; if (Function == NWPlugin::Function::NWPLUGIN_WRITE) { @@ -109,10 +157,11 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) } } #ifdef ESP32 + if (Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) { CheckRunningServices(); } -#endif +#endif // ifdef ESP32 return success; } @@ -221,7 +270,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (runtime_data && runtime_data->_connectedStats.isSet()) { const bool connected = runtime_data->_connectedStats.isOn(); - auto duration = connected + auto duration = connected ? runtime_data->_connectedStats.getLastOnDuration_ms() : runtime_data->_connectedStats.getLastOffDuration_ms(); @@ -230,9 +279,9 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) event->Par64_2 = runtime_data->_connectedStats.getCycleCount(); if (event->kvWriter) { - event->kvWriter->write({ - connected ? F("Connection Duration") : F("Disconnected Duration"), - format_msec_duration_HMS(duration) }); + event->kvWriter->write({ + connected ? F("Connection Duration") : F("Disconnected Duration"), + format_msec_duration_HMS(duration) }); if (!event->kvWriter->summaryValueOnly()) { event->kvWriter->write({ F("Number of Reconnects"), event->Par64_2 }); @@ -266,6 +315,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: #ifdef ESP32 case NWPlugin::Function::NWPLUGIN_GET_INTERFACE: @@ -305,105 +355,118 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (!success && NWPlugin::canQueryViaNetworkInterface(Function)) { String dummy_str; - if (do_NWPluginCall( + if (!do_NWPluginCall( networkDriverIndex, NWPlugin::Function::NWPLUGIN_GET_INTERFACE, event, dummy_str)) { - if (event->networkInterface != nullptr) { - switch (Function) - { - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO: -# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) + if (Function == NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO) { + if (Settings.getNetworkInterface_isFallback(networkIndex)) { + // Show route prio for fallback network interfaces as it is useful for determining which might be the fallback device. + event->Par1 = Settings.getRoutePrio_for_network(networkIndex); + event->Par3 = Settings.getNetworkInterfaceStartupDelay(networkIndex); - // TODO TD-er: Must also add option to set route prio - // See: https://github.com/espressif/arduino-esp32/pull/11617 - event->Par1 = event->networkInterface->getRoutePrio(); -# else // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) - event->Par1 = event->networkInterface->route_prio(); -# endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) - event->Par2 = event->networkInterface->isDefault(); - success = true; - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: - { - success = event->networkInterface->started(); - break; - } - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: - - if (event->kvWriter) { - event->kvWriter->write({ F("Name"), event->networkInterface->desc() }); - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: - - if (event->kvWriter) { - event->kvWriter->write({ F("Hostname"), event->networkInterface->getHostname() }); - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HW_ADDRESS: - - if (event->kvWriter) { - if (event->kvWriter->summaryValueOnly()) { - event->kvWriter->write({ - EMPTY_STRING, - concat(F("MAC: "), event->networkInterface->macAddress()) }); - } - else { - event->kvWriter->write({ - F("MAC"), - event->networkInterface->macAddress(), - KeyValueStruct::Format::PreFormatted - }); - } - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_IP: - { - if (event->kvWriter) { - PrintToString prstr; - success = NWPlugin::print_IP_address( - static_cast(event->Par1), - event->networkInterface, - prstr); - event->kvWriter->write({ F("ip"), prstr.getMove() }); - success = true; - } - break; - } - - case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: - { - IPAddress client_ip; - client_ip.fromString(str); - - if ((SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) && - !Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) { - success = NWPlugin::IP_in_subnet(client_ip, event->networkInterface); - } else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) { - const IPAddress low(SecuritySettings.AllowedIPrangeLow); - const IPAddress high(SecuritySettings.AllowedIPrangeHigh); - success = NWPlugin::ipInRange(client_ip, low, high) && - NWPlugin::IP_in_subnet(low, event->networkInterface) && - NWPlugin::IP_in_subnet(high, event->networkInterface); - } else { - success = true; - } - break; - } - - default: break; + success = true; } } + } else if (event->networkInterface != nullptr) { + switch (Function) + { + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO: +# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) + + // TODO TD-er: Must also add option to set route prio + // See: https://github.com/espressif/arduino-esp32/pull/11617 + event->Par1 = event->networkInterface->getRoutePrio(); +# else // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + event->Par1 = event->networkInterface->route_prio(); +# endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + event->Par2 = event->networkInterface->isDefault(); + + if (Settings.getNetworkInterface_isFallback(networkIndex)) { + event->Par3 = Settings.getNetworkInterfaceStartupDelay(networkIndex); + } + success = true; + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: + { + success = event->networkInterface->started(); + break; + } + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: + + if (event->kvWriter) { + event->kvWriter->write({ F("Name"), event->networkInterface->desc() }); + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: + + if (event->kvWriter) { + event->kvWriter->write({ F("Hostname"), event->networkInterface->getHostname() }); + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HW_ADDRESS: + + if (event->kvWriter) { + if (event->kvWriter->summaryValueOnly()) { + event->kvWriter->write({ + EMPTY_STRING, + concat(F("MAC: "), event->networkInterface->macAddress()) }); + } + else { + event->kvWriter->write({ + F("MAC"), + event->networkInterface->macAddress(), + KeyValueStruct::Format::PreFormatted + }); + } + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_IP: + { + if (event->kvWriter) { + PrintToString prstr; + success = NWPlugin::print_IP_address( + static_cast(event->Par1), + event->networkInterface, + prstr); + event->kvWriter->write({ F("ip"), prstr.getMove() }); + success = true; + } + break; + } + + case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: + { + IPAddress client_ip; + client_ip.fromString(str); + + if ((SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) && + !Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) { + success = NWPlugin::IP_in_subnet(client_ip, event->networkInterface); + } else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) { + const IPAddress low(SecuritySettings.AllowedIPrangeLow); + const IPAddress high(SecuritySettings.AllowedIPrangeHigh); + success = NWPlugin::ipInRange(client_ip, low, high) && + NWPlugin::IP_in_subnet(low, event->networkInterface) && + NWPlugin::IP_in_subnet(high, event->networkInterface); + } else { + success = true; + } + break; + } + + default: break; + } + } } #endif // ifdef ESP32 @@ -533,6 +596,7 @@ const NWPluginData_static_runtime* getDefaultRoute_NWPluginData_static_runtime() if (NW_data) { const int route_prio = NW_data->_routePrio; + if (route_prio > highest_prio) { index_highest_prio = i; highest_prio = route_prio; diff --git a/src/ESPEasy/net/Globals/NetworkState.cpp b/src/ESPEasy/net/Globals/NetworkState.cpp index 1be5bf28c..49b74538a 100644 --- a/src/ESPEasy/net/Globals/NetworkState.cpp +++ b/src/ESPEasy/net/Globals/NetworkState.cpp @@ -28,4 +28,4 @@ WiFiUDP portUDP; #ifdef ESP32 bool nonDefaultNetworkInterface_gotIP{}; #endif - +bool networkConnectionFailed{}; diff --git a/src/ESPEasy/net/Globals/NetworkState.h b/src/ESPEasy/net/Globals/NetworkState.h index 91af0e97a..df768b719 100644 --- a/src/ESPEasy/net/Globals/NetworkState.h +++ b/src/ESPEasy/net/Globals/NetworkState.h @@ -32,3 +32,4 @@ extern WiFiUDP portUDP; #ifdef ESP32 extern bool nonDefaultNetworkInterface_gotIP; #endif +extern bool networkConnectionFailed; diff --git a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp index 113bbbf0e..b533b9221 100644 --- a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp +++ b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp @@ -135,7 +135,7 @@ String NWPlugin_import_export::exportConfig( child->write({ F("enabled"), Settings.getNetworkEnabled(networkIndex) }); child->write({ F("route_prio"), Settings.getRoutePrio_for_network(networkIndex) }); child->write({ F("sn_block"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkIndex) }); - child->write({ F("start_delay"), Settings.getNetworkInterfaceStartupDelayAtBoot(networkIndex) }); + child->write({ F("start_delay"), Settings.getNetworkInterfaceStartupDelay(networkIndex) }); # if FEATURE_USE_IPV6 child->write({ F("en_ipv6"), Settings.getNetworkEnabled_IPv6(networkIndex) }); @@ -223,7 +223,7 @@ String NWPlugin_import_export::importConfig( break; case 2: Settings.setNetworkInterfaceSubnetBlockClientIP(networkIndex, bool_val); break; - case 3: Settings.setNetworkInterfaceStartupDelayAtBoot(networkIndex, value.toInt()); + case 3: Settings.setNetworkInterfaceStartupDelay(networkIndex, value.toInt()); break; # if FEATURE_USE_IPV6 case 4: Settings.setNetworkEnabled_IPv6(networkIndex, bool_val); diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp index d37e1e769..2dce0c493 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp @@ -27,7 +27,7 @@ namespace net { namespace wifi { # ifdef ESP32 -static NWPluginData_static_runtime stats_and_cache(true, &NW_PLUGIN_INTERFACE); +static NWPluginData_static_runtime stats_and_cache(true, &NW_PLUGIN_INTERFACE, "AP"); # else static NWPluginData_static_runtime stats_and_cache(true, "AP"); // Cannot use flash strings during init of static objects # endif // ifdef ESP32 @@ -93,9 +93,7 @@ bool NW002_data_struct_WiFi_AP::init(EventStruct *event) nw002_enable_NAPT = Settings.WiFi_AP_enable_NAPT(); # endif - if (Settings.StartAP_on_NW002_init()) { - ESPEasy::net::wifi::setAPinternal(true); - } + ESPEasy::net::wifi::setAPinternal(true); # ifdef ESP32 NW002_update_NAPT(); # endif diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp index 229bae1d7..16f2bce8d 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp @@ -504,7 +504,9 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (!(data && iface)) { return false; } if (data->started() && data->connected()) { - return EthLinkUp(); + if (EthLinkUp()) return true; + data->mark_connect_failed(); + return false; } ethPrintSettings(); @@ -512,6 +514,7 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { { addLog(LOG_LEVEL_ERROR, F("ETH: Settings not correct!!!")); data->mark_stop(); + data->mark_connect_failed(); return false; } @@ -565,8 +568,11 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (EthLinkUp()) { // We might miss the connected event, since we are already connected. data->mark_connected(); + } else { + data->mark_connect_failed(); } } else { + data->mark_connect_failed(); addLog(LOG_LEVEL_ERROR, F("ETH : Failed to initialize ETH")); } return success; diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index f3ae64fe3..fd7fc0c92 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -3,10 +3,12 @@ #ifdef USES_NW004 # include "../../../src/Globals/Settings.h" +# include "../../../src/Globals/SPIe.h" # include "../../../src/Helpers/ESPEasy_time_calc.h" # include "../../../src/Helpers/Hardware_GPIO.h" # include "../../../src/Helpers/LongTermOnOffTimer.h" +# include "../../../src/Helpers/SPI_Helper.h" # include "../../../src/Helpers/StringConverter.h" # include "../../../src/WebServer/Markup.h" @@ -37,8 +39,9 @@ namespace eth { # define NW004_KEY_GW 8 # define NW004_KEY_SN 9 # define NW004_KEY_DNS 10 +# define NW004_KEY_SPI_BUS 11 -# define NW004_MAX_KEY 11 +# define NW004_MAX_KEY 12 const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t key, bool displayString, KVS_StorageType::Enum& storageType) { @@ -52,6 +55,7 @@ const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t k case NW004_KEY_ETH_PIN_CS: return displayString ? F("Ethernet CS pin") : F("CS"); case NW004_KEY_ETH_PIN_IRQ: return displayString ? F("Ethernet IRQ pin") : F("IRQ"); case NW004_KEY_ETH_PIN_RST: return displayString ? F("Ethernet RST pin") : F("RST"); + case NW004_KEY_SPI_BUS: return displayString ? F("SPI Bus") : F("ethspibus"); case NW004_KEY_IP: storageType = KVS_StorageType::Enum::string_type; return F("IP"); @@ -225,6 +229,17 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) addFormNote(F("I²C-address of Ethernet PHY")); } { + if (Settings.getNrConfiguredSPI_buses()) { + const int key = NW004_KEY_SPI_BUS; + const uint8_t spiBus = _kvs->getValueAsInt(key); + KVS_StorageType::Enum storageType; + SPIInterfaceSelector(getLabelString(key, true, storageType), + getLabelString(key, false, storageType), + spiBus); + } else { + // TODO TD-er: Add error message as we need a SPI bus + } + const int gpio_keys[] = { NW004_KEY_ETH_PIN_CS, NW004_KEY_ETH_PIN_IRQ, @@ -302,7 +317,7 @@ bool NW004_data_struct_ETH_SPI::write_Eth_port(KeyValueWriter *writer) int8_t spi_gpios[3]{}; - if (!Settings.getSPI_pins(spi_gpios)) { return false; } + if (!Settings.getSPI_pins(spi_gpios), (uint8_t)_kvs->getValueAsInt(NW004_KEY_SPI_BUS)) { return false; } const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI"), F("CS"), F("IRQ"), F("RST") }; const int pins[] = { @@ -367,7 +382,7 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; - if (log.reserve(115)) { + if (log.reserve(125)) { const ESPEasy::net::EthPhyType_t phyType = static_cast(_kvs->getValueAsInt(NW004_KEY_ETH_PHY_TYPE)); @@ -375,7 +390,8 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { log += toString(phyType); log += F(" PHY Addr: "); log += _kvs->getValueAsInt(NW004_KEY_ETH_PHY_ADDR); - log += strformat(F(" CS: %d IRQ: %d RST: %d"), + log += strformat(F(" SPI bus: %d CS: %d IRQ: %d RST: %d"), + _kvs->getValueAsInt(NW004_KEY_SPI_BUS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST)); @@ -392,7 +408,9 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (!(data && iface)) { return false; } if (data->started() && data->connected()) { - return EthLinkUp(); + if (EthLinkUp()) return true; + data->mark_connect_failed(); + return false; } ethPrintSettings(); @@ -400,6 +418,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { { addLog(LOG_LEVEL_ERROR, F("ETH: Settings not correct!!!")); data->mark_stop(); + data->mark_connect_failed(); return false; } @@ -421,25 +440,27 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { const int rstPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST); const int csPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS); const int irqPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ); + const int spi_bus = _kvs->getValueAsInt(NW004_KEY_SPI_BUS); - spi_host_device_t SPI_host = Settings.getSPI_host(); + spi_host_device_t SPI_host = Settings.getSPI_host(spi_bus); if (SPI_host == spi_host_device_t::SPI_HOST_MAX) { addLog(LOG_LEVEL_ERROR, F("SPI not enabled")); - # ifdef ESP32C3 + # ifdef ESP32C3 // FIXME TD-er: Fallback for ETH01-EVO board SPI_host = spi_host_device_t::SPI2_HOST; - Settings.InitSPI = static_cast(SPI_Options_e::UserDefined); + Settings.InitSPI = static_cast(SPI_Options_e::UserDefined_VSPI); Settings.SPI_SCLK_pin = 7; Settings.SPI_MISO_pin = 3; Settings.SPI_MOSI_pin = 10; - # endif // ifdef ESP32C3 + # endif // ifdef ESP32C3 } // else - { + if (SPI_host != spi_host_device_t::SPI_HOST_MAX) { + // TODO TD-er: Do we need to include the CLK, MISO, MOSI pins in the call or do we need to start the SPI bus first? # if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( to_ESP_phy_type(phyType), @@ -447,7 +468,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { csPin, irqPin, rstPin, - SPI); + SPI_host); # else // if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( to_ESP_phy_type(phyType), @@ -480,8 +501,11 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (EthLinkUp()) { // We might miss the connected event, since we are already connected. data->mark_connected(); + } else { + data->mark_connect_failed(); } } else { + data->mark_connect_failed(); addLog(LOG_LEVEL_ERROR, F("ETH : Failed to initialize ETH")); } return success; diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp index 779789473..f1e1332ef 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp @@ -865,7 +865,7 @@ void NW005_begin_modem_task(void *parameter) { delay(100); } - while (timePassedSince(start) < 5000 && !NW_PLUGIN_INTERFACE.attached()); + while (timePassedSince(start) < 10000 && !NW_PLUGIN_INTERFACE.attached()); NW_PLUGIN_INTERFACE.mode(ESP_MODEM_MODE_CMUX); modem_task_data->AT_CPSI = NW_PLUGIN_INTERFACE.cmd(F("AT+CPSI?"), 3000); @@ -874,6 +874,9 @@ void NW005_begin_modem_task(void *parameter) NW_PLUGIN_INTERFACE.cmd(F("AT&D1"), 9000); digitalWrite(modem_task_data->dtrPin, HIGH); } + if (!NW_PLUGIN_INTERFACE.attached()) modem_task_data->modem_init_failed = true; + } else { + modem_task_data->modem_init_failed = true; } modem_task_data->modem_initialized = res; } @@ -1037,6 +1040,20 @@ bool NW005_data_struct_PPP_modem::handle_nwplugin_write(EventStruct *event, Stri return success; } +bool NW005_data_struct_PPP_modem::check_connect_failed() +{ + if (_modem_task_data.modem_init_failed) + { + _modem_task_data.modem_init_failed = false; + auto stats = getNWPluginData_static_runtime(); + if (stats) { + stats->mark_connect_failed(); + } + return true; + } + return false; +} + # if FEATURE_NETWORK_STATS bool NW005_data_struct_PPP_modem::initPluginStats() diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h index b0ab4e8fd..91b89a100 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h @@ -18,6 +18,7 @@ struct NW005_modem_task_data { int baud_rate = 115200; bool initializing{}; bool modem_initialized{}; + bool modem_init_failed{}; int dtrPin = -1; String logString; String AT_CPSI; // Result from "AT+CPSI?" @@ -66,6 +67,8 @@ static int32_t getNextKey_noCredentials(int32_t key); bool handle_nwplugin_write(EventStruct *event, String & str) override; + bool check_connect_failed(); + String write_AT_cmd(const String& cmd, int timeout = 1000); diff --git a/src/ESPEasy/net/_NW001_WiFi_STA.cpp b/src/ESPEasy/net/_NW001_WiFi_STA.cpp index 98b6b7bb5..04ee55543 100644 --- a/src/ESPEasy/net/_NW001_WiFi_STA.cpp +++ b/src/ESPEasy/net/_NW001_WiFi_STA.cpp @@ -78,7 +78,7 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin Settings.setRoutePrio_for_network(event->NetworkIndex, 100); # endif // ifdef ESP32 Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 1000); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 1000); Settings.ConnectFailRetryCount = 1; break; @@ -108,6 +108,12 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: { success = ESPEasy::net::wifi::WifiIsSTA(WiFi.getMode()); @@ -241,16 +247,17 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin break; } -# ifdef ESP8266 case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: { if (event->kvWriter) { - event->kvWriter->write({ F("Name"), F("sta") }); + event->kvWriter->write({ F("Name"), F("WiFi") }); success = true; } break; } +# ifdef ESP8266 + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: { if (event->kvWriter) { diff --git a/src/ESPEasy/net/_NW002_WiFi_AP.cpp b/src/ESPEasy/net/_NW002_WiFi_AP.cpp index ce88fb3cd..72993df91 100644 --- a/src/ESPEasy/net/_NW002_WiFi_AP.cpp +++ b/src/ESPEasy/net/_NW002_WiFi_AP.cpp @@ -62,8 +62,8 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin Settings.setRoutePrio_for_network(event->NetworkIndex, 10); # endif Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 10000); - Settings.StartAP_on_NW002_init(false); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 10000); + Settings.setNetworkInterface_isFallback(event->NetworkIndex, true); Settings.StartAPfallback_NoCredentials(true); Settings.DoNotStartAPfallback_ConnectFail(false); Settings.APfallback_autostart_max_uptime_m(0); @@ -88,11 +88,22 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin case NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN: { - success = ESPEasy::net::wifi::wifiAPmodeActivelyUsed(); + success = ESPEasy::net::wifi::WifiIsAP(WiFi.getMode()); +// success = ESPEasy::net::wifi::wifiAPmodeActivelyUsed(); break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + if (Settings.getNetworkInterface_isFallback(event->NetworkIndex)){ + success = ESPEasy::net::wifi::shouldStartAP_fallback(); + } else { + success = true; + } + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: { success = ESPEasy::net::wifi::WifiIsAP(WiFi.getMode()); @@ -246,7 +257,6 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin // Usually the AP will be started when no WiFi is defined, or the defined one cannot be found. This flag may prevent it. Settings.StartAPfallback_NoCredentials(isFormItemChecked(LabelType::WIFI_START_AP_NO_CREDENTIALS)); Settings.DoNotStartAPfallback_ConnectFail(!isFormItemChecked(LabelType::WIFI_START_AP_ON_CONNECT_FAIL)); - Settings.StartAP_on_NW002_init(isFormItemChecked(LabelType::WIFI_START_AP_ON_NW002_INIT)); Settings.APfallback_autostart_max_uptime_m(getFormItemInt(LabelType::WIFI_MAX_UPTIME_AUTO_START_AP)); Settings.APfallback_minimal_on_time_sec(getFormItemInt(LabelType::WIFI_AP_MINIMAL_ON_TIME)); @@ -260,8 +270,6 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin case NWPlugin::Function::NWPLUGIN_WEBFORM_LOAD: { - addFormCheckBox(LabelType::WIFI_START_AP_ON_NW002_INIT); - addFormSubHeader(F("Wifi AP Settings")); addFormPasswordBox(F("WPA AP Mode Key"), F("apkey"), SecuritySettings.WifiAPKey, 63); addFormNote(F("WPA Key must be at least 8 characters long")); diff --git a/src/ESPEasy/net/_NW003_ETH_RMII.cpp b/src/ESPEasy/net/_NW003_ETH_RMII.cpp index 73aa70457..303c4f16f 100644 --- a/src/ESPEasy/net/_NW003_ETH_RMII.cpp +++ b/src/ESPEasy/net/_NW003_ETH_RMII.cpp @@ -60,7 +60,7 @@ bool NWPlugin_003(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 50); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); ESPEasy_key_value_store kvs; ESPEasy::net::eth::NW003_data_struct_ETH_RMII::loadDefaults( @@ -96,6 +96,12 @@ bool NWPlugin_003(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { auto iface = ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getInterface(event->NetworkIndex); diff --git a/src/ESPEasy/net/_NW004_ETH_SPI.cpp b/src/ESPEasy/net/_NW004_ETH_SPI.cpp index 7b6d6dfb0..8059ae97e 100644 --- a/src/ESPEasy/net/_NW004_ETH_SPI.cpp +++ b/src/ESPEasy/net/_NW004_ETH_SPI.cpp @@ -55,7 +55,7 @@ bool NWPlugin_004(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 50); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); ESPEasy_key_value_store kvs; ESPEasy::net::eth::NW004_data_struct_ETH_SPI::loadDefaults( @@ -91,6 +91,12 @@ bool NWPlugin_004(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { auto iface = ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getInterface(event->NetworkIndex); diff --git a/src/ESPEasy/net/_NW005_PPP_modem.cpp b/src/ESPEasy/net/_NW005_PPP_modem.cpp index 5265a76d7..046313611 100644 --- a/src/ESPEasy/net/_NW005_PPP_modem.cpp +++ b/src/ESPEasy/net/_NW005_PPP_modem.cpp @@ -55,7 +55,7 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 20); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, true); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); break; } @@ -84,6 +84,12 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { if (event->kvWriter) { @@ -229,6 +235,16 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_TEN_PER_SECOND: + { + ESPEasy::net::ppp::NW005_data_struct_PPP_modem *NW_data = + static_cast(getNWPluginData(event->NetworkIndex)); + + if (NW_data) { + NW_data->check_connect_failed(); + } + break; + } case NWPlugin::Function::NWPLUGIN_FIFTY_PER_SECOND: { diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index 847b3fb2e..ad426e55c 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -145,6 +145,7 @@ void ESPEasyWiFi_t::loop() // WiFiEventData.warnedNoValidWiFiSettings = true; } + wifi_STA_data->mark_connect_failed(); wifi_STA_data->_establishConnectStats.clear(); @@ -185,6 +186,7 @@ void ESPEasyWiFi_t::loop() if (WiFi_AP_Candidates.hasCandidates()) { setState(WiFiState_e::WiFiOFF, 100); } else { + // FIXME TD-er: This might not be a responsibility of this state machine.... if (shouldStartAP_fallback()) { setState(WiFiState_e::AP_Fallback, Settings.APfallback_minimal_on_time_sec() * 1000); // TODO TD-er: Must keep track of whether the user has forced AP to be autostarted. @@ -258,6 +260,7 @@ void ESPEasyWiFi_t::loop() if (_state == WiFiState_e::STA_Connecting) { setState(WiFiState_e::STA_Reconnecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT); } else { + wifi_STA_data->mark_connect_failed(); setState(WiFiState_e::WiFiOFF); } } diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h index 4f47ef814..f54090c1f 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h @@ -73,8 +73,12 @@ private: STA_connected_state getSTA_connected_state() const; +public: + bool shouldStartAP_fallback() const; +private: + // WiFi_AP_Candidate _active_sta; // WiFi_AP_Candidate _AP_conf; diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index c9a2c8a04..62983691a 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -171,6 +171,10 @@ void exitWiFi() { ESPEasyWiFi.disable(); } void loopWiFi() { ESPEasyWiFi.loop(); } +bool shouldStartAP_fallback() { + return ESPEasyWiFi.shouldStartAP_fallback(); +} + # ifdef BOARD_HAS_SDIO_ESP_HOSTED // ******************************************************************************** @@ -228,9 +232,13 @@ String GetHostedFwVersion(EspHostTypes hostType) String GetHostedMCU() { // Function is not yet implemented in Arduino Core so emulate it here +#if defined(CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6) && CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6==1 + return String("ESP32-C6"); +#else if (equals(F(CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET), F("esp32c6"))) { return String("ESP32-C6"); } +#endif return String("Unknown"); } diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index def0dfb99..6f7eb1511 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -51,6 +51,8 @@ void initWiFi(); void exitWiFi(); void loopWiFi(); +bool shouldStartAP_fallback(); + # ifdef BOARD_HAS_SDIO_ESP_HOSTED // ******************************************************************************** diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp index 9cdddf39e..c8d205c4e 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi_abstracted_ESP8266.cpp @@ -33,14 +33,9 @@ bool WiFi_pre_STA_setup() { // FIXME TD-er: Should ESP8266 first disable autoconnect/autoreconnect? // On ESP32 this does clear the last used credentials, so it will be able to accept different credentials to connect to. - // Assign to 2 separate bools to make sure both are executed. - const bool autoConnect = WiFi.setAutoConnect(true); - const bool autoReconnect = WiFi.setAutoReconnect(true); -#ifndef LIMIT_BUILD_SIZE - if (!autoConnect || !autoReconnect) { - addLog(LOG_LEVEL_ERROR, F("WiFi : Disabling auto (re)connect failed")); - } -#endif + WiFi.setAutoConnect(Settings.SDK_WiFi_autoreconnect()); + WiFi.setAutoReconnect(Settings.SDK_WiFi_autoreconnect()); + WiFi.hostname(NetworkCreateRFCCompliantHostname().c_str()); delay(100); return true; } diff --git a/src/_C002.cpp b/src/_C002.cpp index a0088ee59..e2fa84d88 100644 --- a/src/_C002.cpp +++ b/src/_C002.cpp @@ -43,7 +43,7 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String& proto.usesExtCreds = true; proto.defaultPort = 1883; proto.usesID = true; - # if FEATURE_MQTT_TLS + #if FEATURE_MQTT_TLS proto.usesTLS = true; # endif // if FEATURE_MQTT_TLS break; diff --git a/src/_C004.cpp b/src/_C004.cpp index 84a9c9422..022b32507 100644 --- a/src/_C004.cpp +++ b/src/_C004.cpp @@ -23,9 +23,9 @@ bool CPlugin_004(CPlugin::Function function, struct EventStruct *event, String& proto.usesPassword = true; proto.defaultPort = 80; proto.usesID = true; - # if FEATURE_HTTP_TLS + #if FEATURE_HTTP_TLS proto.usesTLS = true; - # endif // if FEATURE_HTTP_TLS + # endif break; } diff --git a/src/_C009.cpp b/src/_C009.cpp index 1be83eb72..0016b5eba 100644 --- a/src/_C009.cpp +++ b/src/_C009.cpp @@ -150,7 +150,7 @@ bool do_process_c009_delay_queue(cpluginID_t cpluginID, const Queue_element_base // embed IP, important if there is NAT/PAT // char ipStr[20]; - // IPAddress ip = NetworkLocalIP(); + // IPAddress ip = ESPEasy::net::NetworkLocalIP(); // sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]}); esp->write({F("ip"), formatIP(ESPEasy::net::NetworkLocalIP())}); } diff --git a/src/_P037_MQTTImport.ino b/src/_P037_MQTTImport.ino index c5687b4db..44f158a5a 100644 --- a/src/_P037_MQTTImport.ino +++ b/src/_P037_MQTTImport.ino @@ -569,7 +569,7 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string) # if !defined(P037_LIMIT_BUILD_SIZE) || defined(P037_OVERRIDE) if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = strformat(F("IMPT : MQTT fetched json attribute: %s payload: "), + String log = strformat(F("IMPT : MQTT fetched json attribute: %s payload: %s"), key.c_str(), Payload.c_str()); diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index 641840db6..590e0c9a7 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -19,6 +19,8 @@ // Have fun ... Dominik /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-08-13 tonhuisman: Move most code to P039_PluginStruct * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2025-01-03 tonhuisman: Small code size reductions, cleanup of DEBUG level logging * 2024-01-04 tonhuisman: Minor corrections, formatted source using Uncrustify @@ -77,180 +79,11 @@ // #endif -# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) -# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) - # define PLUGIN_039 # define PLUGIN_ID_039 39 # define PLUGIN_NAME_039 "Environment - Thermosensors" # define PLUGIN_VALUENAME1_039 "Temperature" -# define P039_SET true -# define P039_RESET false - -// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we -// spend 1ms -// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device -# define P039_CS_Delay() delayMicroseconds(2u) - -# define P039_MAX_TYPE PCONFIG(0) -# define P039_TC_TYPE PCONFIG(1) -# define P039_FAM_TYPE PCONFIG(2) -# define P039_RTD_TYPE PCONFIG(3) -# define P039_CONFIG_4 PCONFIG(4) -# define P039_RTD_FILT_TYPE PCONFIG(5) -# define P039_RTD_LM_TYPE PCONFIG(6) -# define P039_RTD_LM_SHTDWN PCONFIG(7) -# define P039_RTD_RES PCONFIG_LONG(0) -# define P039_FLAGS PCONFIG_ULONG(3) -# define P039_TEMP_THRESHOLD_FLAG 0 -# define P039_RTD_OFFSET PCONFIG_FLOAT(0) -# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) - -# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value -# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT -# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value -# define P039_TC 0u -# define P039_RTD 1u - -# define P039_MAX6675 1 -# define P039_MAX31855 2 -# define P039_MAX31856 3 -# define P039_MAX31865 4 -# define P039_LM7x 5 - -// MAX 6675 related defines - -// bit masks to identify failures for MAX 6675 -# define MAX6675_TC_DEVID 0x0002u -# define MAX6675_TC_OC 0x0004u - -// MAX 31855 related defines - -// bit masks to identify failures for MAX 31855 -# define MAX31855_TC_OC 0x00000001u -# define MAX31855_TC_SC 0x00000002u -# define MAX31855_TC_SCVCC 0x00000004u -# define MAX31855_TC_GENFLT 0x00010000u - - -// MAX 31856 related defines - -// base address for read/write acces to MAX 31856 -# define MAX31856_READ_ADDR_BASE 0x00u -# define MAX31856_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31856 -# define MAX31856_CR0 0u -# define MAX31856_CR1 1u -# define MAX31856_MASK 2u -# define MAX31856_CJHF 3u -# define MAX31856_CJLF 4u -# define MAX31856_LTHFTH 5u -# define MAX31856_LTHFTL 6u -# define MAX31856_LTLFTH 7u -# define MAX31856_LTLFTL 8u -# define MAX31856_CJTO 9u -# define MAX31856_CJTH 10u -# define MAX31856_CJTL 11u -# define MAX31856_LTCBH 12u -# define MAX31856_LTCBM 13u -# define MAX31856_LTCBL 14u -# define MAX31856_SR 15u - -# define MAX31856_NO_REG 16u - -// bit masks to identify failures for MAX 31856 -# define MAX31856_TC_OC 0x01u -# define MAX31856_TC_OVUV 0x02u -# define MAX31856_TC_TCLOW 0x04u -# define MAX31856_TC_TCLHIGH 0x08u -# define MAX31856_TC_CJLOW 0x10u -# define MAX31856_TC_CJHIGH 0x20u -# define MAX31856_TC_TCRANGE 0x40u -# define MAX31856_TC_CJRANGE 0x80u - -// bit masks for access of configuration bits -# define MAX31856_SET_50HZ 0x01u -# define MAX31856_CLEAR_FAULTS 0x02u -# define MAX31856_FLT_ISR_MODE 0x04u -# define MAX31856_CJ_SENS_DISABLE 0x08u -# define MAX31856_FAULT_CTRL_MASK 0x30u -# define MAX31856_SET_ONE_SHOT 0x40u -# define MAX31856_SET_CONV_AUTO 0x80u - - -// RTD related defines - -// MAX 31865 related defines - -// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) -// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done -// alternatively ONE SHOT bit could be polled (system/SPI bus load !) -# define MAX31865_CONVERSION_TIME 70ul -# define MAX31865_BIAS_WAIT_TIME 10ul - -// MAX 31865 Main States -# define MAX31865_INIT_STATE 0u -# define MAX31865_BIAS_ON_STATE 1u -# define MAX31865_RD_STATE 2u -# define MAX31865_RDY_STATE 3u - -// sensor type -# define MAX31865_PT100 0u -# define MAX31865_PT1000 1u - -// base address for read/write acces to MAX 31865 -# define MAX31865_READ_ADDR_BASE 0x00u -# define MAX31865_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31865 -# define MAX31865_CONFIG 0u -# define MAX31865_RTD_MSB 1u -# define MAX31865_RTD_LSB 2u -# define MAX31865_HFT_MSB 3u -# define MAX31865_HFT_LSB 4u -# define MAX31865_LFT_MSB 5u -# define MAX31865_LFT_LSB 6u -# define MAX31865_FAULT 7u - -// total number of registers in MAX 31865 -# define MAX31865_NO_REG 8u - -// bit masks to identify failures for MAX 31865 -# define MAX31865_FAULT_HIGHTHRESH 0x80u -# define MAX31865_FAULT_LOWTHRESH 0x40u -# define MAX31865_FAULT_REFINLOW 0x20u -# define MAX31865_FAULT_REFINHIGH 0x10u -# define MAX31865_FAULT_RTDINLOW 0x08u -# define MAX31865_FAULT_OVUV 0x04u - -// bit masks for access of configuration bits -# define MAX31865_SET_50HZ 0x01u -# define MAX31865_CLEAR_FAULTS 0x02u -# define MAX31865_FAULT_CTRL_MASK 0x0Cu -# define MAX31865_SET_3WIRE 0x10u -# define MAX31865_SET_ONE_SHOT 0x20u -# define MAX31865_SET_CONV_AUTO 0x40u -# define MAX31865_SET_VBIAS_ON 0x80u - -// LM7x related defines - -// LM7x subtype defines -# define LM7x_SD70 0x00u -# define LM7x_SD71 0x01u -# define LM7x_SD74 0x04u -# define LM7x_SD121 0x05u -# define LM7x_SD122 0x06u -# define LM7x_SD123 0x07u -# define LM7x_SD124 0x08u -# define LM7x_SD125 0x09u - -// bit masks for access of configuration bits -# define LM7x_CONV_RDY 0x02u - - -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event); boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { @@ -269,6 +102,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } @@ -287,7 +121,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) # if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { - success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5);; + success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5); break; } # endif // if FEATURE_MQTT_DISCOVER @@ -316,121 +150,12 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) } - initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct()); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct(event)); P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // set the slaveSelectPin as an output: - init_SPI_CS_Pin(CS_pin_no); - - // initialize SPI: - SPI.setHwCs(false); - SPI.begin(); - - // ensure MODE3 access to SPI device - SPI.setDataMode(SPI_MODE3); - - /* - if (P039_MAX_TYPE == P039_MAX6675) { - - // SPI.setBitOrder(MSBFIRST); - } - */ - if (P039_MAX_TYPE == P039_MAX31855) { - // SPI.setBitOrder(MSBFIRST); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } + if (nullptr != P039_data) { + success = P039_data->begin(event); } - - - if (P039_MAX_TYPE == P039_MAX31856) { - // init string - content accoring to inital implementation of P039 - MAX31856 read function - // write to Adress 0x80 - // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to - // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO - uint8_t sendBuffer[11] = - { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, - 0xFF, 0x80, 0x00, 0x00 }; - - transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } - - // start on shot conversion for upcoming read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - } - - - if (P039_MAX_TYPE == P039_MAX31865) { - // two step initialization buffer - uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - - // write intially 0x00 to CONFIG register - write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); - - // activate 50Hz filter, clear all faults, no auto conversion, no conversion started - change8BitRegister(CS_pin_no, - MAX31865_RD_ADDRESS(MAX31865_CONFIG), - MAX31865_WR_ADDRESS(MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // configure 2/4-wire sensor connection as default - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // set HighFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); - - // set LowFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); - - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - if (nullptr != P039_data) { - // save current timer for next calculation - P039_data->timer = millis(); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - } - } - - /* - if (P039_MAX_TYPE == P039_LM7x) - { - // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 - } - */ - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); - } - # endif // ifndef BUILD_NO_DEBUG - - success = true; break; } @@ -443,7 +168,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *Foptions[] = { F("Thermocouple"), F("RTD") }; const int FoptionValues[] = { P039_TC, P039_RTD }; constexpr size_t optionCount = NR_ELEMENTS(FoptionValues); - FormSelectorOptions selector( optionCount, Foptions, FoptionValues); + FormSelectorOptions selector(optionCount, Foptions, FoptionValues); selector.reloadonchange = true; selector.addFormSelector(F("Sensor Family Type"), F("famtype"), family); } @@ -457,7 +182,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options[] = { F("MAX 6675"), F("MAX 31855"), F("MAX 31856") }; const int optionValues[] = { P039_MAX6675, P039_MAX31855, P039_MAX31856 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector( optionCount, options, optionValues); + FormSelectorOptions selector(optionCount, options, optionValues); selector.reloadonchange = true; selector.addFormSelector(F("Adapter IC"), F("maxtype"), choice); } @@ -485,7 +210,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int ToptionValues[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; constexpr size_t optionCount = NR_ELEMENTS(ToptionValues); - const FormSelectorOptions selector( optionCount, Toptions, ToptionValues); + const FormSelectorOptions selector(optionCount, Toptions, ToptionValues); selector.addFormSelector(F("Thermocouple type"), F("tctype"), P039_TC_TYPE); } { @@ -493,10 +218,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int CoptionValues[] = { 0, 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(CoptionValues); const FormSelectorOptions selector(optionCount, Coptions, CoptionValues); - selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); + selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); addUnit(F("sample(s)")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); } } else { @@ -521,17 +246,17 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int PToptionValues[] = { MAX31865_PT100, MAX31865_PT1000 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); const FormSelectorOptions selector(optionCount, PToptions, PToptionValues); - selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); + selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); } { const __FlashStringHelper *Coptions[] = { F("2-/4"), F("3") }; constexpr size_t optionCount = NR_ELEMENTS(Coptions); - const FormSelectorOptions selector( optionCount, Coptions); + const FormSelectorOptions selector(optionCount, Coptions); selector.addFormSelector(F("Connection Type"), F("contype"), P039_CONFIG_4); addUnit(F("wire")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); { addFormNumericBox(F("Reference Resistor"), F("res"), P039_RTD_RES, 0); @@ -610,64 +335,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_READ: { - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; + P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - float Plugin_039_Celsius = NAN; - - switch (MaxType) { - case P039_MAX6675: - Plugin_039_Celsius = readMax6675(event); - break; - case P039_MAX31855: - Plugin_039_Celsius = readMax31855(event); - break; - case P039_MAX31856: - Plugin_039_Celsius = readMax31856(event); - break; - case P039_MAX31865: - Plugin_039_Celsius = readMax31865(event); - break; - case P039_LM7x: - Plugin_039_Celsius = readLM7x(event); - break; - } - - if (isValidFloat(Plugin_039_Celsius)) - { - UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); - -# ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); - - const uint8_t valueCount = getValueCountForTask(event->TaskIndex); - - for (uint8_t i = 0; i < valueCount; ++i) - { - log += strformat( - F(" %s: %s"), - Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), - formatUserVarNoCheck(event, i).c_str()); - } - addLogMove(LOG_LEVEL_INFO, log); - } -# endif // ifndef BUILD_NO_DEBUG - - if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { - success = true; - } - } - else - { - UserVar.setFloat(event->TaskIndex, 0, NAN); - UserVar.setFloat(event->TaskIndex, 1, NAN); - - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); - } - success = false; + if (nullptr != P039_data) { + success = P039_data->read(event); } break; @@ -677,1391 +348,14 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; - - switch (MaxType) - { - case P039_MAX31865: - { - if ((nullptr != P039_data)) { - switch (event->Par1) - { - case MAX31865_BIAS_ON_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // activate one shot conversion - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_ONE_SHOT, - P039_SET); - - // set next state in sequence -> READ STATE - // start time to follow up on conversion and read the conversion result - P039_data->convReady = false; - Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - break; - } - case MAX31865_RD_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // read conversion result - P039_data->conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); - - // deactivate BIAS short after read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_RESET); - - // read fault register to get a full picture - P039_data->deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); - - // mark conversion as ready - P039_data->convReady = true; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, - strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - formatToHex_decimal(P039_data->conversionResult).c_str(), - formatToHex_decimal(P039_data->deviceFaults).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - - break; - } - case MAX31865_INIT_STATE: - default: - { - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " - "default; next state: MAX31865_BIAS_ON_STATE"), - getTaskDeviceName(event->TaskIndex).c_str())); - - // FIXME: Shouldn't this be in active code? // save current timer for next calculation - P039_data->timer = millis(); - } - # endif // ifndef BUILD_NO_DEBUG - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // set next state in sequence -> BIAS ON STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - - break; - } - } - } - break; - } - default: - { - break; - } + if (nullptr != P039_data) { + success = P039_data->plugin_tasktimer_in(event); } - success = true; break; } } return success; } -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event) -{ - const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; - const int FToptionValues[] = { 0, 1 }; - - const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); - selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); - addUnit(F("Hz")); - # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Filter power net frequency (50/60 Hz)")); - # else // ifndef LIMIT_BUILD_SIZE - addUnit(F("net frequency")); - # endif // ifndef LIMIT_BUILD_SIZE -} - -float readMax6675(struct EventStruct *event) -{ - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - uint8_t messageBuffer[2] = { 0 }; - uint16_t rawvalue = 0u; - - - // "transfer" 2 bytes to SPI to get 16 Bit return value - transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); - - // merge 16Bit return value from messageBuffer - rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue, - formatToHex_decimal(messageBuffer[0]).c_str(), - formatToHex_decimal(messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - // Open Thermocouple - // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the - // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin - // as possible. - // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance - const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); - - if (Plugin_039_SensorAttached) - { - // shift RAW value 3 Bits to the right to get the data - rawvalue >>= 3; - - // calculate Celsius with device resolution 0.25 K/bit - return rawvalue * 0.25f; - } - else - { - return NAN; - } -} - -float readMax31855(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - uint8_t messageBuffer[4] = { 0 }; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // "transfer" 0x0 and read the 32 Bit conversion register from the Chip - transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); - - // merge rawvalue from 4 bytes of messageBuffer - uint32_t rawvalue = - ((static_cast(messageBuffer[0]) << - 24) | - (static_cast(messageBuffer[1]) << - 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(200u))) { // reserve value derived from example log file - log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue); - - for (size_t i = 0u; i < 4; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(messageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - - // check for fault flags in LSB of 32 Bit messageBuffer - if (P039_data->sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { - // Fault code changed, log them - P039_data->sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : MAX31855 : "); - - if ((P039_data->sensorFault)) { - log += F("Fault resolved"); - } else { - log += F("Fault code :"); - - if (rawvalue & MAX31855_TC_OC) { - log += F(" Open (no connection)"); - } - - if (rawvalue & MAX31855_TC_SC) { - log += F(" Short-circuit to GND"); - } - - if (rawvalue & MAX31855_TC_SCVCC) { - log += F(" Short-circuit to Vcc"); - } - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s P039_data->sensorFault: "), - formatToHex_decimal(rawvalue).c_str(), - formatToHex_decimal(P039_data->sensorFault).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - } - - // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. - // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons - const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); - - if (Plugin_039_SensorAttached) - { - // Data is D[31:18] - // Shift RAW value 18 Bits to the right to get the data - rawvalue >>= 18; - - // Check for negative Values - // +25.00 0000 0001 1001 00 - // 0.00 0000 0000 0000 00 - // -0.25 1111 1111 1111 11 - // -1.00 1111 1111 1111 00 - // -250.00 1111 0000 0110 00 - // We're left with (32 - 18 =) 14 bits - int temperature = Plugin_039_convert_two_complement(rawvalue, 14); - - // Calculate Celsius - return temperature * 0.25f; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31856(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - - uint8_t registers[MAX31856_NO_REG] = { 0 }; - uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; - - messageBuffer[0] = MAX31856_READ_ADDR_BASE; - - // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip - transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); - - // transfer data from messageBuffer and get rid of initial address uint8_t - for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { - registers[i] = messageBuffer[i + 1]; - } - - // configure device for next conversion - // activate frequency filter according to configuration - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // set averaging and TC type - write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); - - - // start on shot conversion for next read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - - - // now derive raw value from respective registers - uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); - - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 :"); - - for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - log += F(" rawvalue: "); - log += formatToHex_decimal(rawvalue); - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // ignore TC Range Bit in case Voltage Modes are used - // datasheet: - // Thermocouple Out-of-Range fault. - // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). - // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. - // Note: The TC Range bit should be ignored in voltage mode. - uint8_t sr = registers[MAX31856_SR]; - - if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { - sr &= ~MAX31856_TC_TCRANGE; - } - - - // FIXED: c.k.i. : moved static fault flag to instance data structure - if ((nullptr != P039_data)) { - // P039_data->sensorFault = false; - - P039_data->sensorFault = (sr != 0); // Set new state - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - // FIXME TD-er: Part of expression is always false (sr == 0) - const bool faultResolved = (P039_data->sensorFault) && (sr == 0); - - if ((P039_data->sensorFault) || faultResolved) { - String log; - - if ((log.reserve(140u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 : "); - - if ((P039_data->sensorFault) == 0) { - log += F("Fault resolved"); - } else { - log += F("Fault :"); - - if (sr & MAX31856_TC_OC) { - log += F(" Open (no connection)"); - } - - if (sr & MAX31856_TC_OVUV) { - log += F(" Over/Under Voltage"); - } - - if (sr & MAX31856_TC_TCLOW) { - log += F(" TC Low"); - } - - if (sr & MAX31856_TC_TCLHIGH) { - log += F(" TC High"); - } - - if (sr & MAX31856_TC_CJLOW) { - log += F(" CJ Low"); - } - - if (sr & MAX31856_TC_CJHIGH) { - log += F(" CJ High"); - } - - if (sr & MAX31856_TC_TCRANGE) { - log += F(" TC Range"); - } - - if (sr & MAX31856_TC_CJRANGE) { - log += F(" CJ Range"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - - const bool Plugin_039_SensorAttached = (sr == 0); - - if (Plugin_039_SensorAttached) - { - rawvalue >>= 5; // bottom 5 bits are unused - // We're left with (24 - 5 =) 19 bits - - { - float temperature = 0; - - switch (P039_TC_TYPE) - { - case 8: - { - temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) - break; - } - case 12: - { - temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) - break; - } - default: - { - temperature = Plugin_039_convert_two_complement(rawvalue, 19); - - // Calculate Celsius - temperature /= 128.0f; - break; - } - } - - return temperature; - } - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31865(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - if (P039_data == nullptr) { - return NAN; - } - - uint8_t registers[MAX31865_NO_REG] = { 0 }; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(80u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 :"); - log += F(" P039_data->convReady: "); - log += boolToString(P039_data->convReady); - - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // read conversion result and faults from plugin data structure - // if pointer exists and conversion has been finished - if (P039_data->convReady) { - rawValue = P039_data->conversionResult; - registers[MAX31865_FAULT] = P039_data->deviceFaults; - } - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(160u))) { // reserve value derived from example log file - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); - } - - log = F("P039 : MAX31865 :"); - - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - // Prepare and start next conversion, before handling faults and rawValue - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // set frequency filter - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - - // configure read access with configuration from web interface - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // save current timer for next calculation - P039_data->timer = millis(); - - // set next state to MAX31865_BIAS_ON_STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - if (registers[MAX31865_FAULT]) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 : "); - - log += F("Fault : "); - log += formatToHex_decimal(registers[MAX31865_FAULT]); - log += F(" :"); - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) - { - log += F(" Under/Over voltage"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) - { - log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) - { - log += F(" REFIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) - { - log += F(" REFIN- > 0.85 x Bias"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) - { - log += F(" RTD Low Threshold"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) - { - log += F(" RTD High Threshold"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - # endif // ifndef BUILD_NO_DEBUG - - - bool ValueValid = false; - - if (registers[MAX31865_FAULT] == 0x00u) { - ValueValid = true; - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), - formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), - FsP(boolToString(ValueValid)))); - } - - # endif // ifndef BUILD_NO_DEBUG - - if (ValueValid) - { - rawValue >>= 1; // bottom fault bits is unused - - float temperature = Plugin_039_convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), - formatToHex_decimal(rawValue).c_str(), - temperature, - P039_RTD_TYPE, - P039_RTD_RES)); - } - - # endif // ifndef BUILD_NO_DEBUG - - // add offset handling from configuration webpage - temperature += P039_RTD_OFFSET; - - // Calculate Celsius - return temperature; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -void MAX31865_clearFaults(int8_t l_CS_pin_no) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); - - - // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) - l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); - l_reg |= MAX31865_CLEAR_FAULTS; - - // write configuration - write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); -} - -void MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) -{ - bool l_set_reset = false; - - // configure if 3 WIRE bit will be set/reset - switch (l_conType) - { - case 0: - l_set_reset = P039_RESET; - break; - case 1: - l_set_reset = P039_SET; - break; - default: - l_set_reset = P039_RESET; - break; - } - - // change to configuration register - change8BitRegister(l_CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_3WIRE, - l_set_reset); -} - -/**************************************************************************/ - -/*! - @brief Read the temperature in C from the RTD through calculation of the - resistance. Uses - http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf - technique - @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 - or 1000 - @param refResistor The value of the matching reference resistor, usually - 430 or 4300 - @returns Temperature in C - */ - -/**************************************************************************/ -float Plugin_039_convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) -{ - # define RTD_A 3.9083e-3f - # define RTD_B -5.775e-7f - - float Z1, Z2, Z3, Z4, Rt, temp; - - Rt = l_rawvalue; - Rt /= 32768u; - Rt *= refResistor; - - Z1 = -RTD_A; - Z2 = RTD_A * RTD_A - (4 * RTD_B); - Z3 = (4 * RTD_B) / RTDnominal; - Z4 = 2 * RTD_B; - - temp = Z2 + (Z3 * Rt); - temp = (sqrtf(temp) + Z1) / Z4; - - if (temp >= 0) { - return temp; - } - - Rt /= RTDnominal; - Rt *= 100; // normalize to 100 ohm - - float rpoly = Rt; - - temp = -242.02f; - temp += 2.2228f * rpoly; - rpoly *= Rt; // square - temp += 2.5859e-3f * rpoly; - rpoly *= Rt; // ^3 - temp -= 4.8260e-6f * rpoly; - rpoly *= Rt; // ^4 - temp -= 2.8183e-8f * rpoly; - rpoly *= Rt; // ^5 - temp += 1.5243e-10f * rpoly; - - return temp; -} - -uint16_t getNomResistor(uint8_t l_RType) -{ - uint16_t l_returnValue = 100u; - - switch (l_RType) - { - case MAX31865_PT100: - l_returnValue = 100u; - break; - case MAX31865_PT1000: - l_returnValue = 1000u; - break; - default: - l_returnValue = 100u; - break; - } - return l_returnValue; -} - -int Plugin_039_convert_two_complement(uint32_t value, int nr_bits) { - const bool negative = (value & (1 << (nr_bits - 1))) != 0; - int nativeInt; - - if (negative) { - // Add zeroes to the left to create the proper negative native-sized integer. - nativeInt = value | ~((1 << nr_bits) - 1); - } else { - nativeInt = value; - } - return nativeInt; -} - -float readLM7x(struct EventStruct *event) -{ - float temperature = 0.0f; - uint16_t device_id = 0u; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) - // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register - - rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); - - temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), - formatToHex_decimal(rawValue).c_str(), - formatToHex(device_id).c_str(), - temperature)); - } - # endif // ifndef BUILD_NO_DEBUG - - return temperature; -} - -float convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) -{ - float l_returnValue = 0.0f; - float l_lsbvalue = 0.0f; - uint8_t l_noBits = 0u; - int l_intTemperature = 0; - - switch (l_LM7xsubtype) - { - case LM7x_SD70: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - case LM7x_SD71: - l_rawValue >>= 2; - l_lsbvalue = 0.03125f; - l_noBits = 14u; - break; - case LM7x_SD74: - l_rawValue >>= 3; - l_lsbvalue = 0.0625f; - l_noBits = 13u; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_rawValue >>= 4; - l_lsbvalue = 0.0625f; - l_noBits = 12u; - break; - case LM7x_SD125: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 10u; - break; - default: // use lowest resolution as fallback if no device has been configured - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - } - - l_intTemperature = Plugin_039_convert_two_complement(l_rawValue, l_noBits); - - l_returnValue = l_intTemperature * l_lsbvalue; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, - strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex_decimal(l_LM7xsubtype).c_str(), - formatToHex_decimal(l_rawValue).c_str(), - l_noBits, - l_lsbvalue)); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -uint16_t readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) -{ - uint16_t l_returnValue = 0u; - uint16_t l_mswaitTime = 0u; - - - switch (l_LM7xsubType) - { - case LM7x_SD70: - case LM7x_SD71: - case LM7x_SD74: - l_mswaitTime = 300; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_mswaitTime = 320; - break; - case LM7x_SD125: - l_mswaitTime = 100; - break; - default: - l_mswaitTime = 500; - break; - } - - // // activate communication -> CS low - // handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - if (l_runMode) - { - // shutdown mode active -> conversion when called - uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; - - // send inital 4 bytes to wake the device and start the conversion - transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); - - // wait specific ms for conversion to be ready (TI datasheet per devices) - delay(l_mswaitTime); - - // send remaining 8 bytes to read the device ID and shutdown the device - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); - - // // wakeup device and start conversion - // // initial read of conversion result is obsolete - // SPI.transfer16(0xFFFF); - - // // (wakeup device with "all zero2 message in the last 8 bits - // SPI.transfer16(0xFF00); - - // //wait specific ms for conversion to be ready (TI datasheet per devices) - // delay(l_mswaitTime); - - // //read temperature value (16 Bit) - // l_returnValue = SPI.transfer16(0x0000); - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits ( maybe redundant, check with test) - // SPI.transfer16(0xFFFF); - } - else - { - // shutdown mode inactive -> normal background conversion during call cycle - uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); - - - // l_returnValue = SPI.transfer16(0x0000); //read temperature value (16 Bit) - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // start conversion until next read (8 Bit sufficient) - // // 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F allowed - else device goes to test mode (not desirable here) - // SPI.transfer(0x00); - // // SPI.transfer16(0x0000); - } - - // // stop communication -> CS high - // handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex(*(l_device_id)).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE - -/**************************************************************************/ - -/*! - @brief generic high level library to access SPI interface from plugins - with GPIO pin handled as CS - chri.kai.in 2021 - - Initial Revision - chri.kai.in 2021 - - TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library - - - /**************************************************************************/ - - -/**************************************************************************/ - -/*! - - @brief Identifying the CS pin from the event basic data structure - @param event pointer to the event structure; default GPIO is chosen as GPIO 15 - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -int get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on ESP8266 - if (CONFIG_PIN1 != -1) { - return CONFIG_PIN1; - } - return 15; // D8 -} - -/**************************************************************************/ - -/*! - @brief Initializing GPIO as OUTPUT for CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void init_SPI_CS_Pin(int8_t l_CS_pin_no) { - // set the slaveSelectPin as an output: - pinMode(l_CS_pin_no, OUTPUT); -} - -/**************************************************************************/ - -/*! - @brief Handling GPIO as CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { - P039_CS_Delay(); // tCWH (min) >= x00ns - digitalWrite(l_CS_pin_no, l_state); - P039_CS_Delay(); // tCC (min) >= x00ns -} - -/**************************************************************************/ - -/*! - @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 8 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) -{ - uint8_t l_messageBuffer[2] = { l_address, value }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 16 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) -{ - uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 8 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint8_t read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[2] = { l_address, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_messageBuffer[1]; -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 16 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint16_t read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; - uint16_t l_returnValue; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_returnValue).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -/**************************************************************************/ - -/*! - @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_noBytesToSend number of bytes to read/write from/to SPI - @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send - and provide read bytes from the SPI bus after the call - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) -{ - // activate communication -> CS low - handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - for (size_t i = 0u; i < l_noBytesToSend; i++) - { - l_inoutMessageBuffer[i] = SPI.transfer(l_inoutMessageBuffer[i]); - } - - // stop communication -> CS high - handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char - - for (uint8_t i = 0; i < l_noBytesToSend; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change16BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint16_t l_flagmask, bool l_set_reset) -{ - uint16_t l_reg = 0u; - - // read in config register - l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); - - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - -/**************************************************************************/ - -/*! - @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change8BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint8_t l_flagmask, bool l_set_reset) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); - - - // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - #endif // USES_P039 diff --git a/src/_P046_VentusW266.ino b/src/_P046_VentusW266.ino index 8a1063052..4ca28388c 100644 --- a/src/_P046_VentusW266.ino +++ b/src/_P046_VentusW266.ino @@ -189,7 +189,7 @@ boolean Plugin_046(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.reloadonchange = true; selector.addFormSelector(F("Plugin function"), F("p046"), choice); - addFormNote(F("Changing the function will reload this page.")); + // addFormNote(F("Changing the function will reload this page.")); } if (choice == 0) { diff --git a/src/_P047_i2c-soil-moisture-sensor.ino b/src/_P047_i2c-soil-moisture-sensor.ino index 95c021722..740be6e50 100644 --- a/src/_P047_i2c-soil-moisture-sensor.ino +++ b/src/_P047_i2c-soil-moisture-sensor.ino @@ -170,7 +170,7 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) selector.default_index = static_cast(P047_MODEL_CATNIP); selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("model"), P047_MODEL); - addFormNote(F("Changing the Sensor model will reload the page.")); + // addFormNote(F("Changing the Sensor model will reload the page.")); } if ((P047_MODEL_CATNIP == static_cast(P047_MODEL)) diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index eb21f25a3..8775baa2e 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -42,6 +42,9 @@ // /** History + * 2026-01-17 tonhuisman: Revert to using 'regular' Arduino GPIO functions for TM1637 displays on ESP8266 + * 2026-01-12 tonhuisman: Fix initialization of number of digits when upgrading to 20260108 build, + * formatted source with new Uncrustify config * 2024-09-28 tonhuisman: Switch to using DIRECT_PIN_IO, to get better timing-accuracy for TM1637 displays * 2024-09-27 tonhuisman: Add option to flash dot on second digit instead of the colon for blinking time * 2024-08-24 tonhuisman: Add compiletime define P073_USE_74HC595_OVERRIDE that, when, set to 1, allows 74HC595 displays to be included @@ -96,12 +99,13 @@ # include "src/PluginStructs/P073_data_struct.h" - boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; - switch (function) { - case PLUGIN_DEVICE_ADD: { + switch (function) + { + case PLUGIN_DEVICE_ADD: + { auto& dev = Device[++deviceCount]; dev.Number = PLUGIN_ID_073; dev.Type = DEVICE_TYPE_TRIPLE; @@ -113,13 +117,15 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_GET_DEVICENAME: { + case PLUGIN_GET_DEVICENAME: + { string = F(PLUGIN_NAME_073); break; } # if P073_SCROLL_TEXT || P073_USE_74HC595 - case PLUGIN_SET_DEFAULTS: { + case PLUGIN_SET_DEFAULTS: + { # if P073_SCROLL_TEXT P073_CFG_SCROLLSPEED = 10; // Default 10 * 0.1 sec scroll speed # endif // if P073_SCROLL_TEXT @@ -134,7 +140,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) } # endif // if P073_SCROLL_TEXT || P073_USE_74HC595 - case PLUGIN_WEBFORM_LOAD: { + case PLUGIN_WEBFORM_LOAD: + { addFormNote(F("TM1637: 1st=CLK-Pin, 2nd=DIO-Pin")); addFormNote(F("MAX7219: 1st=DIN-Pin, 2nd=CLK-Pin, 3rd=CS-Pin")); # if P073_USE_74HC595 @@ -151,7 +158,7 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(displtype); const FormSelectorOptions selector(optionCount, displtype); - selector.addFormSelector(F("Display Type"), F("displtype"), PCONFIG(0)); + selector.addFormSelector(F("Display Type"), F("displtype"), P073_CFG_DISPLAYTYPE); } # if P073_USE_74HC595 @@ -258,7 +265,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WEBFORM_SAVE: { + case PLUGIN_WEBFORM_SAVE: + { P073_CFG_DISPLAYTYPE = getFormItemInt(F("displtype")); P073_CFG_OUTPUTTYPE = getFormItemInt(F("displout")); P073_CFG_BRIGHTNESS = getFormItemInt(F("brightness")); @@ -292,12 +300,14 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: { + case PLUGIN_EXIT: + { success = true; break; } - case PLUGIN_INIT: { + case PLUGIN_INIT: + { initPluginTaskData(event->TaskIndex, new (std::nothrow) P073_data_struct()); P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -317,7 +327,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WRITE: { + case PLUGIN_WRITE: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -327,7 +338,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_ONCE_A_SECOND: { + case PLUGIN_ONCE_A_SECOND: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -339,7 +351,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) } # if P073_SCROLL_TEXT - case PLUGIN_TEN_PER_SECOND: { + case PLUGIN_TEN_PER_SECOND: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -353,7 +366,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) # if P073_USE_74HC595 - case PLUGIN_TASKTIMER_IN: { + case PLUGIN_TASKTIMER_IN: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 9f7ae8a03..fa2a36a4e 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -10,11 +10,12 @@ # define PLUGIN_NAME_095 "Display - TFT ILI934x/ILI948x" # define PLUGIN_VALUENAME1_095 "CursorX" # define PLUGIN_VALUENAME2_095 "CursorY" -# define PLUGIN_095_MAX_DISPLAY 1 /** * Changelog: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-07-07 tonhuisman: Remove explicit support for ILI9486 as all ILI9486 displays tested so far work with the ILI9488 driver, * sometimes with Invert display setting enabled (or they are actually ILI9488 displays...) * 2024-06-23 tonhuisman: Add support for ILI9488 displays by implementing an adapted library (jaretburkett/ILI9488) @@ -153,6 +154,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -179,20 +181,34 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) break; } + # ifndef LIMIT_BUILD_SIZE + case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: + { + const char *separator = event->String1.c_str(); // contains the NewLine sequence + string = strformat( + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: %s"), + separator, + formatGpioLabel(PIN(0), false).c_str(), + separator, + formatGpioLabel(PIN(1), false).c_str(), + separator, + formatGpioLabel(PIN(2), false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BUTTON_PIN, false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BACKLIGHT_PIN, false).c_str()); + success = true; + break; + } + # endif // ifndef LIMIT_BUILD_SIZE + case PLUGIN_SET_DEFAULTS: { - # ifdef ESP32 - - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P095_TFT_CS_HSPI; - } else { - PIN(0) = P095_TFT_CS; - } - # else // ifdef ESP32 + # ifdef ESP8266 PIN(0) = P095_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P095_TFT_DC; - PIN(2) = P095_TFT_RST; + PIN(1) = P095_TFT_DC; + PIN(2) = P095_TFT_RST; + # endif // ifdef ESP8266 P095_CONFIG_BUTTON_PIN = -1; // No button connected P095_CONFIG_BACKLIGHT_PIN = P095_BACKLIGHT_PIN; P095_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight @@ -451,7 +467,9 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { # if P095_ENABLE_ILI948X if (10 == P095_CONFIG_FLAG_GET_TYPE) { // If ILI9486 was selected, reset to ILI9488 @@ -470,7 +488,8 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) P095_CONFIG_FLAG_GET_CMD_TRIGGER)), P095_CONFIG_GET_COLOR_FOREGROUND, P095_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P095_CONFIG_DEFAULT_FONT diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index c698c2793..31deacad6 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -13,6 +13,11 @@ // #define PLUGIN_096_MAX_DISPLAY 1 // Unused +/** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-08-13 tonhuisman: Start changelog + */ + /* README.MD @@ -184,6 +189,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) dev.TimerOption = true; dev.TimerOptional = true; # endif // if P096_USE_EXTENDED_SETTINGS + dev.SpiBusSelect = true; break; } @@ -278,8 +284,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) options4, optionValues4); selector.addFormSelector(F("eInk display model"), - F("_type"), - P096_CONFIG_FLAG_GET_DISPLAYTYPE); + F("_type"), + P096_CONFIG_FLAG_GET_DISPLAYTYPE); } addFormSubHeader(F("Layout")); @@ -292,7 +298,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options2[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; int optionValues2[] = { 0, 1, 2, 3 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - const FormSelectorOptions selector( optionCount, options2, optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); selector.addFormSelector(F("Rotation"), F("_rotate"), P096_CONFIG_ROTATION); } # endif // ifdef P096_USE_ADA_GRAPHICS @@ -341,8 +347,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) } constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions); const FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); - selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"), - P096_CONFIG_FLAG_GET_COLORDEPTH); + selector.addFormSelector(F("Greyscale levels"), F("_colorDepth"), + P096_CONFIG_FLAG_GET_COLORDEPTH); } AdaGFXFormTextPrintMode(F("_mode"), P096_CONFIG_FLAG_GET_MODE); @@ -379,7 +385,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"), - P096_CONFIG_FLAG_GET_CMD_TRIGGER); + P096_CONFIG_FLAG_GET_CMD_TRIGGER); addFormNote(F("Select the command that is used to handle commands for this display.")); } @@ -502,7 +508,9 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, # if P096_USE_EXTENDED_SETTINGS new (std::nothrow) P096_data_struct(static_cast(P096_CONFIG_FLAG_GET_DISPLAYTYPE), @@ -511,6 +519,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) static_cast(P096_CONFIG_FLAG_GET_MODE), P096_CommandTrigger_toString(static_cast( P096_CONFIG_FLAG_GET_CMD_TRIGGER)), + spi_bus, P096_CONFIG_GET_COLOR_FOREGROUND, P096_CONFIG_GET_COLOR_BACKGROUND, static_cast(P096_CONFIG_FLAG_GET_COLORDEPTH), @@ -522,7 +531,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) P096_CONFIG_ROTATION, P096_CONFIG_FLAG_GET_FONTSCALE, AdaGFXTextPrintMode::ContinueToNextLine, - F("epd")) + F("epd"), + spi_bus) # endif // if P096_USE_EXTENDED_SETTINGS ); P096_data_struct *P096_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index 430c4afea..06c2bfeab 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -6,6 +6,7 @@ /** * Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Touch) * 2020-11-01 tonhuisman: Solved previous strange rotation settings to be compatible with TFT ILI9341 * 2020-11-01 tonhuisman: Add option to flip rotation by 180 deg, and command touch,flip,<0|1> @@ -52,10 +53,11 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_DEVICE_ADD: { auto& dev = Device[++deviceCount]; - dev.Number = PLUGIN_ID_099; - dev.Type = DEVICE_TYPE_SPI; - dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - dev.ValueCount = 3; + dev.Number = PLUGIN_ID_099; + dev.Type = DEVICE_TYPE_SPI; + dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + dev.ValueCount = 3; + dev.SpiBusSelect = true; break; } @@ -81,14 +83,14 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) break; } - # if FEATURE_MQTT_DISCOVER + #if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported success = true; break; } - # endif // if FEATURE_MQTT_DISCOVER + #endif // if FEATURE_MQTT_DISCOVER case PLUGIN_SET_DEFAULTS: { @@ -362,6 +364,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -374,7 +377,8 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z), bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION), P099_CONFIG_X_RES, - P099_CONFIG_Y_RES); + P099_CONFIG_Y_RES, + spi_bus); break; } diff --git a/src/_P104_max7219_Dotmatrix.ino b/src/_P104_max7219_Dotmatrix.ino index c00c18b4c..580b3f793 100644 --- a/src/_P104_max7219_Dotmatrix.ino +++ b/src/_P104_max7219_Dotmatrix.ino @@ -72,56 +72,61 @@ // Display of the selected zone is suspended until all provided dots are drawn. No quotes are needed around // the coordinate set. // -// History: -// 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts -// 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments -// Re-enable use of settings-version V3 after some more testing -// 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility -// 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by -// a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended -// CustomTaskSettings file, by using the offset as a starting location for the data elements -// b) Combine storing the size and the data-block in a single save action -// Apply toStringNoZero() converter to reduce the settings-data to be saved -// 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the -// original content when that's updated...) -// Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress -// 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces -// 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag -// 2021-10-03 tonhuisman: Add Inverted option per zone -// 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures -// 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation -// and saving/loading per zone. That should sove the numerous stack related crashes. -// 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections -// 2021-07-18 tonhuisman: Small optimizations and improvements -// 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font -// 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed -// update/correct some documentation -// 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings -// 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order -// tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements -// All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING -// Disabled P104_DEBUG_DEV by default -// 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build -// 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes -// 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes -// 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes -// 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible -// 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH -// 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure -// 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by -// P104_USE_DATETIME_OPTIONS -// Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size -// 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization -// Webform_Load now works on current settings if the plugin is active, instead of last stored settings -// Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration -// 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands -// Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, -// Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight -// 2021-06-16 tonhuisman: Implement Clock and Date (once per second) -// 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation -// 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones -// 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy -// 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + +/** History/Changelog: + * 2026-02-04 tonhuisman: Add ASCII font with Cyrillic extension characters + * Reformat sources with updated Uncrustify settings, use block-comment for History/Changelog + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts + * 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments + * Re-enable use of settings-version V3 after some more testing + * 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility + * 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by + * a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended + * CustomTaskSettings file, by using the offset as a starting location for the data elements + * b) Combine storing the size and the data-block in a single save action + * Apply toStringNoZero() converter to reduce the settings-data to be saved + * 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the + * original content when that's updated...) + * Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress + * 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces + * 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag + * 2021-10-03 tonhuisman: Add Inverted option per zone + * 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures + * 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation + * and saving/loading per zone. That should sove the numerous stack related crashes. + * 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections + * 2021-07-18 tonhuisman: Small optimizations and improvements + * 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font + * 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed + * update/correct some documentation + * 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings + * 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order + * tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements + * All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING + * Disabled P104_DEBUG_DEV by default + * 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build + * 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes + * 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes + * 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes + * 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible + * 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH + * 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure + * 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by + * P104_USE_DATETIME_OPTIONS + * Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size + * 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization + * Webform_Load now works on current settings if the plugin is active, instead of last stored settings + * Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration + * 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands + * Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, + * Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight + * 2021-06-16 tonhuisman: Implement Clock and Date (once per second) + * 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation + * 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones + * 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy + * 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + */ # define PLUGIN_104 # define PLUGIN_ID_104 104 @@ -132,37 +137,43 @@ # include "src/PluginStructs/P104_data_struct.h" - boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; - switch (function) { - case PLUGIN_DEVICE_ADD: { + switch (function) + { + case PLUGIN_DEVICE_ADD: + { auto& dev = Device[++deviceCount]; dev.Number = PLUGIN_ID_104; dev.Type = DEVICE_TYPE_SPI; dev.VType = Sensor_VType::SENSOR_TYPE_NONE; dev.ExitTaskBeforeSave = false; + dev.SpiBusSelect = true; break; } - case PLUGIN_SET_DEFAULTS: { + case PLUGIN_SET_DEFAULTS: + { CONFIG_PORT = -1; P104_CONFIG_HARDWARETYPE = static_cast(MD_MAX72XX::moduleType_t::FC16_HW); break; } - case PLUGIN_GET_DEVICENAME: { + case PLUGIN_GET_DEVICENAME: + { string = F(PLUGIN_NAME_104); break; } - case PLUGIN_GET_DEVICEGPIONAMES: { + case PLUGIN_GET_DEVICEGPIONAMES: + { event->String1 = formatGpioName_output(F("CS")); break; } - case PLUGIN_WEBFORM_LOAD: { + case PLUGIN_WEBFORM_LOAD: + { int8_t spi_pins[3]; Settings.getSPI_pins(spi_pins); int pinnr = -1; @@ -213,7 +224,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WEBFORM_SAVE: { + case PLUGIN_WEBFORM_SAVE: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); P104_CONFIG_ZONE_COUNT = getFormItemInt(F("zonecount")); @@ -244,7 +256,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_INIT: { + case PLUGIN_INIT: + { uint8_t numDevices = P104_CONFIG_TOTAL_UNITS; if (numDevices == 0) { // fallback value @@ -283,7 +296,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: { + case PLUGIN_EXIT: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr == P104_data) || (nullptr == P104_data->P)) { @@ -298,7 +312,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WRITE: { + case PLUGIN_WRITE: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P104_data) { @@ -308,7 +323,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_TEN_PER_SECOND: { + case PLUGIN_TEN_PER_SECOND: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr != P104_data) && (nullptr != P104_data->P)) { @@ -319,7 +335,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_ONCE_A_SECOND: { + case PLUGIN_ONCE_A_SECOND: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr != P104_data) && (nullptr != P104_data->P)) { diff --git a/src/_P105_AHT.ino b/src/_P105_AHT.ino index 1852aad1c..31d046c0c 100644 --- a/src/_P105_AHT.ino +++ b/src/_P105_AHT.ino @@ -155,7 +155,7 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optionCount, options, indices); selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ahttype"), P105_AHT_TYPE); - addFormNote(F("Changing Sensor model will reload the page.")); + // addFormNote(F("Changing Sensor model will reload the page.")); if (static_cast(AHTx_device_type::AHT10_DEVICE) == P105_AHT_TYPE) { addFormCheckBox(F("AHT10 Alternative initialization"), F("altinit"), P105_ALT_INIT); diff --git a/src/_P111_RC522_RFID.ino b/src/_P111_RC522_RFID.ino index 20ee7c320..844f7ece9 100644 --- a/src/_P111_RC522_RFID.ino +++ b/src/_P111_RC522_RFID.ino @@ -8,6 +8,7 @@ /** Changelog: * 2025-09-12 TD-er: Add support for 7-byte UID * 2025-08-20 TD-er: Speed-up reading + send immediate event + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-06-14 tonhuisman: Add support for Custom Value Type per task value * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for RFID) * Update changelog @@ -52,6 +53,7 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) dev.HasFormatUserVar = true; dev.SendDataOption = true; dev.CustomVTypeVar = true; + dev.SpiBusSelect = true; break; } @@ -183,7 +185,8 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN, spi_bus)); P111_data_struct *P111_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P111_data) { diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index 28f192e59..8d393d1c2 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -7,35 +7,38 @@ // ####################################################################################################### -// History: -// 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution -// 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution -// 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included -// 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller -// used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 -// By default (also) only enabled on ESP32 builds -// Disabled the ST7789 alternatives for now, as that's not verified on any hardware -// 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on -// some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 -// By default only enabled on ESP32 builds -// 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details -// 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) -// 2021-11-16 tonhuisman: P116: Change state from Development to Testing -// 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters -// as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution -// 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays -// Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') -// 2021-08-16 tonhuisman: P116: Add default color settings -// 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) -// 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) -// P116: Use more features of AdafruitGFX_helper -// AdafruitGFX: Apply 'Text Print Mode' options -// 2021-08 tonhuisman: Refactor into AdafruitGFX_helper -// 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines -// update to current ESPEasy state/style of development, make multi-instance possible -// 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) -// Added several features like display button, rotation -// 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 +/** History: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution + * 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution + * 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included + * 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller + * used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 + * By default (also) only enabled on ESP32 builds + * Disabled the ST7789 alternatives for now, as that's not verified on any hardware + * 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on + * some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 + * By default only enabled on ESP32 builds + * 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details + * 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) + * 2021-11-16 tonhuisman: P116: Change state from Development to Testing + * 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters + * as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution + * 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays + * Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') + * 2021-08-16 tonhuisman: P116: Add default color settings + * 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) + * 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) + * P116: Use more features of AdafruitGFX_helper + * AdafruitGFX: Apply 'Text Print Mode' options + * 2021-08 tonhuisman: Refactor into AdafruitGFX_helper + * 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines + * update to current ESPEasy state/style of development, make multi-instance possible + * 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) + * Added several features like display button, rotation + * 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 + */ # define PLUGIN_116 # define PLUGIN_ID_116 116 @@ -60,6 +63,7 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -84,11 +88,13 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) break; } + # ifndef LIMIT_BUILD_SIZE case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: { const char *separator = event->String1.c_str(); // contains the NewLine sequence string = strformat( - F("CS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: %s"), + separator, formatGpioLabel(PIN(0), false).c_str(), separator, formatGpioLabel(PIN(1), false).c_str(), @@ -101,21 +107,15 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) success = true; break; } + # endif // ifndef LIMIT_BUILD_SIZE case PLUGIN_SET_DEFAULTS: { - # ifdef ESP32 - - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P116_TFT_CS_HSPI; - } else { - PIN(0) = P116_TFT_CS; - } - # else // ifdef ESP32 + # ifdef ESP8266 PIN(0) = P116_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P116_TFT_DC; - PIN(2) = P116_TFT_RST; + PIN(1) = P116_TFT_DC; + PIN(2) = P116_TFT_RST; + # endif // ifdef ESP8266 P116_CONFIG_BUTTON_PIN = -1; // No button connected P116_CONFIG_BACKLIGHT_PIN = P116_BACKLIGHT_PIN; P116_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight @@ -342,7 +342,9 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P116_data_struct(static_cast(P116_CONFIG_FLAG_GET_TYPE), P116_CONFIG_FLAG_GET_ROTATION, @@ -355,7 +357,8 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) P116_CONFIG_FLAG_GET_CMD_TRIGGER)), P116_CONFIG_GET_COLOR_FOREGROUND, P116_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P116_CONFIG_DEFAULT_FONT diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino index 1c83be4f8..3cec889a8 100644 --- a/src/_P118_Itho.ino +++ b/src/_P118_Itho.ino @@ -17,6 +17,7 @@ // https://github.com/arjenhiemstra/IthoEcoFanRFT /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for ITHO) * Changelog is reverted and reformatted! * 05-03-2023 tonhuisman: Deprecate 'state' command, and add support for 'itho' as the main command @@ -135,6 +136,7 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; dev.ValueCount = 3; dev.SendDataOption = true; + dev.SpiBusSelect = true; break; } @@ -198,10 +200,12 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P118_DEBUG_LOG if (validGpio(P118_CSPIN) && (P118_IRQPIN != P118_CSPIN)) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P118_data_struct(P118_CSPIN, P118_IRQPIN, P118_CONFIG_LOG == 1, - P118_CONFIG_RF_LOG == 1)); + P118_CONFIG_RF_LOG == 1, + spi_bus)); P118_data_struct *P118_data = static_cast(getPluginTaskData(event->TaskIndex)); success = (nullptr != P118_data) && P118_data->plugin_init(event); diff --git a/src/_P125_ADXL345_SPI.ino b/src/_P125_ADXL345_SPI.ino index d90d8ed3f..f2a9295d5 100644 --- a/src/_P125_ADXL345_SPI.ino +++ b/src/_P125_ADXL345_SPI.ino @@ -11,6 +11,7 @@ */ /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for Accelerometer) * Use all relevant code from P120 (I2C variant of the same sensor) * 2021-12-10 tonhuisman, Start SPI interface version of ADXL345 plugin, based on P120 ADXL345 I2C plugin @@ -51,6 +52,7 @@ boolean Plugin_125(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.PluginStats = true; dev.OutputDataType = Output_Data_type_t::Simple; + dev.SpiBusSelect = true; break; } diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino index 6d956559e..27f54c015 100644 --- a/src/_P129_74HC165.ino +++ b/src/_P129_74HC165.ino @@ -187,7 +187,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("Number of chips (Q7 → DS)"), F("chipcnt"), P129_CONFIG_CHIP_COUNT); addUnit(concat(F("Daisychained 1.."), P129_MAX_CHIP_COUNT)); # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); + // addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); # endif // ifndef LIMIT_BUILD_SIZE } diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 746468adf..9f4ef8f10 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -1,11 +1,15 @@ +#include "_Plugin_Helper.h" #ifdef USES_P132 // ####################################################################################################### -// ######################### Plugin 132: INA3221 DC Voltage/Current sensor ############################### +// ############### Plugin 132: INA3221/INA226/INA228/INA260 DC Voltage/Current sensor #################### // ####################################################################################################### /** * Changelog: + * 2025-12-25 tonhuisman: Add P132_EXTENDED to also implement support for INA219, INA226, INA228, INA230, INA231 and INA260 + * INA230/INA231 are sometimes recognized as INA226 + * Without P132_EXTENDED set the original code is used (default for ESP8266). * 2025-01-18 tonhuisman: Implement support for MQTT AutoDiscovery * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for INA3221) * 2022-04-23 tonhuisman: Add separate settings for Conversion rate Voltage and Current @@ -16,18 +20,17 @@ // Initial development: ## 25 jan 2021 Fred van Duin #### -#include "_Plugin_Helper.h" +# include "./src/PluginStructs/P132_data_struct.h" -#define PLUGIN_132 -#define PLUGIN_ID_132 132 -#define PLUGIN_NAME_132 "Energy (DC) - INA3221" -#define PLUGIN_VALUENAME1_132 "Value1" -#define PLUGIN_VALUENAME2_132 "Value2" -#define PLUGIN_VALUENAME3_132 "Value3" -#define PLUGIN_VALUENAME4_132 "Value4" - -#include "./src/PluginStructs/P132_data_struct.h" +# define PLUGIN_132 +# define PLUGIN_ID_132 132 +# define PLUGIN_VALUENAME1_132 "Value1" +# define PLUGIN_VALUENAME2_132 "Value2" +# define PLUGIN_VALUENAME3_132 "Value3" +# define PLUGIN_VALUENAME4_132 "Value4" +// See below for original code with just INA3221 support +# if P132_EXTENDED boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; @@ -63,13 +66,449 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) break; } - #if FEATURE_MQTT_DISCOVER + # if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { - success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5);; + success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5); break; } - #endif // if FEATURE_MQTT_DISCOVER + # endif // if FEATURE_MQTT_DISCOVER + + case PLUGIN_I2C_HAS_ADDRESS: + case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: + { + const uint8_t i2cAddressValues[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F }; + constexpr size_t optionCount = NR_ELEMENTS(i2cAddressValues); + + if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t options = P132_DeviceType::Ina3221 == deviceType ? 4 : optionCount; + addFormSelectorI2C(F("i2c_addr"), options, i2cAddressValues, P132_I2C_ADDR); + addFormNote(F("Address selection: see docs")); + } else { + success = intArrayContains(optionCount, i2cAddressValues, event->Par1); + } + break; + } + + # if FEATURE_I2C_GET_ADDRESS + case PLUGIN_I2C_GET_ADDRESS: + { + event->Par1 = P132_I2C_ADDR; + success = true; + break; + } + # endif // if FEATURE_I2C_GET_ADDRESS + + case PLUGIN_SET_DEFAULTS: + { + P132_VALUE_1 = 1; // Configure 'randomly' + P132_VALUE_2 = 0; + P132_VALUE_3 = 3; + P132_VALUE_4 = 2; + uint32_t lSettings = 0; + set3BitToUL(lSettings, P132_FLAG_AVERAGE, 0x00); + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current + set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // V2 + P132_CONFIG_FLAGS = lSettings; + P132_MAX_CURRENT = 10; // Guestimated + P132_SHUNT_V2 = 100000; // Default != 0 + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + { + const __FlashStringHelper*typOptions[] = { + toString(P132_DeviceType::Ina3221), + toString(P132_DeviceType::Ina219), + toString(P132_DeviceType::Ina226), + toString(P132_DeviceType::Ina228), + toString(P132_DeviceType::Ina230), + toString(P132_DeviceType::Ina231), + toString(P132_DeviceType::Ina260), + }; + constexpr size_t typOptionsCount = NR_ELEMENTS(typOptions); + + const int typeOptionIndexes[] = { + static_cast(P132_DeviceType::Ina3221), + static_cast(P132_DeviceType::Ina219), + static_cast(P132_DeviceType::Ina226), + static_cast(P132_DeviceType::Ina228), + static_cast(P132_DeviceType::Ina230), + static_cast(P132_DeviceType::Ina231), + static_cast(P132_DeviceType::Ina260), + }; + FormSelectorOptions typSelector(typOptionsCount, typOptions, typeOptionIndexes); + typSelector.reloadonchange = true; + typSelector.addFormSelector(F("INA type"), F("ityp"), P132_INA_TYPE); + } + + addFormNumericBox(F("Max. Current"), F("mcur"), P132_MAX_CURRENT, 1, 1022); + addUnit(F("1..1022 A")); + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + + { + const __FlashStringHelper *varOptions[9]; + int varValues[9]; + + if (P132_DeviceType::Ina3221 == deviceType) + { + varOptions[0] = F("Voltage channel 1"); + varOptions[1] = F("Current channel 1"); + varOptions[2] = F("Power channel 1"); + varOptions[3] = F("Voltage channel 2"); + varOptions[4] = F("Current channel 2"); + varOptions[5] = F("Power channel 2"); + varOptions[6] = F("Voltage channel 3"); + varOptions[7] = F("Current channel 3"); + varOptions[8] = F("Power channel 3"); + varValues[0] = 1; + varValues[1] = 0; + varValues[2] = 6; + varValues[3] = 3; + varValues[4] = 2; + varValues[5] = 7; + varValues[6] = 5; + varValues[7] = 4; + varValues[8] = 8; + } + else + { + varOptions[0] = F("Voltage"); + varOptions[1] = F("Current"); + varOptions[2] = F("Power"); + varValues[0] = 1; + varValues[1] = 0; + varValues[2] = 6; + } + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + if (P132_INA_PREVIOUS != P132_INA_TYPE) { + P132_VALUE_1 = 1; // Configure 'randomly' Voltage/Current/ + P132_VALUE_2 = 0; + + if (P132_DeviceType::Ina3221 == deviceType) { + P132_VALUE_3 = 3; // Voltage/Current + P132_VALUE_4 = 2; + } else { + P132_VALUE_3 = 6; // Power + } + + if (P132_DeviceType::Ina219 == deviceType) { + P132_SET_V2_CONVERSION_B(0b1000); + P132_SET_V2_CONVERSION_S(0b1000); + } else if (P132_DeviceType::Ina228 == deviceType) { + P132_SET_V2_CONVERSION_B(0b0101); + P132_SET_V2_CONVERSION_S(0b0101); + } else { + P132_SET_V2_CONVERSION_B(0b0100); + P132_SET_V2_CONVERSION_S(0b0100); + } + + if (P132_DeviceType::Ina260 == deviceType) { + P132_SHUNT_V2 = 2000; // INA260 has 2 mOhm shunt built-in + } + } + + const FormSelectorOptions selector(optionCount, varOptions, varValues); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + selector.addFormSelector( + concat(F("Power value "), r + 1), + getPluginCustomArgName(r), + PCONFIG(P132_CONFIG_BASE + r)); + } + } + + addFormSubHeader(F("Hardware")); + + { + const __FlashStringHelper *varShuntOptions[] = { + F("0.1 Ω"), + F("0.01 Ω"), + F("0.015 Ω"), // INA228 often used + F("0.005 Ω"), + F("0.002 Ω"), // INA260 built-in + F("20 A @ 75mV"), + F("30 A @ 75mV"), + F("50 A @ 75mV"), + F("75 A @ 75mV"), + F("100 A @ 75mV"), + F("150 A @ 75mV"), + F("200 A @ 75mV"), + F("300 A @ 75mV"), + F("400 A @ 75mV"), + F("500 A @ 75mV"), + F("600 A @ 75mV"), + F("750 A @ 75mV"), + F("1000 A @ 75mV"), + }; + + /* *INDENT-OFF* */ + const int shuntValues[] = { 100000, 10000, 15000, 5000, 2000, // 'Ohm' + 3750, 2500, 1500, 1000, 750, 500, 375, 250, 188, 150, 125, 100, 75 }; // Amp@75mV + // Values in microOhm + /* *INDENT-ON* */ + + constexpr size_t optionCount = NR_ELEMENTS(shuntValues); + FormSelectorOptions selector(optionCount, varShuntOptions, shuntValues); + selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt + const uint32_t shunt = P132_CFG_VERSION != P132_GET_CFG_VERSION ? (100 / P132_SHUNT) * 1000 : P132_SHUNT_V2; + selector.addFormSelector(F("Shunt resistor"), F("shunt"), shunt); + addUnit(F("Ohm / Ampere @ 75mV")); + addFormNote(F("Select as is installed on the board.")); + } + + addFormSubHeader(F("Measurement")); + + if (P132_DeviceType::Ina219 != deviceType) { + const __FlashStringHelper *averagingSamples[] = { + F("1"), + F("4"), + F("16"), + F("64"), + F("128"), + F("256"), + F("512"), + F("1024"), + }; + const int averageValue[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; + constexpr size_t optionCount = NR_ELEMENTS(averageValue); + const FormSelectorOptions selector(optionCount, averagingSamples, averageValue); + + selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE); + } + + { + const __FlashStringHelper *conversionRates[11]; + int conversionValues[11]; + + if (P132_DeviceType::Ina219 == deviceType) { + conversionRates[0] = F("9 bits / 84 µsec"); + conversionRates[1] = F("10 bits / 148 µsec"); + conversionRates[2] = F("11 bits / 276 µsec"); + conversionRates[3] = F("12 bits / 532 µsec"); + conversionRates[4] = F("2 samples / 1.106 msec"); + conversionRates[5] = F("4 samples / 2.13 msec"); + conversionRates[6] = F("8 samples / 4.26 msec"); + conversionRates[7] = F("16 samples / 8.51 msec"); + conversionRates[8] = F("32 samples / 17.02 msec"); + conversionRates[9] = F("54 samples / 34.05 msec"); + conversionRates[10] = F("128 samples / 68.10 msec"); + conversionValues[0] = 0b0000; + conversionValues[1] = 0b0001; + conversionValues[2] = 0b0010; + conversionValues[3] = 0b1000; + conversionValues[4] = 0b1001; + conversionValues[5] = 0b1010; + conversionValues[6] = 0b1011; + conversionValues[7] = 0b1100; + conversionValues[8] = 0b1101; + conversionValues[9] = 0b1110; + conversionValues[10] = 0b1111; + } else { + if (P132_DeviceType::Ina228 == deviceType) { + conversionRates[0] = F("50 µsec"); + conversionRates[1] = F("84 µsec"); + conversionRates[2] = F("150 µsec"); + conversionRates[3] = F("280 µsec"); + conversionRates[4] = F("588 µsec"); + conversionRates[5] = F("1.052 msec"); + conversionRates[6] = F("2.074 msec"); + conversionRates[7] = F("4.120 msec"); + } else { + conversionRates[0] = F("140 µsec"); + conversionRates[1] = F("204 µsec"); + conversionRates[2] = F("332 µsec"); + conversionRates[3] = F("588 µsec"); + conversionRates[4] = F("1.1 msec"); + conversionRates[5] = F("2.116 msec"); + conversionRates[6] = F("4.156 msec"); + conversionRates[7] = F("8.244 msec"); + } + conversionValues[0] = 0b0000; + conversionValues[1] = 0b0001; + conversionValues[2] = 0b0010; + conversionValues[3] = 0b0011; + conversionValues[4] = 0b0100; + conversionValues[5] = 0b0101; + conversionValues[6] = 0b0110; + conversionValues[7] = 0b0111; + } + + const size_t optionCount = P132_DeviceType::Ina219 == deviceType ? 11 : 8; + FormSelectorOptions selector(optionCount, conversionRates, conversionValues); + + // INA219: 12 bit / 523 usec INA228: 1.052 msec, other INA: 1.1 msec + selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : + (P132_DeviceType::Ina228 == deviceType ? 0b0101 : 0b0100); + const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_B : P132_GET_V2_CONVERSION_B; + const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), convB); + selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), convS); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P132_I2C_ADDR = getFormItemInt(F("i2c_addr")); + P132_INA_PREVIOUS = P132_INA_TYPE; + P132_INA_TYPE = getFormItemInt(F("ityp")); + P132_MAX_CURRENT = getFormItemInt(F("mcur")); + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + PCONFIG(P132_CONFIG_BASE + r) = getFormItemIntCustomArgName(r); + } + P132_SHUNT_V2 = getFormItemInt(F("shunt")); + + uint32_t lSettings = 0; + + if (P132_DeviceType::Ina219 != deviceType) { + set3BitToUL(lSettings, P132_FLAG_AVERAGE, getFormItemInt(F("average"))); + } + + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, getFormItemInt(F("conv_v"))); + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, getFormItemInt(F("conv_c"))); + + set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // Write version update + P132_CONFIG_FLAGS = lSettings; + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P132_data_struct(event)); + P132_data_struct *P132_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = nullptr != P132_data && P132_data->isInitialized(); + + break; + } + + case PLUGIN_READ: + { + P132_data_struct *P132_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P132_data) { + return success; + } + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + // VALUES 1..4 + const uint8_t reg = static_cast(PCONFIG(P132_CONFIG_BASE + r)); + uint8_t channel = 0; // + + if ((2 == reg) || (3 == reg) || (7 == reg)) { + channel = 1; + } else if ((4 == reg) || (5 == reg) || (8 == reg)) { + channel = 2; + } + + if (P132_data->conversionFinished(channel)) { + switch (reg) { + case 0: // Current + case 2: // Current + case 4: // Current + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusCurrent_mA(channel)); + break; + case 1: // Voltage + case 3: // Voltage + case 5: // Voltage + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusVoltage_V(channel) + + (P132_data->getShuntVoltage_mV(channel) / 1000.0f)); + break; + case 6: // Power + case 7: // Power + case 8: // Power + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusPower_mW(channel)); + break; + } + success = true; // Only if we have read data + } + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("%s: Values: %.2f/%.2f/%.2f/%.2f"), + FsP(toString(static_cast(P132_INA_TYPE))), + UserVar.getFloat(event->TaskIndex, 0), + UserVar.getFloat(event->TaskIndex, 1), + UserVar.getFloat(event->TaskIndex, 2), + UserVar.getFloat(event->TaskIndex, 3))); + } + # endif // ifndef BUILD_NO_DEBUG + + break; + } + } + + return success; +} + +# endif // if P132_EXTENDED + +# if !P132_EXTENDED +boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + auto& dev = Device[++deviceCount]; + dev.Number = PLUGIN_ID_132; + dev.Type = DEVICE_TYPE_I2C; + dev.VType = Sensor_VType::SENSOR_TYPE_QUAD; + dev.FormulaOption = true; + dev.ValueCount = 4; + dev.SendDataOption = true; + dev.TimerOption = true; + dev.PluginStats = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_132); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_132)); + break; + } + + # if FEATURE_MQTT_DISCOVER + case PLUGIN_GET_DISCOVERY_VTYPES: + { + success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5); + break; + } + # endif // if FEATURE_MQTT_DISCOVER case PLUGIN_I2C_HAS_ADDRESS: case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: @@ -85,14 +524,14 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) break; } - #if FEATURE_I2C_GET_ADDRESS + # if FEATURE_I2C_GET_ADDRESS case PLUGIN_I2C_GET_ADDRESS: { event->Par1 = P132_I2C_ADDR; success = true; break; } - #endif // if FEATURE_I2C_GET_ADDRESS + # endif // if FEATURE_I2C_GET_ADDRESS case PLUGIN_SET_DEFAULTS: { @@ -126,7 +565,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) for (uint8_t r = 0; r < VARS_PER_TASK; ++r) { selector.addFormSelector( concat(F("Power value "), r + 1), - getPluginCustomArgName(r), + getPluginCustomArgName(r), PCONFIG(P132_CONFIG_BASE + r)); } } @@ -165,7 +604,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(averageValue); FormSelectorOptions selector(optionCount, averagingSamples, averageValue); selector.default_index = 0b000; - selector.addFormSelector(F("Averaging samples"),F("average"),P132_GET_AVERAGE); + selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE); addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!")); } @@ -247,7 +686,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) } } - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("INA3221: Values: %.2f/%.2f/%.2f/%.2f"), @@ -256,7 +695,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) UserVar[event->BaseVarIndex + 2], UserVar[event->BaseVarIndex + 3])); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG success = true; break; @@ -266,4 +705,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) return success; } +# endif // if !P132_EXTENDED + #endif // USES_P132 diff --git a/src/_P135_SCD4x.ino b/src/_P135_SCD4x.ino index 36507c325..c19a295b2 100644 --- a/src/_P135_SCD4x.ino +++ b/src/_P135_SCD4x.ino @@ -121,7 +121,7 @@ boolean Plugin_135(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ptype"), P135_SENSOR_TYPE); # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef LIMIT_BUILD_SIZE } diff --git a/src/_P137_AXP192.ino b/src/_P137_AXP192.ino index 7bdad37fb..342477f5e 100644 --- a/src/_P137_AXP192.ino +++ b/src/_P137_AXP192.ino @@ -207,9 +207,9 @@ boolean Plugin_137(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = !Settings.isPowerManagerTask(event->TaskIndex); selector.addFormSelector(F("Predefined device configuration"), F("predef"), 0); - if (!Settings.isPowerManagerTask(event->TaskIndex)) { - addFormNote(F("Page will reload when selection is changed.")); - } + // if (!Settings.isPowerManagerTask(event->TaskIndex)) { + // addFormNote(F("Page will reload when selection is changed.")); + // } const P137_PredefinedDevices_e current_ = static_cast(P137_CURRENT_PREDEFINED); diff --git a/src/_P141_PCD8544_Nokia5110.ino b/src/_P141_PCD8544_Nokia5110.ino index 7c79d281a..2511a34b0 100644 --- a/src/_P141_PCD8544_Nokia5110.ino +++ b/src/_P141_PCD8544_Nokia5110.ino @@ -8,6 +8,7 @@ /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-08-12 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included * 2022-10-08 tonhuisman: Enable PLUGIN_GET_CONFIG_VALUE event to get runtime info from plugin * 2022-09-24 tonhuisman: Store inverted setting when changed via inv subcommand (not saved) @@ -53,6 +54,7 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = false; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -248,7 +250,9 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P141_data_struct(P141_CONFIG_FLAG_GET_ROTATION, P141_CONFIG_FLAG_GET_FONTSCALE, @@ -262,7 +266,8 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) ADAGFX_WHITE, ADAGFX_BLACK, bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_BACK_FILL) == 0, - bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1 + bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1, + spi_bus # if ADAGFX_FONTS_INCLUDED , P141_CONFIG_DEFAULT_FONT diff --git a/src/_P143_I2C_Rotary.ino b/src/_P143_I2C_Rotary.ino index 9303670f5..cff8e6a03 100644 --- a/src/_P143_I2C_Rotary.ino +++ b/src/_P143_I2C_Rotary.ino @@ -194,7 +194,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optionCount, selectModeOptions, selectModeValues); selector.reloadonchange = true; selector.addFormSelector(F("Encoder type"), F("pdevice"), P143_ENCODER_TYPE); - addFormNote(F("Changing the Encoder type will reload the page and reset Encoder specific settings to default!")); + // addFormNote(F("Changing the Encoder type will reload the page and reset Encoder specific settings to default!")); } P143_DeviceType_e device = static_cast(P143_ENCODER_TYPE); diff --git a/src/_P147_SGP4x.ino b/src/_P147_SGP4x.ino index 5d74074d6..6154cd5dc 100644 --- a/src/_P147_SGP4x.ino +++ b/src/_P147_SGP4x.ino @@ -135,13 +135,13 @@ boolean Plugin_147(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ptype"), P147_SENSOR_TYPE); # ifndef BUILD_NO_DEBUG - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef BUILD_NO_DEBUG } addFormSelector_YesNo(F("Use Compensation"), F("comp"), P147_GET_USE_COMPENSATION, true); # ifndef BUILD_NO_DEBUG - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef BUILD_NO_DEBUG if (P147_GET_USE_COMPENSATION) { diff --git a/src/_P150_TMP117.ino b/src/_P150_TMP117.ino index ab869e07d..3b924ac17 100644 --- a/src/_P150_TMP117.ino +++ b/src/_P150_TMP117.ino @@ -162,7 +162,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Conversion mode"), F("conv"), P150_GET_CONF_CONVERSION_MODE); # ifndef BUILD_NO_DEBUG - addFormNote(F("Changing this setting will save and reload this page.")); + // addFormNote(F("Changing this setting will save and reload this page.")); # endif // ifndef BUILD_NO_DEBUG } @@ -196,7 +196,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) addFormSelector_YesNo(F("Enable 'Raw' value"), F("raw"), P150_GET_OPT_ENABLE_RAW ? 1 : 0, true); # ifndef BUILD_NO_DEBUG - addFormNote(F("Changing this setting will save and reload this page.")); + // addFormNote(F("Changing this setting will save and reload this page.")); # endif // ifndef BUILD_NO_DEBUG addFormCheckBox(F("Log measured values (INFO)"), F("log"), P150_GET_OPT_ENABLE_LOG); diff --git a/src/_P159_LD2410.ino b/src/_P159_LD2410.ino index cff0418d6..161150998 100644 --- a/src/_P159_LD2410.ino +++ b/src/_P159_LD2410.ino @@ -230,7 +230,7 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { addFormSelector_YesNo(F("Engineering mode"), F("eng"), P159_GET_ENGINEERING_MODE, true); - addFormNote(F("When changing this setting the page will be reloaded")); + // addFormNote(F("When changing this setting the page will be reloaded")); addFormCheckBox(F("Generate Events only when changed"), F("diff"), P159_GET_UPDATE_DIFF_ONLY); diff --git a/src/_P162_MCP42xxx.ino b/src/_P162_MCP42xxx.ino index 77d3454c6..769949205 100644 --- a/src/_P162_MCP42xxx.ino +++ b/src/_P162_MCP42xxx.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2024-04-16 tonhuisman: Add Send values on change option, so Interval can be set to 0, and the data will be sent when changed * 2024-04-10 tonhuisman: Initial version. Support for Digipot MCP42xxx (dual channel) and MCP41xxx (single channel). @@ -40,6 +41,7 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.FormulaOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } @@ -123,7 +125,8 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN, spi_bus)); P162_data_struct *P162_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P162_data) { diff --git a/src/_P167_Vindstyrka.ino b/src/_P167_Vindstyrka.ino index 8f65203ee..19d362ce6 100644 --- a/src/_P167_Vindstyrka.ino +++ b/src/_P167_Vindstyrka.ino @@ -190,7 +190,7 @@ boolean Plugin_167(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optCount, options_model, options_model_value); selector.reloadonchange = true; selector.addFormSelector(F("Model Type"), P167_MODEL_LABEL, P167_MODEL); - addFormNote(F("Changing the Model Type will reload the page.")); + // addFormNote(F("Changing the Model Type will reload the page.")); if (P167_MODEL == P167_MODEL_VINDSTYRKA) { addFormPinSelect(PinSelectPurpose::Generic_input, F("MonPin SCL"), F("taskdevicepin3"), P167_MON_SCL_PIN); diff --git a/src/_P172_BMP3xx_SPI.ino b/src/_P172_BMP3xx_SPI.ino index 7864d7255..e179ad307 100644 --- a/src/_P172_BMP3xx_SPI.ino +++ b/src/_P172_BMP3xx_SPI.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * Use all relevant code from P154 (I2C variant of the same sensor) * 2024-06-30 tonhuisman: Start SPI plugin, based on P154, re-using most code (dependency checked in define_plugin_sets.h) @@ -36,6 +37,7 @@ boolean Plugin_172(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } diff --git a/src/_P180_I2C_generic.ino b/src/_P180_I2C_generic.ino index af3e495fc..15bf71e8c 100644 --- a/src/_P180_I2C_generic.ino +++ b/src/_P180_I2C_generic.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2026-02-14 tonhuisman: Set default Cache-name when empty, for improved plugin runtime performance. * 2025-12-06 tonhuisman: Enable CustomVTypeVar and MQTTStateClass device options, as any type of sensor can be connected * 2025-06-04 tonhuisman: Add [#log] to Get Config to fetch the current Parsing and execution log setting * 2025-06-03 tonhuisman: Restore PLUGIN_GET_CONFIG_VALUE support, to allow I2C operations to be executed and return a value without @@ -251,7 +252,12 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) sensorTypeHelper_webformLoad(event, P180_VALUE_OFFSET + i, singleOptions.size(), &singleOptions[0], false, i + 1); // Name - addFormTextBox(strformat(F("Cache-Name %d (optional)"), i + 1), + const String cacheName = concat(F("c"), i + 1); + + if (strings[P180_BUFFER_START_CACHE + i].isEmpty()) { + strings[P180_BUFFER_START_CACHE + i] = cacheName; + } + addFormTextBox(strformat(F("Cache-Name %d"), i + 1), getPluginCustomArgName(20 + i), strings[P180_BUFFER_START_CACHE + i], P180_MAX_NAME_LENGTH); @@ -272,8 +278,6 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) { const String addr = webArg(F("i2c_addr")); P180_I2C_ADDRESS = (uint8_t)strtol(addr.c_str(), 0, 16); - P180_ENABLE_PIN = getFormItemInt(F("taskdevicepin1")); - P180_RST_PIN = getFormItemInt(F("taskdevicepin2")); P180_LOG_DEBUG = isFormItemChecked(F("plog")) ? 1 : 0; pconfig_webformSave(event, P180_SENSOR_TYPE_INDEX); @@ -285,7 +289,11 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) String strings[P180_CUSTOM_BUFFER_SIZE]; for (uint8_t varNr = 0; varNr < VARS_PER_TASK; varNr++) { - strings[P180_BUFFER_START_CACHE + varNr] = webArg(getPluginCustomArgName(20 + varNr)); // Name + strings[P180_BUFFER_START_CACHE + varNr] = webArg(getPluginCustomArgName(20 + varNr)); // Name + + if (strings[P180_BUFFER_START_CACHE + varNr].isEmpty()) { + strings[P180_BUFFER_START_CACHE + varNr] = concat(F("c"), varNr + 1); // Default cache-name + } strings[P180_BUFFER_START_COMMANDS + varNr] = webArg(getPluginCustomArgName(30 + varNr)); // Commands } strings[P180_BUFFER_ENTRY_INIT] = webArg(getPluginCustomArgName(40)); // INIT Commands diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index 3d99d2984..a3f7dac28 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -101,13 +101,13 @@ bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) { log += F(" ExtraArg"); } log += i; - log += '='; + log += ':'; log += parameter; } ++i; } } - log += F(" lineLength="); + log += F(" lineLength:"); log += Line.length(); addLogMove(LOG_LEVEL_ERROR, log); } @@ -326,7 +326,9 @@ bool InternalCommands::executeInternalCommand() #if FEATURE_WIFI case ESPEasy_cmd_e::hiddenssid: COMMAND_CASE_R(Command_Wifi_HiddenSSID, 1); // wifi.h #endif +#if FEATURE_I2C case ESPEasy_cmd_e::i2cscanner: COMMAND_CASE_R(Command_i2c_Scanner, -1); // i2c.h +#endif case ESPEasy_cmd_e::inc: COMMAND_CASE_A(Command_Rules_Inc, -1); // Rules.h case ESPEasy_cmd_e::ip: COMMAND_CASE_R(Command_IP, 1); // Network Command #if FEATURE_USE_IPV6 @@ -412,18 +414,6 @@ bool InternalCommands::executeInternalCommand() #endif // if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING case ESPEasy_cmd_e::provision: COMMAND_CASE_A(Command_Provisioning_Dispatcher, -1); // Provisioning.h -# ifdef PLUGIN_BUILD_MAX_ESP32 - - // FIXME DEPRECATED: Fallback for temporary backward compatibility - case ESPEasy_cmd_e::provisionconfig: COMMAND_CASE_A(Command_Provisioning_ConfigFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionsecurity: COMMAND_CASE_A(Command_Provisioning_SecurityFallback, 0); // Provisioning.h -# if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionnotification: COMMAND_CASE_A(Command_Provisioning_NotificationFallback, 0); // Provisioning.h -# endif // if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionprovision: COMMAND_CASE_A(Command_Provisioning_ProvisionFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionrules: COMMAND_CASE_A(Command_Provisioning_RulesFallback, 1); // Provisioning.h - case ESPEasy_cmd_e::provisionfirmware: COMMAND_CASE_A(Command_Provisioning_FirmwareFallback, 1); // Provisioning.h -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 #endif // if FEATURE_CUSTOM_PROVISIONING case ESPEasy_cmd_e::pulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h #if FEATURE_MQTT diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index ef04f453a..77849e31c 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -89,7 +89,9 @@ const char Internal_commands_fghij[] PROGMEM = #if FEATURE_WIFI "hiddenssid|" #endif +#if FEATURE_I2C "i2cscanner|" +#endif "inc|" "ip|" #if FEATURE_USE_IPV6 @@ -193,16 +195,6 @@ const char Internal_commands_p[] PROGMEM = #endif // #if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING "provision|" - # ifdef PLUGIN_BUILD_MAX_ESP32 // FIXME DEPRECATED: Fallback for temporary backward compatibility - "provisionconfig|" - "provisionsecurity|" - # if FEATURE_NOTIFIER - "provisionnotification|" - # endif // #if FEATURE_NOTIFIER - "provisionprovision|" - "provisionrules|" - "provisionfirmware|" - # endif // #ifdef PLUGIN_BUILD_MAX_ESP32 #endif // #if FEATURE_CUSTOM_PROVISIONING "pulse|" #if FEATURE_MQTT diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 8a6d12a8f..4815d2c59 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -65,7 +65,9 @@ enum class ESPEasy_cmd_e : uint8_t { hiddenssid, #endif +#if FEATURE_I2C i2cscanner, +#endif inc, ip, #if FEATURE_USE_IPV6 @@ -154,16 +156,6 @@ enum class ESPEasy_cmd_e : uint8_t { #endif // #if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING provision, -# ifdef PLUGIN_BUILD_MAX_ESP32 // FIXME DEPRECATED: Fallback for temporary backward compatibility - provisionconfig, - provisionsecurity, -# if FEATURE_NOTIFIER - provisionnotification, -# endif // #if FEATURE_NOTIFIER - provisionprovision, - provisionrules, - provisionfirmware, -# endif // #ifdef PLUGIN_BUILD_MAX_ESP32 #endif // #if FEATURE_CUSTOM_PROVISIONING pulse, #if FEATURE_MQTT diff --git a/src/src/Commands/Provisioning.cpp b/src/src/Commands/Provisioning.cpp index 1feaec561..8d246d39b 100644 --- a/src/src/Commands/Provisioning.cpp +++ b/src/src/Commands/Provisioning.cpp @@ -33,6 +33,11 @@ String Command_Provisioning_Dispatcher(struct EventStruct *event, } else if (equals(cmd, F("security"))) { return Command_Provisioning_Security(); +#if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE + } else + if (equals(cmd, F("devsecurity"))) { + return Command_Provisioning_DeviceSecurity(); +#endif // if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE } return return_command_failed_flashstr(); } @@ -86,62 +91,4 @@ String Command_Provisioning_Firmware(struct EventStruct *event, const char *Line return error; } -# ifdef PLUGIN_BUILD_MAX_ESP32 -void Command_Provisioning_DeprecatedMessage(const String& param) { - addLog(LOG_LEVEL_ERROR, strformat(F("WARNING: 'Provision%s' is deprecated, change to 'Provision,%s'"), param.c_str(), param.c_str())); -} - -String Command_Provisioning_ConfigFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Config")); - return Command_Provisioning_Config(); -} - -String Command_Provisioning_SecurityFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Security")); - return Command_Provisioning_Security(); -} - -# if FEATURE_NOTIFIER -String Command_Provisioning_NotificationFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Notification")); - return Command_Provisioning_Notification(); -} - -# endif // if FEATURE_NOTIFIER - - -String Command_Provisioning_ProvisionFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Provision")); - return Command_Provisioning_Provision(); -} - -String Command_Provisioning_RulesFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Rules,")); - - if ((event->Par1 <= 0) || (event->Par1 > 4)) { - return F("ProvisionRules: rules index out of range"); - } - return downloadFileType(FileType::RULES_TXT, event->Par1 - 1); -} - -String Command_Provisioning_FirmwareFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Firmware,")); - - // FIXME TD-er: Must only allow to use set prefix in the provisioning settings - const String url = parseStringToEndKeepCase(Line, 2); - String error; - - downloadFirmware(url, error); - return error; -} - - -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 - #endif // if FEATURE_CUSTOM_PROVISIONING diff --git a/src/src/Commands/Provisioning.h b/src/src/Commands/Provisioning.h index c7ebc3fd9..ef5c66088 100644 --- a/src/src/Commands/Provisioning.h +++ b/src/src/Commands/Provisioning.h @@ -25,25 +25,6 @@ String Command_Provisioning_Rules(struct EventStruct *event); String Command_Provisioning_Firmware(struct EventStruct *event, const char *Line); -// FIXME DEPRECATED! -# ifdef PLUGIN_BUILD_MAX_ESP32 -String Command_Provisioning_ConfigFallback(struct EventStruct *event, - const char *Line); -String Command_Provisioning_SecurityFallback(struct EventStruct *event, - const char *Line); -# if FEATURE_NOTIFIER -String Command_Provisioning_NotificationFallback(struct EventStruct *event, - const char *Line); -# endif // if FEATURE_NOTIFIER -String Command_Provisioning_ProvisionFallback(struct EventStruct *event, - const char *Line); -String Command_Provisioning_RulesFallback(struct EventStruct *event, - const char *Line); - -String Command_Provisioning_FirmwareFallback(struct EventStruct *event, - const char *Line); -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 - #endif // if FEATURE_CUSTOM_PROVISIONING #endif // ifndef COMMANDS_PROVISIONING_H diff --git a/src/src/Commands/SDCARD.cpp b/src/src/Commands/SDCARD.cpp index 1a95297af..dc37ae81b 100644 --- a/src/src/Commands/SDCARD.cpp +++ b/src/src/Commands/SDCARD.cpp @@ -6,11 +6,12 @@ #if FEATURE_SD -#include "../ESPEasyCore/Serial.h" -#include "../Globals/Settings.h" -#include "../Helpers/StringConverter.h" +# include "../ESPEasyCore/Serial.h" +# include "../Globals/Settings.h" +# include "../Helpers/Hardware_SPI.h" +# include "../Helpers/StringConverter.h" -#include +# include void printDirectory(fs::File dir, int numTabs) @@ -40,22 +41,26 @@ void printDirectory(fs::File dir, int numTabs) } } - -const __FlashStringHelper * Command_SD_LS(struct EventStruct *event, const char* Line) +const __FlashStringHelper* Command_SD_LS(struct EventStruct *event, const char*Line) { - fs::File root = SD.open("/"); - root.rewindDirectory(); - printDirectory(root, 0); - root.close(); - return return_see_serial(event); + if (initSDcard()) { + fs::File root = SD.open("/"); + root.rewindDirectory(); + printDirectory(root, 0); + root.close(); + return return_see_serial(event); + } + return return_command_failed_flashstr(); } -String Command_SD_Remove(struct EventStruct *event, const char* Line) +String Command_SD_Remove(struct EventStruct *event, const char*Line) { // FIXME TD-er: This one is not using parseString* function String fname = Line; + fname = fname.substring(9); - SD.remove((char*)fname.c_str()); + SD.remove((char *)fname.c_str()); return return_result(event, concat(F("Removing:"), fname)); } -#endif + +#endif // if FEATURE_SD diff --git a/src/src/Commands/i2c.cpp b/src/src/Commands/i2c.cpp index d0e69777b..36a6d9618 100644 --- a/src/src/Commands/i2c.cpp +++ b/src/src/Commands/i2c.cpp @@ -11,6 +11,7 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C void i2c_scanI2Cbus(bool dbg, int8_t channel, uint8_t i2cBus) { uint8_t error, address; @@ -70,3 +71,4 @@ const __FlashStringHelper* Command_i2c_Scanner(struct EventStruct *event, const I2CSelectHighClockSpeed(0); // By default the bus is in standard speed return return_see_serial(event); } +#endif diff --git a/src/src/Commands/i2c.h b/src/src/Commands/i2c.h index 777308ff0..2131873db 100644 --- a/src/src/Commands/i2c.h +++ b/src/src/Commands/i2c.h @@ -2,12 +2,12 @@ #define COMMAND_I2C_H #include "../../ESPEasy_common.h" - +#if FEATURE_I2C void i2c_scanI2Cbus(bool dbg, int8_t channel, uint8_t i2cBus); const __FlashStringHelper* Command_i2c_Scanner(struct EventStruct *event, const char *Line); - +#endif #endif // COMMAND_I2C_H diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 14983ba0a..82b1d4ed3 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -55,8 +55,10 @@ To create/register a plugin, you have to : #define WEBSERVER_INCLUDE_JS #endif #ifndef WEBSERVER_LOG + #ifndef PLUGIN_BUILD_MINIMAL_OTA #define WEBSERVER_LOG #endif + #endif #ifndef WEBSERVER_JSON #define WEBSERVER_JSON #endif @@ -103,6 +105,9 @@ To create/register a plugin, you have to : #ifndef WEBSERVER_HARDWARE #define WEBSERVER_HARDWARE #endif + #ifndef WEBSERVER_INTERFACES + #define WEBSERVER_INTERFACES + #endif #ifndef WEBSERVER_PINSTATES #define WEBSERVER_PINSTATES #endif @@ -2179,6 +2184,10 @@ To create/register a plugin, you have to : #define PLUGIN_DESCR "Climate A" #endif + #ifndef BUILD_NO_DEBUG + #define BUILD_NO_DEBUG + #endif + // Features and plugins cherry picked from stable set #ifndef FEATURE_SERVO #define FEATURE_SERVO 1 @@ -3541,15 +3550,6 @@ To create/register a plugin, you have to : #endif #endif -#if FEATURE_MQTT_TLS || FEATURE_EMAIL_TLS || FEATURE_HTTP_TLS - #if defined(FEATURE_TLS) && !FEATURE_TLS - #undef FEATURE_TLS - #endif - #ifndef FEATURE_TLS - #define FEATURE_TLS 1 - #endif -#endif - #if !defined(FEATURE_MQTT_DISCOVER) && FEATURE_MQTT #if defined(LIMIT_BUILD_SIZE) || defined(ESP8266) // Must enable this explicitly for ESP8266 Custom build @@ -3753,6 +3753,26 @@ To create/register a plugin, you have to : #define FEATURE_MODBUS 0 #endif +#ifndef FEATURE_MODBUS_INTERFACES_TAB // TODO Temporary, until P183 finished +#ifdef USES_P183 +#define FEATURE_MODBUS_INTERFACES_TAB 1 +#else +#define FEATURE_MODBUS_INTERFACES_TAB 0 +#endif +#endif + +#ifndef FEATURE_CAN +#define FEATURE_CAN 0 +#endif // ifndef FEATURE_CAN + +#ifndef FEATURE_WRMBUS // mBus (wired) +#define FEATURE_WRMBUS 0 +#endif // ifndef FEATURE_WRMBUS + +#ifndef FEATURE_WIMBUS // w-mBus (wireless) +#define FEATURE_WIMBUS 0 +#endif // ifndef FEATURE_WIMBUS + #ifndef FEATURE_MQTT #define FEATURE_MQTT 0 #endif @@ -3934,6 +3954,16 @@ To create/register a plugin, you have to : #endif #endif // ifndef FEATURE_HTTP_TLS +#if FEATURE_MQTT_TLS || FEATURE_EMAIL_TLS || FEATURE_HTTP_TLS + #if defined(FEATURE_TLS) && !FEATURE_TLS + #undef FEATURE_TLS + #endif + #ifndef FEATURE_TLS + #define FEATURE_TLS 1 + #endif +#endif + + #ifndef FEATURE_AUTO_DARK_MODE #ifdef LIMIT_BUILD_SIZE #define FEATURE_AUTO_DARK_MODE 0 @@ -4463,4 +4493,55 @@ To create/register a plugin, you have to : #endif #endif +#ifndef FEATURE_I2C + #ifdef ESP32 + #define FEATURE_I2C 1 + #else + // TODO TD-er: Add check for plugins requiring I2C + // Checks: + // FEATURE_I2CMULTIPLEXER + // FEATURE_PLUGIN_PRIORITY + // FEATURE_I2C_MULTIPLE + // FEATURE_CLEAR_I2C_STUCK + // FEATURE_I2C_GET_ADDRESS + // FEATURE_EXT_RTC + // I2C Watchdog???? Settings.WDI2CAddress + #ifdef PLUGIN_BUILD_MINIMAL_OTA + #ifdef USES_P180 + #define FEATURE_I2C 1 + #else + #define FEATURE_I2C 0 + #endif + #else + #define FEATURE_I2C 1 + #endif + #endif +#endif + +#if !FEATURE_I2C +# ifdef WEBSERVER_I2C_SCANNER +# undef WEBSERVER_I2C_SCANNER +# endif +#endif + +#ifndef FEATURE_SPI + #if FEATURE_SD + #define FEATURE_SPI 1 + // TODO TD-er: Add check for plugins requiring SPI + #else + #if defined(USES_NW004) || defined(USES_P039) || defined(USES_P046) || defined(USES_P095) || defined(USES_P096) || defined(USES_P099) || defined(USES_P104) || defined(USES_P111) || defined(USES_P116) || defined(USES_P118) || defined(USES_P125) || defined(USES_P141) || defined(USES_P162) || defined(USES_P172) + #define FEATURE_SPI 1 + #else + #define FEATURE_SPI 0 + #endif +#endif +#endif + + +#if !FEATURE_SPI && !FEATURE_I2C && !FEATURE_MODBUS && !FEATURE_CAN && !FEATURE_WRMBUS && !FEATURE_WIMBUS +#ifdef WEBSERVER_INTERFACES +#undef WEBSERVER_INTERFACES +#endif +#endif // if !FEATURE_SPI && !FEATURE_I2C && !FEATURE_MODBUS && !FEATURE_CAN && !FEATURE_WRMBUS && !FEATURE_WIMBUS + #endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index c5918ff64..bc36b5c31 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -77,7 +77,7 @@ struct ControllerSettingsStruct CONTROLLER_HOSTNAME, CONTROLLER_IP, CONTROLLER_PORT, -#if FEATURE_MQTT_TLS +#if FEATURE_MQTT_TLS || FEATURE_HTTP_TLS CONTROLLER_MQTT_TLS_TYPE, CONTROLLER_MQTT_TLS_STORE_FINGERPRINT, CONTROLLER_MQTT_TLS_STORE_CERT, diff --git a/src/src/DataStructs/DeviceStruct.cpp b/src/src/DataStructs/DeviceStruct.cpp index 23d8a8ec1..43069f3e8 100644 --- a/src/src/DataStructs/DeviceStruct.cpp +++ b/src/src/DataStructs/DeviceStruct.cpp @@ -57,17 +57,23 @@ void DeviceStruct::clear() { bool DeviceStruct::connectedToGPIOpins() const { switch(Type) { case DEVICE_TYPE_SINGLE: // Single GPIO +#if FEATURE_SPI case DEVICE_TYPE_SPI: +#endif case DEVICE_TYPE_CUSTOM1: case DEVICE_TYPE_DUAL: // Dual GPIOs case DEVICE_TYPE_SERIAL: +#if FEATURE_SPI case DEVICE_TYPE_SPI2: +#endif case DEVICE_TYPE_CUSTOM2: case DEVICE_TYPE_TRIPLE: // Triple GPIOs case DEVICE_TYPE_SERIAL_PLUS1: +#if FEATURE_SPI case DEVICE_TYPE_SPI3: +#endif case DEVICE_TYPE_CUSTOM3: return true; default: @@ -81,12 +87,16 @@ bool DeviceStruct::usesTaskDevicePin(int pin) const { if (pin == 2) return connectedToGPIOpins() && !(Type == DEVICE_TYPE_SINGLE || +#if FEATURE_SPI Type == DEVICE_TYPE_SPI || +#endif Type == DEVICE_TYPE_CUSTOM1); if (pin == 3) return Type == DEVICE_TYPE_TRIPLE || Type == DEVICE_TYPE_SERIAL_PLUS1 || +#if FEATURE_SPI Type == DEVICE_TYPE_SPI3 || +#endif Type == DEVICE_TYPE_CUSTOM3; return false; } @@ -95,12 +105,13 @@ bool DeviceStruct::isSerial() const { return (Type == DEVICE_TYPE_SERIAL) || (Type == DEVICE_TYPE_SERIAL_PLUS1); } - +#if FEATURE_SPI bool DeviceStruct::isSPI() const { return (Type == DEVICE_TYPE_SPI) || (Type == DEVICE_TYPE_SPI2) || (Type == DEVICE_TYPE_SPI3); } +#endif bool DeviceStruct::isCustom() const { return (Type == DEVICE_TYPE_CUSTOM0) || diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index a1aff7ab0..7433096f5 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -17,12 +17,16 @@ #define DEVICE_TYPE_DUAL 2 // connected through 2 datapins #define DEVICE_TYPE_TRIPLE 3 // connected through 3 datapins #define DEVICE_TYPE_ANALOG 10 // AIN/tout pin +#if FEATURE_I2C #define DEVICE_TYPE_I2C 20 // connected through I2C +#endif #define DEVICE_TYPE_SERIAL 21 // connected through UART/Serial #define DEVICE_TYPE_SERIAL_PLUS1 22 // connected through UART/Serial + 1 extra signal pin +#if FEATURE_SPI #define DEVICE_TYPE_SPI 23 // connected through SPI #define DEVICE_TYPE_SPI2 24 // connected through SPI, 2 GPIOs #define DEVICE_TYPE_SPI3 25 // connected through SPI, 3 GPIOs +#endif #define DEVICE_TYPE_CUSTOM0 30 // Custom labels, Not using TaskDevicePin1 ... TaskDevicePin3 #define DEVICE_TYPE_CUSTOM1 31 // Custom labels, 1 GPIO #define DEVICE_TYPE_CUSTOM2 32 // Custom labels, 2 GPIOs @@ -47,6 +51,11 @@ // #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE +// Stored in Settings.I2C_SPI_bus_Flags !!! +#define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task +// Stored in Settings.I2C_SPI_bus_Flags for Task 1 settings +// #define SPI_FLAGS_xxx_unused 2 // 2 bit, available +#define SPI_FLAGS_SDCARD_BUS_NUMBER 4 // 2 bit, stores the configured bus for the SDCard /*********************************************************************************************\ * DeviceStruct @@ -68,8 +77,9 @@ struct DeviceStruct } bool isSerial() const; - +#if FEATURE_SPI bool isSPI() const; +#endif bool isCustom() const; @@ -118,7 +128,7 @@ struct DeviceStruct uint32_t MqttStateClass : 1; // MQTT StateClass setting in DevicesPage uint32_t HideDerivedValues : 1; // Hide the options for derived values uint32_t NoDeviceSettings : 1; // Indicating the device is not referencing actual hardware, like the Dummy task - uint32_t Dummy25 : 1; // Dummy added to force alignment, can be re-used + uint32_t SpiBusSelect : 1; // Allow selection of the SPI bus, if multiple buses are configured uint32_t Dummy26 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy27 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy28 : 1; // Dummy added to force alignment, can be re-used diff --git a/src/src/DataStructs/ProtocolStruct.h b/src/src/DataStructs/ProtocolStruct.h index dff4857ef..51d19938d 100644 --- a/src/src/DataStructs/ProtocolStruct.h +++ b/src/src/DataStructs/ProtocolStruct.h @@ -38,7 +38,7 @@ struct ProtocolStruct uint32_t needsNetwork : 1; uint32_t allowsExpire : 1; uint32_t allowLocalSystemTime : 1; -#if FEATURE_MQTT_TLS +#if FEATURE_MQTT_TLS || FEATURE_HTTP_TLS uint32_t usesTLS : 1; // May offer TLS related settings and options #else uint32_t dontUseBit16 : 1; diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index dc6ac7838..d16376482 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -260,9 +260,6 @@ class SettingsStruct_tmpl inline bool StartAPfallback_NoCredentials() const { return !VariousBits_2.StartAPfallback_NoCredentials; } inline void StartAPfallback_NoCredentials(bool value) { VariousBits_2.StartAPfallback_NoCredentials = !value; } - inline bool StartAP_on_NW002_init() const { return VariousBits_2.StartAP_on_NW002_init; } - inline void StartAP_on_NW002_init(bool value) { VariousBits_2.StartAP_on_NW002_init = value; } - inline bool DoNotStartAPfallback_ConnectFail() const { return VariousBits_1.DoNotStartAPfallback_ConnectFail; } inline void DoNotStartAPfallback_ConnectFail(bool value) { VariousBits_1.DoNotStartAPfallback_ConnectFail = value; } @@ -366,23 +363,41 @@ public: PinBootState getPinBootState(int8_t gpio_pin) const; void setPinBootState(int8_t gpio_pin, PinBootState state); - bool getSPI_pins(int8_t spi_gpios[3]) const; +#if FEATURE_SPI + bool getSPI_pins(int8_t spi_gpios[3], + uint8_t spi_bus = 0, + bool noCheck = false) const; + bool isSPI_enabled(uint8_t spi_bus) const; + uint8_t getNrConfiguredSPI_buses() const; #ifdef ESP32 - spi_host_device_t getSPI_host() const; + spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const; #endif // Return true when pin is one of the SPI pins and SPI is enabled - bool isSPI_pin(int8_t pin) const; + bool isSPI_pin(int8_t pin, + uint8_t spi_bus = 0xFF) const; // Return true when SPI enabled and opt. user defined pins valid. - bool isSPI_valid() const; + bool isSPI_valid(uint8_t spi_bus) const; + uint8_t getSPIBusForTask(taskIndex_t TaskIndex) const; + void setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus); + + #if FEATURE_SD + uint8_t getSPIBusForSDCard() const; + void setSPIBusForSDCard(uint8_t spi_bus); + #endif // if FEATURE_SD +#endif + +#if FEATURE_I2C // Return true when pin is one of the configured I2C pins. bool isI2C_pin(int8_t pin) const; // Return true if I2C settings are correct bool isI2CEnabled(uint8_t i2cBus) const; + uint8_t getNrConfiguredI2C_buses() const; + uint8_t getI2CInterface(taskIndex_t TaskIndex) const; int8_t getI2CSdaPin(uint8_t i2cBus) const; @@ -402,13 +417,14 @@ public: int8_t getI2CMultiplexerAddr(uint8_t i2cBus) const; int8_t getI2CMultiplexerResetPin(uint8_t i2cBus) const; #endif // if FEATURE_I2CMULTIPLEXER - +#endif +#if FEATURE_ETHERNET // Return true when pin is one of the fixed Ethernet pins and Ethernet is enabled bool isEthernetPin(int8_t pin) const; // Return true when pin is one of the optional Ethernet pins and Ethernet is enabled bool isEthernetPinOptional(int8_t pin) const; - +#endif // Access to TaskDevicePin1 ... TaskDevicePin3 // @param pinnr 1 = TaskDevicePin1, ..., 3 = TaskDevicePin3 int8_t getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const; @@ -444,6 +460,10 @@ public: void setNetworkEnabled(ESPEasy::net::networkIndex_t index, bool enabled); + bool getNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index) const; + + void setNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index, bool enabled); + bool getNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index) const; void setNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index, bool enabled); @@ -464,9 +484,9 @@ public: void setRoutePrio_for_network(ESPEasy::net::networkIndex_t index, uint8_t prio); #endif - uint32_t getNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index) const; + uint32_t getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const; - void setNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index, uint32_t delay_ms); + void setNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index, uint32_t delay_ms); uint32_t PID = 0; int Version = 0; @@ -565,7 +585,11 @@ public: int8_t I2C3_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C3_Multiplexer_Addr = -1; int8_t I2C3_Multiplexer_ResetPin = -1; - uint32_t OLD_TaskDeviceID[N_TASKS - 7] = {0}; //UNUSED: this can be reused + uint8_t InitSPI1 = 0; // 0 = disabled, 1= enabled but for ESP32 there is option 2= SPI2 9 = User defined, see src/src/WebServer/HardwarePage.h enum SPI_Options_e + int8_t SPI1_SCLK_pin = -1; + int8_t SPI1_MISO_pin = -1; + int8_t SPI1_MOSI_pin = -1; + unsigned int OLD_TaskDeviceID[N_TASKS - 8] = {0}; // UNUSED: this can be reused // FIXME TD-er: When used on ESP8266, this conversion union may not work // It might work as it is 32-bit in size. @@ -681,7 +705,7 @@ public: int8_t I2C_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C_Multiplexer_Addr = -1; int8_t I2C_Multiplexer_Channel[N_TASKS]{}; - uint8_t I2C_Flags[N_TASKS] = {0}; + uint8_t I2C_SPI_bus_Flags[N_TASKS] = {0}; uint32_t I2C_clockSpeed_Slow = 100000; int8_t I2C_Multiplexer_ResetPin = -1; @@ -718,7 +742,7 @@ public: uint32_t MQTTConnectInBackground : 1; // Bit 13 // inverted uint32_t StartAPfallback_NoCredentials : 1; // Bit 14 // inverted - uint32_t StartAP_on_NW002_init : 1; // Bit 15 + uint32_t Unused_bit15 : 1; // Bit 15 uint32_t APfallback_minimal_on_time_sec : 8; // Bit 16 - 23 uint32_t APfallback_autostart_max_uptime_m : 8; // Bit 23 - 31 '0' == disabled }; @@ -739,12 +763,12 @@ public: uint8_t NetworkEnabled_bits{}; uint8_t NetworkInterfaceSubnetBlockClientIP_bits{}; // Client IP Block Level. Allow from subnet of this interface uint8_t NetworkEnabled_ipv6_bits{}; // Whether or not to use IPv6 for the given interface (Settings.EnableIPv6() is the global on/off for IPv6) - uint8_t NetworkUnused_3{}; + uint8_t NetworkInterface_isFallback_bits{}; #ifdef ESP32 uint8_t NetworkRoutePrio[NETWORK_MAX] = {0}; #endif // TODO TD-er: For ESP8266 we may likely ever use upto 2 or 3 network interfaces, so maybe re-use the rest later? - uint16_t NetworkInterfaceStartupDelayAtBoot[NETWORK_MAX]{}; + uint16_t NetworkInterfaceStartupDelay[NETWORK_MAX]{}; // Try to extend settings to make the checksum 4-uint8_t aligned. diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 41f017435..c38957641 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -986,16 +986,33 @@ void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState } # endif // ifdef ESP32 } +#if FEATURE_SPI +template +bool SettingsStruct_tmpl::isSPI_enabled(uint8_t spi_bus) const { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); + return SPI_Options_e::None != SPI_selection; +} template -bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { +uint8_t SettingsStruct_tmpl::getNrConfiguredSPI_buses() const +{ + uint8_t res{}; + if (isSPI_valid(0u)) ++res; + #ifdef ESP32 + if (getSPIBusCount() > 1 && isSPI_valid(1u)) ++res; + #endif + return res; +} + +template +bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const { spi_gpios[0] = -1; spi_gpios[1] = -1; spi_gpios[2] = -1; - if (isSPI_valid()) { + if (noCheck || isSPI_valid(spi_bus)) { # ifdef ESP32 - const SPI_Options_e SPI_selection = static_cast(InitSPI); + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: @@ -1014,11 +1031,26 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { break; } #endif - case SPI_Options_e::UserDefined: + case SPI_Options_e::UserDefined_VSPI: +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: +#endif { - spi_gpios[0] = SPI_SCLK_pin; - spi_gpios[1] = SPI_MISO_pin; - spi_gpios[2] = SPI_MOSI_pin; + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + spi_gpios[0] = SPI_SCLK_pin; + spi_gpios[1] = SPI_MISO_pin; + spi_gpios[2] = SPI_MOSI_pin; + } + #ifdef ESP32 + else if (1 == spi_bus) { + spi_gpios[0] = SPI1_SCLK_pin; + spi_gpios[1] = SPI1_MISO_pin; + spi_gpios[2] = SPI1_MOSI_pin; + } + #endif // ifdef ESP32 break; } case SPI_Options_e::None: @@ -1035,12 +1067,13 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { #ifdef ESP32 template -spi_host_device_t SettingsStruct_tmpl::getSPI_host() const +spi_host_device_t SettingsStruct_tmpl::getSPI_host(uint8_t spi_bus) const { - if (isSPI_valid()) { - const SPI_Options_e SPI_selection = static_cast(InitSPI); + if (isSPI_valid(spi_bus)) { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: + case SPI_Options_e::UserDefined_VSPI: { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 return static_cast(FSPI_HOST); @@ -1054,14 +1087,12 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host() const return static_cast(HSPI_HOST); } #endif - case SPI_Options_e::UserDefined: +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: { - #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - return static_cast(FSPI_HOST); - #else - return static_cast(VSPI_HOST); - #endif + return static_cast(HSPI_HOST); } +#endif case SPI_Options_e::None: break; } @@ -1081,33 +1112,86 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host() const template -bool SettingsStruct_tmpl::isSPI_pin(int8_t pin) const { +bool SettingsStruct_tmpl::isSPI_pin(int8_t pin, uint8_t spi_bus) const { if (pin < 0) { return false; } int8_t spi_gpios[3]; - if (getSPI_pins(spi_gpios)) { + if (getSPI_pins(spi_gpios, 0u) && ((0xFF == spi_bus) || (0u == spi_bus))) { for (uint8_t i = 0; i < 3; ++i) { if (spi_gpios[i] == pin) { return true; } } } + #ifdef ESP32 + if ((getSPIBusCount() > 1u) && getSPI_pins(spi_gpios, 1u) && ((0xFF == spi_bus) || (1u == spi_bus))) { + for (uint8_t i = 0; i < 3; ++i) { + if (spi_gpios[i] == pin) { return true; } + } + } + #endif // ifdef ESP32 return false; } template -bool SettingsStruct_tmpl::isSPI_valid() const { - if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } +bool SettingsStruct_tmpl::isSPI_valid(uint8_t spi_bus) const { + int8_t spi0_pins[3]; + bool result = false; + getSPI_pins(spi0_pins, 0u, true); - if (InitSPI == static_cast(SPI_Options_e::UserDefined)) { - return !((SPI_SCLK_pin == -1) || - (SPI_MISO_pin == -1) || - (SPI_MOSI_pin == -1) || - (SPI_SCLK_pin == SPI_MISO_pin) || - (SPI_MISO_pin == SPI_MOSI_pin) || - (SPI_MOSI_pin == SPI_SCLK_pin)); + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } + + result = !((spi0_pins[0] == -1) || + (spi0_pins[1] == -1) || + (spi0_pins[2] == -1) || + (spi0_pins[0] == spi0_pins[1]) || + (spi0_pins[1] == spi0_pins[2]) || + (spi0_pins[2] == spi0_pins[0])); + #ifdef ESP32 + if (result && (getSPIBusCount() > 1)) { // Cross-check pins with other bus + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u, true); + + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } + #endif // ifdef ESP32 } - return true; -} + #ifdef ESP32 + else if ((1 == spi_bus) && (getSPIBusCount() > 1)) { + if (InitSPI1 == static_cast(SPI_Options_e::None)) { return false; } + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u, true); + result = !((spi1_pins[0] == -1) || + (spi1_pins[1] == -1) || + (spi1_pins[2] == -1) || + (spi1_pins[0] == spi1_pins[1]) || + (spi1_pins[1] == spi1_pins[2]) || + (spi1_pins[2] == spi1_pins[0])); + + if (result) { // Cross-check pins + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } + } + #endif // ifdef ESP32 + return result; +} +#endif +#if FEATURE_I2C template bool SettingsStruct_tmpl::isI2C_pin(int8_t pin) const { if (pin < 0) { return false; } @@ -1132,9 +1216,54 @@ bool SettingsStruct_tmpl::isI2CEnabled(uint8_t i2cBus) const { (getI2CClockSpeedSlow(i2cBus) > 0); } +template +uint8_t SettingsStruct_tmpl::getNrConfiguredI2C_buses() const +{ +#ifdef ESP32 + uint8_t res{}; + if (isI2CEnabled(0)) ++res; + if (getI2CBusCount() > 1) { + if (isI2CEnabled(1)) ++res; +#if FEATURE_I2C_INTERFACE_3 + if (isI2CEnabled(2)) ++res; +#endif + } + return res; +#else + return isI2CEnabled(0) ? 1 : 0; +#endif +} +#endif +#if FEATURE_SPI +// stored in I2C_SPI_bus_Flags per Task +template +uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { + return get2BitFromUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus) { + set2BitToUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); +} + +#if FEATURE_SD +// stored in I2C_SPI_bus_Flags for Task 1 (index 0) +template +uint8_t SettingsStruct_tmpl::getSPIBusForSDCard() const { + return get2BitFromUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForSDCard(uint8_t spi_bus) { + set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); +} +#endif // if FEATURE_SD +#endif + +#if FEATURE_I2C template uint8_t SettingsStruct_tmpl::getI2CInterface(taskIndex_t TaskIndex) const { - return get3BitFromUL(I2C_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); + return get3BitFromUL(I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); } template @@ -1275,7 +1404,8 @@ int8_t SettingsStruct_tmpl::getI2CMultiplexerResetPin(uint8_t i2cBus) c return -1; } #endif // if FEATURE_I2CMULTIPLEXER - +#endif +#if FEATURE_ETHERNET template bool SettingsStruct_tmpl::isEthernetPin(int8_t pin) const { #if FEATURE_ETHERNET @@ -1330,6 +1460,7 @@ bool SettingsStruct_tmpl::isEthernetPinOptional(int8_t pin) const { #endif // if FEATURE_ETHERNET return false; } +#endif template int8_t SettingsStruct_tmpl::getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const { @@ -1410,6 +1541,22 @@ void SettingsStruct_tmpl::setNetworkEnabled(ESPEasy::net::networkIndex_ } } +template +bool SettingsStruct_tmpl::getNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index) const +{ + if (validNetworkIndex(index)) return bitRead(NetworkInterface_isFallback_bits, index); + return false; +} + +template +void SettingsStruct_tmpl::setNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index, bool enabled) +{ + if (validNetworkIndex(index)) { + bitWrite(NetworkInterface_isFallback_bits, index, enabled); + } +} + + template bool SettingsStruct_tmpl::getNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index) const { if (validNetworkIndex(index)) return bitRead(NetworkInterfaceSubnetBlockClientIP_bits, index); @@ -1460,19 +1607,19 @@ void SettingsStruct_tmpl::setRoutePrio_for_network(ESPEasy::net::networ #endif template -uint32_t SettingsStruct_tmpl::getNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index) const +uint32_t SettingsStruct_tmpl::getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const { if (validNetworkIndex(index)) { - return static_cast(NetworkInterfaceStartupDelayAtBoot[index]) * 10ul; + return static_cast(NetworkInterfaceStartupDelay[index]) * 10ul; } return 0; } template -void SettingsStruct_tmpl::setNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index, uint32_t delay_ms) +void SettingsStruct_tmpl::setNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index, uint32_t delay_ms) { if (validNetworkIndex(index)) { - NetworkInterfaceStartupDelayAtBoot[index] = delay_ms/10ul; + NetworkInterfaceStartupDelay[index] = delay_ms/10ul; } } diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h index 224a02034..f275b2056 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.h +++ b/src/src/DataTypes/ESPEasy_plugin_functions.h @@ -176,6 +176,7 @@ public: NWPLUGIN_EXIT, NWPLUGIN_WRITE, // Send commands to a network adapter. NWPLUGIN_CREDENTIALS_CHANGED, + NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START, #ifdef ESP32 NWPLUGIN_GET_INTERFACE, NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO, diff --git a/src/src/DataTypes/SPI_options.cpp b/src/src/DataTypes/SPI_options.cpp index 57bac16f2..e227bd83f 100644 --- a/src/src/DataTypes/SPI_options.cpp +++ b/src/src/DataTypes/SPI_options.cpp @@ -3,6 +3,8 @@ #ifdef ESP32 +#include "../Helpers/Hardware_device_info.h" + // ESP32 VSPI: // SCK = 18 // MISO = 19 @@ -35,28 +37,31 @@ # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 -#define VSPI_FSPI_SHORT_STRING "FSPI" +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")" # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 -#define VSPI_FSPI_SHORT_STRING "SPI" -# elif CONFIG_IDF_TARGET_ESP32C5 // ESP32-C5 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C61 // ESP32-C61 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 -#define VSPI_FSPI_SHORT_STRING "FSPI" +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")" +#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32P4 +# if SOC_SPI_PERIPH_NUM > 2 +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(VSPI_HOST) ")" +# else +#define VSPI_FSPI_SHORT_STRING "SPI" // STRINGIFY(VSPI_HOST) +# endif + # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#define VSPI_FSPI_SHORT_STRING "VSPI" +#define VSPI_FSPI_SHORT_STRING "VSPI (" STRINGIFY(VSPI_HOST) ")" # else // if CONFIG_IDF_TARGET_ESP32S2 # error Target CONFIG_IDF_TARGET is not supported # endif // if CONFIG_IDF_TARGET_ESP32S2 +#if SOC_SPI_PERIPH_NUM > 2 +#ifdef ESP32_CLASSIC +#define HSPI_SHORT_STRING "HSPI (" STRINGIFY(HSPI_HOST) ")" +#else +#define HSPI_SHORT_STRING STRINGIFY(HSPI_HOST) +#endif +#endif + const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { switch (option) { @@ -65,38 +70,73 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { case SPI_Options_e::Vspi_Fspi: return F( VSPI_FSPI_SHORT_STRING - ": CLK=GPIO-" STRINGIFY(VSPI_FSPI_SCK) - ", MISO=GPIO-" STRINGIFY(VSPI_FSPI_MISO) - ", MOSI=GPIO-" STRINGIFY(VSPI_FSPI_MOSI) ); + ": CLK=" STRINGIFY(VSPI_FSPI_SCK) + ", MISO=" STRINGIFY(VSPI_FSPI_MISO) + ", MOSI=" STRINGIFY(VSPI_FSPI_MOSI) ); #ifdef ESP32_CLASSIC case SPI_Options_e::Hspi: return F( - "HSPI" - ": CLK=GPIO-" STRINGIFY(HSPI_SCLK) - ", MISO=GPIO-" STRINGIFY(HSPI_MISO) - ", MOSI=GPIO-" STRINGIFY(HSPI_MOSI) ); + HSPI_SHORT_STRING + ": CLK=" STRINGIFY(HSPI_SCLK) + ", MISO=" STRINGIFY(HSPI_MISO) + ", MOSI=" STRINGIFY(HSPI_MOSI) ); #endif - case SPI_Options_e::UserDefined: - return F("User-defined: CLK, MISO, MOSI GPIO-pins"); + case SPI_Options_e::UserDefined_VSPI: + return F("User-defined " VSPI_FSPI_SHORT_STRING); +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: + return F("User-defined " HSPI_SHORT_STRING); +#endif } return F("Unknown"); } -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option) { +const __FlashStringHelper* get_vspi_fspi_str() +{ + return F(VSPI_FSPI_SHORT_STRING); +} + +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus) { +#ifdef ESP32 + String res; + switch (option) { + case SPI_Options_e::None: + return F("Disabled"); + case SPI_Options_e::Vspi_Fspi: + res = F(VSPI_FSPI_SHORT_STRING); + break; +#ifdef ESP32_CLASSIC + case SPI_Options_e::Hspi: + res = F("HSPI"); + break; +#endif + case SPI_Options_e::UserDefined_VSPI: + res = F("User-defined " VSPI_FSPI_SHORT_STRING); + break; +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: + res = F("User-defined " HSPI_SHORT_STRING); + break; +#endif + } + if (!res.isEmpty()) { + if (getSPIBusCount() > 1) { + return concat(res + F(" bus "), spi_bus); + } + return res; + } +#else switch (option) { case SPI_Options_e::None: return F("Disabled"); case SPI_Options_e::Vspi_Fspi: return F(VSPI_FSPI_SHORT_STRING); -#ifdef ESP32_CLASSIC - case SPI_Options_e::Hspi: - return F("HSPI"); -#endif - case SPI_Options_e::UserDefined: + case SPI_Options_e::UserDefined_VSPI: return F("User-defined SPI"); } +#endif return F("Unknown"); } diff --git a/src/src/DataTypes/SPI_options.h b/src/src/DataTypes/SPI_options.h index 3bb385a8f..affbd8a42 100644 --- a/src/src/DataTypes/SPI_options.h +++ b/src/src/DataTypes/SPI_options.h @@ -2,6 +2,7 @@ #define DATATYPES_SPI_OPTIONS_H #include "../../ESPEasy_common.h" +#include "../Helpers/StringConverter.h" // ESP32 classic has default pins for HSPI. @@ -13,7 +14,7 @@ // ESP32-S2/S3 : FSPI // ESP32 classic: -// SPI_HOST = SPI1_HOST // Only usable on ESP32 +// SPI_HOST = SPI1_HOST // Only usable on ESP32, when all functions doing SPI operations are in IRAM // HSPI_HOST = SPI2_HOST // VSPI_HOST = SPI3_HOST // @@ -72,13 +73,17 @@ enum class SPI_Options_e { // For ESP32 classic, this is called VSPI // For later versions it is called FSPI // N.B. the ESP32-C3 does not seem to name these as there is no SPI3_HOST. - UserDefined = 9 // Leave some room for other, possible future, hardware-related options + UserDefined_VSPI = 9 // Leave some room for other, possible future, hardware-related options +#if SOC_SPI_PERIPH_NUM > 2 + ,UserDefined_HSPI = 10 +#endif }; #ifdef ESP32 const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option); -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option); +const __FlashStringHelper* get_vspi_fspi_str(); +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus = 0); #endif // ifdef ESP32 #endif // ifndef DATATYPES_SPI_OPTIONS_H diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index cba1a0498..5e07aeab5 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -34,6 +34,11 @@ #include "../Helpers/PeriodicalActions.h" #include "../Helpers/PortStatus.h" +# if FEATURE_MQTT_TLS + # include + # include "../CustomBuild/Certificate_CA.h" +# endif // if FEATURE_MQTT_TLS + constexpr pluginID_t PLUGIN_ID_MQTT_IMPORT(37); @@ -1192,11 +1197,15 @@ void MQTTStatus(struct EventStruct *event, const String& status) } } -# if FEATURE_MQTT_TLS +#endif + +# if FEATURE_MQTT_TLS || FEATURE_HTTP_TLS bool GetTLSfingerprint(String& fp) { # ifdef ESP32 +# if FEATURE_MQTT_TLS +// FIXME TD-er: We need to have a closer look at this as currently FEATURE_HTTP_TLS really needs FEATURE_MQTT_TLS if (MQTTclient_connected && (mqtt_tls != nullptr)) { const uint8_t *recv_fingerprint = mqtt_tls->getRecvPubKeyFingerprint(); @@ -1223,6 +1232,7 @@ bool GetTLSfingerprint(String& fp) return true; } } +#endif # endif // ifdef ESP32 return false; } @@ -1249,8 +1259,6 @@ bool GetTLS_Certificate(String& cert, bool caRoot) # endif // if FEATURE_MQTT_TLS -#endif // if FEATURE_MQTT - /*********************************************************************************************\ * send specific sensor task data, effectively calling PluginCall(PLUGIN_READ...) \*********************************************************************************************/ diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index 21e4f59d7..046a7e87b 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -77,15 +77,16 @@ bool MQTTpublish(controllerIndex_t controller_idx, taskIndex_t taskIndex, Strin * Send status info back to channel where request came from \*********************************************************************************************/ void MQTTStatus(struct EventStruct *event, const String& status); +#endif //if FEATURE_MQTT -#if FEATURE_MQTT_TLS +# if FEATURE_MQTT_TLS || FEATURE_HTTP_TLS bool GetTLSfingerprint(String& fp); bool GetTLS_Certificate(String& cert, bool caRoot); #endif -#endif //if FEATURE_MQTT + /*********************************************************************************************\ diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 477497d35..eefacec8c 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -98,11 +98,11 @@ void rulesProcessing(const String& event) { #ifndef BUILD_NO_DEBUG const unsigned long timer = millis(); #endif // ifndef BUILD_NO_DEBUG -#ifndef BUILD_NO_DEBUG +// #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("EVENT: "), event)); } -#endif +// #endif if (Settings.OldRulesEngine()) { bool eventHandled = false; @@ -1049,13 +1049,13 @@ void processMatchedRule(String& action, const String& event, substitute_eventvalue(action, event); const bool executeRestricted = equals(parseString(action, 1), F("restrict")); -#ifndef BUILD_NO_DEBUG +// #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String actionlog = executeRestricted ? F("ACT : (restricted) ") : F("ACT : "); actionlog += action; addLogMove(LOG_LEVEL_INFO, actionlog); } -#endif +// #endif if (executeRestricted) { ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_RULES_RESTRICTED, parseStringToEndKeepCase(action, 2)}); } else { diff --git a/src/src/ESPEasyCore/ESPEasy_Console.cpp b/src/src/ESPEasyCore/ESPEasy_Console.cpp index 8b4ff8384..306b18aef 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console.cpp @@ -17,7 +17,7 @@ #include #ifdef ESP32 -#include +# include #endif @@ -241,11 +241,12 @@ void EspEasy_Console_t::begin(uint32_t baudrate) if (_fallbackSerial._serial != nullptr) { _fallbackSerial._serial->begin(baudrate); -#ifdef ESP32 +# ifdef ESP32 + // Need to have this string as C-string, not F-string perimanSetPinBusExtraType(SOC_RX0, "Console"); perimanSetPinBusExtraType(SOC_TX0, "Console"); -#endif +# endif // ifdef ESP32 addLog(LOG_LEVEL_INFO, F("ESPEasy console fallback enabled")); } @@ -329,8 +330,10 @@ bool EspEasy_Console_t::process_serialWriteBuffer() { res = true; } #endif // if USES_ESPEASY_CONSOLE_FALLBACK_PORT +#if FEATURE_TIMING_STATS - STOP_TIMER(CONSOLE_WRITE_SERIAL); + if (res) { STOP_TIMER(CONSOLE_WRITE_SERIAL); } +#endif // if FEATURE_TIMING_STATS return res; } diff --git a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp index 5f36d2aa2..d0a5c54b1 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp @@ -18,10 +18,10 @@ #ifdef ESP32 -#define CONSOLE_INPUT_BUFFER_SIZE 1280 +# define CONSOLE_INPUT_BUFFER_SIZE 1280 #else -#define CONSOLE_INPUT_BUFFER_SIZE 128 -#endif +# define CONSOLE_INPUT_BUFFER_SIZE 128 +#endif // ifdef ESP32 /* @@ -31,19 +31,20 @@ */ EspEasy_Console_Port::EspEasy_Console_Port(LogDestination log_destination) -: _serialWriteBuffer(log_destination) + : _serialWriteBuffer(log_destination) { - InputBuffer_Serial = (char*)calloc(1, CONSOLE_INPUT_BUFFER_SIZE); + InputBuffer_Serial = (char *)calloc(1, CONSOLE_INPUT_BUFFER_SIZE); } EspEasy_Console_Port::~EspEasy_Console_Port() { #if FEATURE_DEFINE_SERIAL_CONSOLE_PORT + if (_serial != nullptr) { delete _serial; _serial = nullptr; } -#endif +#endif // if FEATURE_DEFINE_SERIAL_CONSOLE_PORT free(InputBuffer_Serial); } @@ -111,14 +112,36 @@ void EspEasy_Console_Port::endPort() } } - bool EspEasy_Console_Port::process_serialWriteBuffer() { if (_serial != nullptr) { #ifdef ESP32 - if (!xPortCanYield()) return false; -#endif - return _serialWriteBuffer.process(_serial, _serial->availableForWrite()); + + if (!xPortCanYield()) { return false; } +#endif // ifdef ESP32 + size_t availableForWrite = _serial->availableForWrite(); + + if (availableForWrite == 0) { return false; } + + if (availableForWrite == 1) { + // For only a single byte, just write it directly + return _serialWriteBuffer.process(_serial, availableForWrite); + } + + if (availableForWrite > 64) { + // Set to max. of 64 bytes as this is the optimum 'chunk size' for most + // serial ports, like the CDC ports and I2C to UART. + // Also it is relatively fast to allocate. + availableForWrite = 64; + } + + PrintToString str; + str.reserve(availableForWrite); + + if (_serialWriteBuffer.process(&str, availableForWrite)) { + _serial->write(str.get().c_str(), str.length()); + return true; + } } return false; } @@ -147,7 +170,7 @@ bool EspEasy_Console_Port::process_consoleInput(uint8_t SerialInByte) Logging.consolePrintln(concat('>', cmd)); #endif - ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd)}, true); + ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd) }, true); SerialInByteCounter = 0; InputBuffer_Serial[0] = 0; // serial data processed, clear buffer return true; diff --git a/src/src/ESPEasyCore/ESPEasy_loop.cpp b/src/src/ESPEasyCore/ESPEasy_loop.cpp index 81a88fad9..857ea87b1 100644 --- a/src/src/ESPEasyCore/ESPEasy_loop.cpp +++ b/src/src/ESPEasyCore/ESPEasy_loop.cpp @@ -108,6 +108,7 @@ void ESPEasy_loop() setWebserverRunning(ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN)); // ESPEasy::net::processNetworkEvents(); +#if FEATURE_I2C #if FEATURE_CLEAR_I2C_STUCK if (Settings.EnableClearHangingI2Cbus()) { @@ -143,6 +144,7 @@ void ESPEasy_loop() } } #endif +#endif // Work around for nodes that do not have WiFi connection for a long time and may reboot after N unsuccessful connect attempts diff --git a/src/src/Globals/I2Cdev.cpp b/src/src/Globals/I2Cdev.cpp index f4062267a..58ee92526 100644 --- a/src/src/Globals/I2Cdev.cpp +++ b/src/src/Globals/I2Cdev.cpp @@ -1,4 +1,6 @@ #include "../Globals/I2Cdev.h" +#if FEATURE_I2C I2Cdev i2cdev; +#endif \ No newline at end of file diff --git a/src/src/Globals/I2Cdev.h b/src/src/Globals/I2Cdev.h index 3b5121ecc..8266a2641 100644 --- a/src/src/Globals/I2Cdev.h +++ b/src/src/Globals/I2Cdev.h @@ -1,11 +1,14 @@ #ifndef GLOBALS_I2CDEV_H #define GLOBALS_I2CDEV_H +#include "../../ESPEasy_common.h" +#if FEATURE_I2C #include extern I2Cdev i2cdev; +#endif #endif // ifndef GLOBALS_I2CDEV_H diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index da293c6d1..a786958c9 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -16,6 +16,7 @@ # if FEATURE_MQTT_TLS # include + # include "../CustomBuild/Certificate_CA.h" # endif // if FEATURE_MQTT_TLS # if FEATURE_MQTT_CONNECT_BACKGROUND diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index 0c8a1d835..dc6e7e542 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -205,7 +205,7 @@ uint8_t getTaskI2CAddress(taskIndex_t taskIndex) { // Functions to assist changing I2C multiplexer port or clock speed // when addressing a task // ******************************************************************************** - +#if FEATURE_I2C bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { if (!validTaskIndex(taskIndex) || !validDeviceIndex(DeviceIndex)) { return false; @@ -229,7 +229,7 @@ bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) const uint8_t i2cBus = 0; #endif // if FEATURE_I2C_MULTIPLE - if (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { I2CSelectLowClockSpeed(i2cBus); // Set to slow, also switch the bus } else { I2CSelectHighClockSpeed(i2cBus); // Set to normal, also switch the bus @@ -264,6 +264,7 @@ void post_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { I2CSelectHighClockSpeed(i2cBus); // Reset, stay on current bus } +#endif // Add an event to the event queue. // event value 1 = taskIndex (first task = 1) @@ -362,10 +363,11 @@ bool PluginCallForTask(taskIndex_t taskIndex, uint8_t Function, EventStruct *Tem if (event != nullptr) { TempEvent->OriginTaskIndex = event->TaskIndex; } - +#if FEATURE_I2C if (!prepare_I2C_by_taskIndex(taskIndex, DeviceIndex)) { return false; } +#endif #ifndef BUILD_NO_RAM_TRACKER if (Settings.EnableRAMTracking()) { @@ -456,8 +458,9 @@ bool PluginCallForTask(taskIndex_t taskIndex, uint8_t Function, EventStruct *Tem #if FEATURE_I2C_DEVICE_CHECK } #endif // if FEATURE_I2C_DEVICE_CHECK - +#if FEATURE_I2C post_I2C_by_taskIndex(taskIndex, DeviceIndex); +#endif delay(0); // SMY: call delay(0) unconditionally } else { #if FEATURE_PLUGIN_STATS @@ -833,10 +836,11 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) #ifndef BUILD_NO_RAM_TRACKER checkRAM_PluginCall_task(event->TaskIndex, Function); #endif // ifndef BUILD_NO_RAM_TRACKER - +#if FEATURE_I2C if (!prepare_I2C_by_taskIndex(event->TaskIndex, DeviceIndex)) { return false; } +#endif bool retval = false; const bool performPluginCall = (Function != PLUGIN_READ && Function != PLUGIN_INIT) || @@ -976,7 +980,9 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) #if FEATURE_I2C_DEVICE_CHECK } #endif // if FEATURE_I2C_DEVICE_CHECK +#if FEATURE_I2C post_I2C_by_taskIndex(event->TaskIndex, DeviceIndex); +#endif delay(0); // SMY: call delay(0) unconditionally return retval; diff --git a/src/src/Globals/Plugins.h b/src/src/Globals/Plugins.h index 89015f245..89ad28334 100644 --- a/src/src/Globals/Plugins.h +++ b/src/src/Globals/Plugins.h @@ -97,12 +97,12 @@ uint8_t getTaskI2CAddress(taskIndex_t taskIndex); String getPluginNameFromPluginID(pluginID_t pluginID); - +#if FEATURE_I2C // Prepare I2C bus for next call to task // Return false if task is I2C, but I2C bus is not ready bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); void post_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); - +#endif void loadDefaultTaskValueNames_ifEmpty(taskIndex_t TaskIndex); /*********************************************************************************************\ diff --git a/src/src/Globals/SPIe.cpp b/src/src/Globals/SPIe.cpp new file mode 100644 index 000000000..d17238b37 --- /dev/null +++ b/src/src/Globals/SPIe.cpp @@ -0,0 +1,5 @@ +#include "../Globals/SPIe.h" + +#ifdef ESP32 +SPIClass SPIe(HSPI); +#endif // ifdef ESP32 diff --git a/src/src/Globals/SPIe.h b/src/src/Globals/SPIe.h new file mode 100644 index 000000000..567768d74 --- /dev/null +++ b/src/src/Globals/SPIe.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef ESP32 +# include + +extern SPIClass SPIe; +#endif // ifdef ESP32 diff --git a/src/src/Helpers/Dallas1WireHelper.cpp b/src/src/Helpers/Dallas1WireHelper.cpp index 27ba89da6..c3697952e 100644 --- a/src/src/Helpers/Dallas1WireHelper.cpp +++ b/src/src/Helpers/Dallas1WireHelper.cpp @@ -77,29 +77,9 @@ void DALLAS_IRAM_ATTR Dallas_pinWrite(uint32_t gpio_pin_rx, uint32_t gpio_pin_tx } } -int32_t DALLAS_IRAM_ATTR Dallas_measureWaitForPinState(uint32_t gpio_pin_rx, uint32_t start_usec, int32_t timeout_usec, bool newState) -{ - int32_t passed{}; - - do { - passed = usecPassedSince_fast(start_usec); - } while (passed < timeout_usec && - (!DIRECT_pinRead_ISR(gpio_pin_rx) == newState)); // Using '!' to do a quick cast to bool - - if ((passed > timeout_usec) || - (!DIRECT_pinRead_ISR(gpio_pin_rx) == newState)) { - return -1; - } - - // N.B. we allow the situation where passed == timeout_usec - // and pin might have reached the state we're waiting for. - // This way we don't need to average for time before reading pin state and after. - return passed; -} - inline bool Dallas_waitForPinState(int8_t gpio_pin_rx, uint32_t start_usec, int32_t timeout_usec, uint32_t newState) { - return Dallas_measureWaitForPinState(gpio_pin_rx, start_usec, timeout_usec, newState) >= 0; + return DIRECT_measureWaitForPinState_ISR(gpio_pin_rx, start_usec, timeout_usec, newState) >= 0; } # define Dallas_pinLow Dallas_pinWrite(gpio_pin_rx, gpio_pin_tx, false) @@ -108,8 +88,8 @@ inline bool Dallas_waitForPinState(int8_t gpio_pin_rx, uint32_t start_usec, int3 // # define Dallas_waitForPinLow(P, S, T) Dallas_waitForPinState(P, S, T, 0) # define Dallas_waitForPinHigh(P, S, T) Dallas_waitForPinState(P, S, T, 1) -# define Dallas_measureWaitForPinLow(P, S, T) Dallas_measureWaitForPinState(P, S, T, 0) -# define Dallas_measureWaitForPinHigh(P, S, T) Dallas_measureWaitForPinState(P, S, T, 1) +# define Dallas_measureWaitForPinLow(P, S, T) DIRECT_measureWaitForPinState_ISR(P, S, T, 0) +# define Dallas_measureWaitForPinHigh(P, S, T) DIRECT_measureWaitForPinState_ISR(P, S, T, 1) int Dallas_measure_rise_time(int8_t gpio_pin_rx, int8_t gpio_pin_tx) { diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index c75b15727..609474adc 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -466,7 +466,7 @@ bool BuildFixes() } if (Settings.Build < 20115) { - if (Settings.InitSPI != static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI pins set to None + if (Settings.InitSPI != static_cast(SPI_Options_e::UserDefined_VSPI)) { // User-defined SPI pins set to None Settings.SPI_SCLK_pin = -1; Settings.SPI_MISO_pin = -1; Settings.SPI_MOSI_pin = -1; @@ -550,6 +550,17 @@ bool BuildFixes() } } + if (Settings.Build <= 21303) { // 2025-08-25 + // Add second SPI bus + if ((Settings.SPI1_SCLK_pin == 0) && + (Settings.SPI1_MISO_pin == 0) && + (Settings.SPI1_MOSI_pin == 0) ){ + Settings.SPI1_SCLK_pin = -1; + Settings.SPI1_MISO_pin = -1; + Settings.SPI1_MOSI_pin = -1; + } + } + // Starting 2022/08/18 // Use get_build_nr() value for settings transitions. // This value will also be shown when building using PlatformIO, when showing the Compile time defines diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f7265d487..07a7e6fe5 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -168,7 +168,7 @@ void run_compiletime_checks() { #endif } - constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit); +// constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit); constexpr size_t offset_ConnectionFailuresThreshold = offsetof(SettingsStruct, ConnectionFailuresThreshold); static_assert(184 == offset_ConnectionFailuresThreshold, ""); @@ -178,7 +178,7 @@ void run_compiletime_checks() { static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS - static_assert((228 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. + static_assert((232 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. static_assert((200 + (67 * TASKS_MAX)) == offsetof(SettingsStruct, ControllerEnabled), ""); // Used to compute true offset. diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 6763dbb4d..cf409cbf6 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -20,6 +20,7 @@ #include "../Helpers/Hardware_device_info.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware_SPI.h" #include "../Helpers/I2C_access.h" #include "../Helpers/Misc.h" #include "../Helpers/PortStatus.h" @@ -35,8 +36,6 @@ # include #endif // if defined(ESP32) -// #include "../../ESPEasy-Globals.h" - #ifdef ESP32 # include # include @@ -44,45 +43,14 @@ # include # include - # if ESP_IDF_VERSION_MAJOR == 4 - # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 - # include - # include - # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # include - # include - # include - # else // if CONFIG_IDF_TARGET_ESP32S3 - # error Target CONFIG_IDF_TARGET is not supported - # endif // if CONFIG_IDF_TARGET_ESP32S3 - # else // ESP32 IDF 5.x and later - # include - # include - # include - # endif // if ESP_IDF_VERSION_MAJOR == 4 + // ESP32 IDF 5.x and later + # include + # include + # include -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # if ESP_IDF_VERSION_MAJOR < 5 - # define HAS_HALL_EFFECT_SENSOR 1 - # else // if ESP_IDF_VERSION_MAJOR < 5 + // Support for Hall Effect sensor was removed in ESP_IDF 5.x + # define HAS_HALL_EFFECT_SENSOR 0 -// Support for Hall Effect sensor was removed in ESP_IDF 5.x - # define HAS_HALL_EFFECT_SENSOR 0 - # endif // if ESP_IDF_VERSION_MAJOR < 5 -# else - # define HAS_HALL_EFFECT_SENSOR 0 -# endif - - -# if ESP_IDF_VERSION_MAJOR >= 5 # include # include @@ -91,27 +59,11 @@ // #include -# endif // if ESP_IDF_VERSION_MAJOR >= 5 - # include "../Helpers/Hardware_ADC_cali.h" -#if FEATURE_ETHERNET -#include -#endif - #endif // ifdef ESP32 -#if FEATURE_SD -# include -#endif // if FEATURE_SD - - -#include - - -# define GPIO_PLUGIN_ID 1 - /********************************************************************************************\ * Initialize specific hardware settings (only global ones, others are set through devices) \*********************************************************************************************/ @@ -206,85 +158,22 @@ void hardwareInit() } } + // Initialize I2C buses +#if FEATURE_I2C initI2C(); +#endif #if FEATURE_PLUGIN_PRIORITY String dummy; PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); #endif // if FEATURE_PLUGIN_PRIORITY - bool tryInitSPI = true; -#if FEATURE_ETHERNET -// FIXME TD-er: Is this still needed? - if ((Settings.NetworkMedium == ESPEasy::net::NetworkMedium_t::Ethernet) && - isValid(Settings.ETH_Phy_Type) && - ESPEasy::net::isSPI_EthernetType(Settings.ETH_Phy_Type)) - { -#if !ETH_SPI_SUPPORTS_CUSTOM - tryInitSPI = false; +#if FEATURE_SPI + // Initialize SPI buses, also initializes SD-card when available in build and configured + initializeSPIBuses(); #endif - } -#endif - - - // SPI Init - bool SPI_initialized = false; - if (tryInitSPI && Settings.isSPI_valid()) - { - SPI.setHwCs(false); - - // MFD: for ESP32 enable the SPI on HSPI as the default is VSPI - #ifdef ESP32 - - const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); - int8_t spi_gpios[3] = {}; - - if (Settings.getSPI_pins(spi_gpios)) { - if (SPI_selection == SPI_Options_e::Vspi_Fspi) { - SPI.begin(); // Default SPI bus - } else { - SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); - } - SPI_initialized = true; - } - #else // ifdef ESP32 - SPI.begin(); - SPI_initialized = true; - #endif // ifdef ESP32 - } - - if (SPI_initialized) - { -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); -#endif - #if FEATURE_SD - - if (Settings.Pin_sd_cs >= 0) - { - if (SD.begin(Settings.Pin_sd_cs)) - { -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, F("SD : Init OK")); -#endif - } - else - { - SD.end(); -#ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); -#endif - } - } -#endif // if FEATURE_SD -#ifndef BUILD_NO_DEBUG - } else { - addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); -#endif - } } - void checkResetFactoryPin() { static uint8_t factoryResetCounter = 0; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index dc0f21bb4..68d3f406a 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -57,23 +57,8 @@ int espeasy_analogRead(int pin, int getCPU_MaxFreqMHz(); int getCPU_MinFreqMHz(); -# if ESP_IDF_VERSION_MAJOR < 5 -# if CONFIG_IDF_TARGET_ESP32 -# define ESP_PM_CONFIG_T esp_pm_config_esp32_t -# elif CONFIG_IDF_TARGET_ESP32S3 -# define ESP_PM_CONFIG_T esp_pm_config_esp32s3_t -# elif CONFIG_IDF_TARGET_ESP32S2 -# define ESP_PM_CONFIG_T esp_pm_config_esp32s2_t -# elif CONFIG_IDF_TARGET_ESP32C6 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t -# elif CONFIG_IDF_TARGET_ESP32C3 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t -# elif CONFIG_IDF_TARGET_ESP32C2 -# define ESP_PM_CONFIG_T esp_pm_config_esp32c2_t -# endif // if CONFIG_IDF_TARGET_ESP32 -# else // if ESP_IDF_VERSION_MAJOR < 5 -# define ESP_PM_CONFIG_T esp_pm_config_t -# endif // if ESP_IDF_VERSION_MAJOR < 5 +// if ESP_IDF_VERSION_MAJOR > 5 +# define ESP_PM_CONFIG_T esp_pm_config_t #endif // ifdef ESP32 diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index b4c210c6b..6743da518 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -7,20 +7,14 @@ #include "../Helpers/I2C_access.h" #include "../Helpers/StringConverter.h" +#if FEATURE_I2C #include void initI2C() { // configure hardware pins according to eeprom settings. - if (!Settings.isI2CEnabled(0) - #if FEATURE_I2C_MULTIPLE - && !Settings.isI2CEnabled(1) - # if FEATURE_I2C_INTERFACE_3 - && !Settings.isI2CEnabled(2) - # endif // if FEATURE_I2C_INTERFACE_3 - #endif // if FEATURE_I2C_MULTIPLE - ) + if (Settings.getNrConfiguredI2C_buses() == 0) { return; } @@ -263,7 +257,7 @@ void I2CMultiplexerSelectByTaskIndex(taskIndex_t taskIndex) { const uint8_t i2cBus = 0; # endif // if FEATURE_I2C_MULTIPLE - if (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { uint8_t i = Settings.I2C_Multiplexer_Channel[taskIndex]; if (i > 7) { return; } @@ -322,8 +316,9 @@ bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex) { # endif // if FEATURE_I2C_MULTIPLE if (!isI2CMultiplexerEnabled(i2cBus)) { return false; } - return (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) - || (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); + return (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) + || (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); } -#endif // if FEATURE_I2CMULTIPLEXER +#endif +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware_I2C.h b/src/src/Helpers/Hardware_I2C.h index 22b3d3947..fc546350a 100644 --- a/src/src/Helpers/Hardware_I2C.h +++ b/src/src/Helpers/Hardware_I2C.h @@ -3,6 +3,8 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C + #include "../DataTypes/TaskIndex.h" void initI2C(); @@ -37,5 +39,5 @@ void I2CMultiplexerReset(uint8_t i2cBus); bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex); #endif // if FEATURE_I2CMULTIPLEXER - +#endif #endif // ifndef HELPERS_HARDWARE_I2C_H diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp new file mode 100644 index 000000000..2ac275b2c --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -0,0 +1,142 @@ + +#include "../ESPEasy/net/DataTypes/NetworkMedium.h" +#include "../Helpers/Hardware_SPI.h" +#include "../Globals/Settings.h" + +#if FEATURE_SPI +#if FEATURE_SD +# include +#endif // if FEATURE_SD + +#include + +#include "../Globals/SPIe.h" + +#if FEATURE_ETHERNET +# include +#endif // if FEATURE_ETHERNET + +void initializeSPIBuses() { + // SPI Init + uint8_t SPI_initialized = 0; + + #ifdef ESP32 + uint8_t skipInitSPI = 0; + #endif // ifdef ESP32 + + #if FEATURE_ETHERNET + + if ((Settings.NetworkMedium == ESPEasy::net::NetworkMedium_t::Ethernet) && + isValid(Settings.ETH_Phy_Type) && + isSPI_EthernetType(Settings.ETH_Phy_Type)) + { + # if !ETH_SPI_SUPPORTS_CUSTOM + skipInitSPI = Settings.getSPIBusForEth() + 1; // Increment by 1 + # endif // if !ETH_SPI_SUPPORTS_CUSTOM + } + #endif // if FEATURE_ETHERNET + + + if (Settings.getNrConfiguredSPI_buses()) + { + #ifdef ESP32 + + const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); + int8_t spi_gpios[3] = {}; + + if (skipInitSPI != 1) { + SPI.end(); // Disconnect current GPIO mapping + } + + if ((skipInitSPI != 1) && Settings.getSPI_pins(spi_gpios, 0u)) { + SPI.setHwCs(false); + SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration + SPI_initialized |= 1; + } + + delay(1); + + // Init second SPI interface (SPIe) + const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); + + if (skipInitSPI != 2) { + SPIe.end(); // Disconnect current GPIO mapping + } + + if ((skipInitSPI != 2) && Settings.getSPI_pins(spi_gpios, 1u)) { + SPIe.setHwCs(false); + SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + SPI_initialized |= 2; + } + + #else // ifdef ESP32 + SPI.setHwCs(false); + SPI.begin(); // Use default GPIO configuration + SPI_initialized = 1; + #endif // ifdef ESP32 + } + + if (SPI_initialized) + { + #ifdef ESP32 + + for (uint8_t i = 1; i < 3; ++i) { + if (SPI_initialized & i) { + addLog(LOG_LEVEL_INFO, strformat(F("INIT : SPI Bus %d Init (without CS)"), i - 1)); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); + #endif // ifdef ESP8266 + + #if FEATURE_SD + initSDcard(); + #endif // if FEATURE_SD + } else { + addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); + } +} + +#if FEATURE_SD +bool initSDcard() { + bool result = false; + + if (Settings.Pin_sd_cs >= 0) + { + # ifdef ESP32 + const uint8_t sdspi = Settings.getSPIBusForSDCard(); + # else // ifdef ESP32 + constexpr uint8_t sdspi{}; + # endif // ifdef ESP32 + + if (Settings.isSPI_enabled(sdspi)) { + # ifdef ESP32 + + if (SD.begin(Settings.Pin_sd_cs, (0 == sdspi) ? SPI : SPIe)) + # endif // ifdef ESP32 + # ifdef ESP8266 + + if (SD.begin(Settings.Pin_sd_cs)) + # endif // ifdef ESP8266 + { + # ifdef ESP32 + addLog(LOG_LEVEL_INFO, strformat(F("SD : Init on SPI Bus %d OK"), sdspi)); + # endif // ifdef ESP32 + # ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("SD : Init OK")); + # endif // ifdef ESP8266 + result = true; + } + else + { + SD.end(); + addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); + } + } + } + return result; +} + +#endif // if FEATURE_SD +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware_SPI.h b/src/src/Helpers/Hardware_SPI.h new file mode 100644 index 000000000..c9c7ee0df --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.h @@ -0,0 +1,11 @@ +#pragma once + +#include "../../ESPEasy_common.h" + +#if FEATURE_SPI +void initializeSPIBuses(); + +#if FEATURE_SD +bool initSDcard(); +#endif // if FEATURE_SD +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware_device_info.cpp b/src/src/Helpers/Hardware_device_info.cpp index 527c8ec2e..8598556be 100644 --- a/src/src/Helpers/Hardware_device_info.cpp +++ b/src/src/Helpers/Hardware_device_info.cpp @@ -266,7 +266,8 @@ uint32_t getFlashChipSpeed() { // spi_clk is equal to system clock return getApbFrequency(); } - return spiClockDivToFrequency(spi_clock); + // TODO TD-er: For P4 a pointer to the SPI bus is needed. + return spiClockDivToFrequency(nullptr, spi_clock); #endif # endif // if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED #endif // ifdef ESP8266 diff --git a/src/src/Helpers/Hardware_device_info.h b/src/src/Helpers/Hardware_device_info.h index 48548fc1b..6b82fe44e 100644 --- a/src/src/Helpers/Hardware_device_info.h +++ b/src/src/Helpers/Hardware_device_info.h @@ -103,6 +103,7 @@ constexpr uint8_t getI2CBusCount() { #if FEATURE_I2C_MULTIPLE // Not querying the supported nr. of I2C busses in hardware, but using software multiplexing // Assume/expect IDF 5.x + // FIXME TD-er: Maybe we should look at SOC_HP_I2C_NUM ???? // # if defined(SOC_I2C_SUPPORTED) && SOC_I2C_SUPPORTED # if FEATURE_I2C_INTERFACE_3 return 3u; // SOC_I2C_NUM; // Let's go for all I2C busses, including LP_I2C (low power, where available) @@ -117,6 +118,18 @@ constexpr uint8_t getI2CBusCount() { #endif } +// Get the number of usable SPI buses +constexpr uint8_t getSPIBusCount() { +#if SOC_SPI_PERIPH_NUM > 3 + static_assert(false, "Implement processor architecture"); + return 2u; +#elif SOC_SPI_PERIPH_NUM > 2 + return 2u; +#else + return 1u; +#endif +} + /********************************************************************************************\ PSRAM support \*********************************************************************************************/ diff --git a/src/src/Helpers/I2C_Plugin_Helper.cpp b/src/src/Helpers/I2C_Plugin_Helper.cpp index 2f92f3b57..fc976f108 100644 --- a/src/src/Helpers/I2C_Plugin_Helper.cpp +++ b/src/src/Helpers/I2C_Plugin_Helper.cpp @@ -7,6 +7,7 @@ * outputToHtml = false: no html output is generated * Outputs an error message and returns false if not correct **********************************************************************/ +#if FEATURE_I2C bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, bool outputToHtml) { #if FEATURE_I2C_MULTIPLE @@ -28,3 +29,4 @@ bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, #endif // if FEATURE_I2CMULTIPLEXER return true; } +#endif \ No newline at end of file diff --git a/src/src/Helpers/I2C_Plugin_Helper.h b/src/src/Helpers/I2C_Plugin_Helper.h index 1f734dd52..ba7d7337c 100644 --- a/src/src/Helpers/I2C_Plugin_Helper.h +++ b/src/src/Helpers/I2C_Plugin_Helper.h @@ -4,6 +4,8 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C + #include "../Globals/Plugins.h" #include "../Globals/Settings.h" #include "../Helpers/Hardware_I2C.h" @@ -13,4 +15,5 @@ bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, bool outputToHtml = true); +#endif #endif // ifndef _SRC_HELPERS_I2C_PLUGIN_HELPER_H diff --git a/src/src/Helpers/I2C_access.cpp b/src/src/Helpers/I2C_access.cpp index c55563074..bd12c53b5 100644 --- a/src/src/Helpers/I2C_access.cpp +++ b/src/src/Helpers/I2C_access.cpp @@ -1,5 +1,7 @@ #include "../Helpers/I2C_access.h" +#if FEATURE_I2C + #include "../DataStructs/TimingStats.h" #include "../Globals/I2Cdev.h" #include "../Globals/Settings.h" @@ -509,13 +511,7 @@ void I2CInterfaceSelector(String label, String id, uint8_t choice, bool reloadWhenNeeded) { - const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1 - ? ((Settings.isI2CEnabled(1) ? 1 : 0) - # if FEATURE_I2C_INTERFACE_3 - + (Settings.isI2CEnabled(2) ? 1 : 0) - # endif // if FEATURE_I2C_INTERFACE_3 - ) - : 0) + (Settings.isI2CEnabled(0) ? 1 : 0); + const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); if (i2cMaxBusCount > 1) { static uint8_t i2cBusCount = 0; @@ -563,3 +559,4 @@ void I2CInterfaceSelector(String label, } #endif // if FEATURE_I2C_MULTIPLE +#endif \ No newline at end of file diff --git a/src/src/Helpers/I2C_access.h b/src/src/Helpers/I2C_access.h index 2041445b7..3d05fc6e5 100644 --- a/src/src/Helpers/I2C_access.h +++ b/src/src/Helpers/I2C_access.h @@ -1,6 +1,8 @@ #ifndef HELPERS_I2C_ACCESS_H #define HELPERS_I2C_ACCESS_H +#include "../../ESPEasy_common.h" +#if FEATURE_I2C #include "../DataStructs/I2CTypes.h" #include "../DataTypes/TaskIndex.h" @@ -201,4 +203,5 @@ void I2CInterfaceSelector(String label, bool reloadWhenNeeded); #endif // if FEATURE_I2C_MULTIPLE +#endif #endif // HELPERS_I2C_ACCESS_H diff --git a/src/src/Helpers/KeyValueWriter_JSON.cpp b/src/src/Helpers/KeyValueWriter_JSON.cpp index 0d45907c7..1b3011858 100644 --- a/src/src/Helpers/KeyValueWriter_JSON.cpp +++ b/src/src/Helpers/KeyValueWriter_JSON.cpp @@ -121,6 +121,8 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) const size_t nrValues = kv._values.size(); + const bool forceString = kv._format == KeyValueStruct::Format::PreFormatted; + if (!kv._isArray) { // Either 1 value or empty value if (nrValues == 0) { @@ -129,7 +131,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) pr.write('"'); } else { - writeValue(kv._values[0]); + writeValue(kv._values[0], forceString); } } else { // Multiple values, so we must wrap it in [] @@ -153,7 +155,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) pr.write('\t'); #endif // ifdef USE_KWH_JSON_PRETTY_PRINT - writeValue(kv._values[i]); + writeValue(kv._values[i], forceString); } getPrint().write(']'); #ifndef USE_KWH_JSON_PRETTY_PRINT @@ -162,7 +164,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) } } -void KeyValueWriter_JSON::writeValue(const ValueStruct& val) +void KeyValueWriter_JSON::writeValue(const ValueStruct& val, bool forceString) { if (!val.isSet()) { return; } auto& pr = getPrint(); @@ -196,6 +198,14 @@ void KeyValueWriter_JSON::writeValue(const ValueStruct& val) case ValueStruct::ValueType::Unset: case ValueStruct::ValueType::String: case ValueStruct::ValueType::FlashString: + if (forceString) { + String tmp(to_json_value(str)); + if (!isWrappedWithQuotes(tmp)) { + tmp = wrap_String(tmp, '"'); + } + pr.print(tmp); + return; + } break; } pr.print(to_json_value(str)); diff --git a/src/src/Helpers/KeyValueWriter_JSON.h b/src/src/Helpers/KeyValueWriter_JSON.h index e32483d44..db853bb13 100644 --- a/src/src/Helpers/KeyValueWriter_JSON.h +++ b/src/src/Helpers/KeyValueWriter_JSON.h @@ -73,7 +73,7 @@ public: private: - void writeValue(const ValueStruct& value); + void writeValue(const ValueStruct& value, bool forceString); #ifdef USE_KWH_JSON_PRETTY_PRINT diff --git a/src/src/Helpers/LogStreamWriter.cpp b/src/src/Helpers/LogStreamWriter.cpp index b58e56ce2..d50eb9105 100644 --- a/src/src/Helpers/LogStreamWriter.cpp +++ b/src/src/Helpers/LogStreamWriter.cpp @@ -14,7 +14,7 @@ void LogStreamWriter::clear() _readpos = 0; } -bool LogStreamWriter::process(Stream*stream, size_t availableForWrite) +bool LogStreamWriter::process(Print*stream, size_t availableForWrite) { if (stream == nullptr) { return false; } return write(*stream, availableForWrite) != 0; @@ -30,7 +30,7 @@ uint32_t LogStreamWriter::getNrMessages() const return Logging.getNrMessages(_log_destination); } -size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite) +size_t LogStreamWriter::write(Print& stream, size_t nrBytesToWrite) { size_t bytesWritten = 0; @@ -45,7 +45,7 @@ size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite) return bytesWritten; } -size_t LogStreamWriter::write_single_item(Stream& stream, +size_t LogStreamWriter::write_single_item(Print& stream, size_t nrBytesToWrite) { const size_t res = write_item(stream, nrBytesToWrite); @@ -54,7 +54,7 @@ size_t LogStreamWriter::write_single_item(Stream& stream, return res; } -size_t LogStreamWriter::write_item(Stream& stream, +size_t LogStreamWriter::write_item(Print& stream, size_t nrBytesToWrite) { size_t bytesWritten = 0; @@ -103,7 +103,7 @@ size_t LogStreamWriter::write_item(Stream& stream, ++_readpos; } else { if ((bytesWritten + 2) > nrBytesToWrite) { return bytesWritten; } - bytesWritten += stream.println(); + bytesWritten += stream.print(F("\r\n")); // stream.println(); // Done with entry, cleanup and leave clear(); @@ -113,6 +113,6 @@ size_t LogStreamWriter::write_item(Stream& stream, return bytesWritten; } -size_t LogStreamWriter::write_skipping(Stream& stream) { return 0; } +size_t LogStreamWriter::write_skipping(Print& stream) { return 0; } void LogStreamWriter::prepare_prefix() {} diff --git a/src/src/Helpers/LogStreamWriter.h b/src/src/Helpers/LogStreamWriter.h index 2e94453ed..b32866401 100644 --- a/src/src/Helpers/LogStreamWriter.h +++ b/src/src/Helpers/LogStreamWriter.h @@ -11,7 +11,7 @@ public: virtual ~LogStreamWriter() {} - virtual bool process(Stream* stream, size_t availableForWrite); + virtual bool process(Print* stream, size_t availableForWrite); // Only use this from derived classes, as we need a Stream to further process virtual bool process(); @@ -24,20 +24,20 @@ protected: // Write continuously until either nrBytesToWrite was reached or no new messages were available to process. // @retval Number of bytes written. Zero when no new message was available to process. - virtual size_t write(Stream& stream, + virtual size_t write(Print& stream, size_t nrBytesToWrite); // Write single item and clear() on return. // This way each call starts with a new item and long messages may get truncated based on nrBytesToWrite // @retval Number of bytes written. Zero when no new message was available to process. - virtual size_t write_single_item(Stream& stream, + virtual size_t write_single_item(Print& stream, size_t nrBytesToWrite); - virtual size_t write_item(Stream& stream, + virtual size_t write_item(Print& stream, size_t nrBytesToWrite); - virtual size_t write_skipping(Stream& stream); + virtual size_t write_skipping(Print& stream); virtual void prepare_prefix(); diff --git a/src/src/Helpers/MDNS_Helper.cpp b/src/src/Helpers/MDNS_Helper.cpp index bef39c021..fce3eb844 100644 --- a/src/src/Helpers/MDNS_Helper.cpp +++ b/src/src/Helpers/MDNS_Helper.cpp @@ -31,8 +31,8 @@ void update_mDNS() { if (webserverRunning) { if (!mDNS_init) { addLog(LOG_LEVEL_INFO, F("mDNS : Starting mDNS...")); - mDNS_init = MDNS.begin(NetworkGetHostname().c_str()); - MDNS.setInstanceName(NetworkGetHostname()); // Needed for when the hostname has changed. + mDNS_init = MDNS.begin(ESPEasy::net::NetworkGetHostname().c_str()); + MDNS.setInstanceName(ESPEasy::net::NetworkGetHostname()); // Needed for when the hostname has changed. if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("mDNS : "); diff --git a/src/src/Helpers/Misc.cpp b/src/src/Helpers/Misc.cpp index a536c13a5..1d081705f 100644 --- a/src/src/Helpers/Misc.cpp +++ b/src/src/Helpers/Misc.cpp @@ -15,11 +15,12 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringParser.h" +#include "../../ESPEasy/net/_NWPlugin_Helper.h" + #if FEATURE_SD # include #endif // if FEATURE_SD - bool remoteConfig(struct EventStruct *event, const String& string) { // FIXME TD-er: Why have an event here as argument? It is not used. @@ -80,18 +81,25 @@ bool setNetworkEnableStatus(ESPEasy::net::networkIndex_t networkIndex, bool enab #endif // ifndef BUILD_NO_RAM_TRACKER // Only enable network if it has a network interface configured - if (Settings.getNWPluginID_for_network(networkIndex) != ESPEasy::net::INVALID_NW_PLUGIN_ID || !enabled) { + if ((Settings.getNWPluginID_for_network(networkIndex) != ESPEasy::net::INVALID_NW_PLUGIN_ID) || !enabled) { struct EventStruct TempEvent; TempEvent.NetworkIndex = networkIndex; String dummy; if (!enabled) { - ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_EXIT, &TempEvent, dummy); + // Use the scheduler as this also removes any pending init calls. + Scheduler.setNetworkExitTimer(10, networkIndex); } Settings.setNetworkEnabled(networkIndex, enabled); + if (enabled) { - if (!ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_INIT, &TempEvent, dummy)) - return false; + if (ESPEasy::net::getNWPluginData(networkIndex) == nullptr) { + // Only init when not yet started + + if (!ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_INIT, &TempEvent, dummy)) { + return false; + } + } } return true; @@ -99,7 +107,6 @@ bool setNetworkEnableStatus(ESPEasy::net::networkIndex_t networkIndex, bool enab return false; } - /********************************************************************************************\ Toggle controller enabled state \*********************************************************************************************/ @@ -115,6 +122,7 @@ bool setControllerEnableStatus(controllerIndex_t controllerIndex, bool enabled) struct EventStruct TempEvent; TempEvent.ControllerIndex = controllerIndex; String dummy; + if (!enabled) { CPluginCall(CPlugin::Function::CPLUGIN_EXIT, &TempEvent, dummy); } @@ -217,6 +225,7 @@ void taskClear(taskIndex_t taskIndex, bool save) it is excluded from the calculation ! \*********************************************************************************************/ #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) + void dump(uint32_t addr) { // Seems already included in core 2.4 ... serialPrint(String(addr, HEX)); serialPrint(": "); @@ -275,9 +284,7 @@ void dump(uint32_t addr) { // Seems already included in core 2.4 ... /********************************************************************************************\ Handler for keeping ExtraTaskSettings up to date using cache \*********************************************************************************************/ -String getTaskDeviceName(taskIndex_t TaskIndex) { - return Cache.getTaskDeviceName(TaskIndex); -} +String getTaskDeviceName(taskIndex_t TaskIndex) { return Cache.getTaskDeviceName(TaskIndex); } /********************************************************************************************\ Handler for getting Value Names from TaskIndex @@ -578,22 +585,16 @@ void RGB2HSV(uint8_t r, uint8_t g, uint8_t b, float hsv[3]) { hsv[2] = v * 255.0f; } -float getCPUload() { - return 100.0f - Scheduler.getIdleTimePct(); -} +float getCPUload() { return 100.0f - Scheduler.getIdleTimePct(); } -int getLoopCountPerSec() { - return loopCounterLast / 30; -} +int getLoopCountPerSec() { return loopCounterLast / 30; } -int getUptimeMinutes() { - return wdcounter / 2; -} +int getUptimeMinutes() { return wdcounter / 2; } /****************************************************************************** * scan an int array of specified size for a value *****************************************************************************/ -bool intArrayContains(const int arraySize, const int array[], const int& value) { +bool intArrayContains(const int arraySize, const int array[], const int& value) { for (int i = 0; i < arraySize; i++) { if (array[i] == value) { return true; } } @@ -608,6 +609,7 @@ bool intArrayContains(const int arraySize, const uint8_t array[], const uint8_t& } #ifndef BUILD_NO_RAM_TRACKER + void logMemUsageAfter(const __FlashStringHelper *function, int value) { // Store free memory in an int, as subtracting may sometimes result in negative value. // The recorded used memory is not an exact value, as background (or interrupt) tasks may also allocate or free heap memory. @@ -625,11 +627,13 @@ void logMemUsageAfter(const __FlashStringHelper *function, int value) { log += value; } - while (log.length() < 30) { log += ' '; } + while (log.length() < 30) { log += ' '; + } log += F("Free mem after: "); log += freemem_end; - while (log.length() < 55) { log += ' '; } + while (log.length() < 55) { log += ' '; + } log += F("diff: "); log += last_freemem - freemem_end; addLogMove(LOG_LEVEL_DEBUG, log); diff --git a/src/src/Helpers/Misc.h b/src/src/Helpers/Misc.h index 5b263a374..098c4039e 100644 --- a/src/src/Helpers/Misc.h +++ b/src/src/Helpers/Misc.h @@ -21,11 +21,13 @@ #define setNBitToUL(N, B, V, M) N=(((N) & ~((M) << (B))) | (static_cast((V) & (M)) << (B))) #define getNBitFromUL(number, bitnr, mask) (((number) >> (bitnr)) & (mask)) +#define set16BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFFFUL) #define set8BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFUL) #define set4BitToUL(N, B, V) setNBitToUL(N, B, V, 0x0FUL) #define set3BitToUL(N, B, V) setNBitToUL(N, B, V, 0x07UL) #define set2BitToUL(N, B, V) setNBitToUL(N, B, V, 0x03UL) +#define get16BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFFFUL) #define get8BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFUL) #define get4BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x0FUL) #define get3BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x07UL) diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index a0e69bedf..10e2aede0 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -559,7 +559,7 @@ void SSDP_schema() { return; } - const IPAddress ip = NetworkLocalIP(); + const IPAddress ip = ESPEasy::net::NetworkLocalIP(); const uint32_t chipId = ESP.getChipId(); char uuid[64]; @@ -658,7 +658,7 @@ bool SSDP_begin() { ip_addr_t ifaddr; - ifaddr.addr = NetworkLocalIP(); + ifaddr.addr = ESPEasy::net::NetworkLocalIP(); ip_addr_t multicast_addr; multicast_addr.addr = (uint32_t)SSDP_MULTICAST_ADDR; @@ -705,7 +705,7 @@ bool SSDP_begin() { Send SSDP messages (notify & responses) \*********************************************************************************************/ void SSDP_send(uint8_t method) { - uint32_t ip = NetworkLocalIP(); + uint32_t ip = ESPEasy::net::NetworkLocalIP(); // FIXME TD-er: Why create String objects of these flashstrings? String _ssdp_response_template = F( diff --git a/src/src/Helpers/OLed_helper.cpp b/src/src/Helpers/OLed_helper.cpp index b7ffe4dc3..4835ad366 100644 --- a/src/src/Helpers/OLed_helper.cpp +++ b/src/src/Helpers/OLed_helper.cpp @@ -1,5 +1,7 @@ #include "../Helpers/OLed_helper.h" +#if defined(USES_P023) || defined(USES_P036) || defined(USES_P109) + /************************************************************************** * Select controller type, SSD1306 or SH1106 *************************************************************************/ @@ -126,3 +128,4 @@ bool OLedI2CAddressCheck(uint8_t function, } return success; } +#endif \ No newline at end of file diff --git a/src/src/Helpers/OLed_helper.h b/src/src/Helpers/OLed_helper.h index 94cb2f15f..90ffb5280 100644 --- a/src/src/Helpers/OLed_helper.h +++ b/src/src/Helpers/OLed_helper.h @@ -1,8 +1,6 @@ #ifndef HELPERS_OLED_HELPER_H #define HELPERS_OLED_HELPER_H #include "../../_Plugin_Helper.h" -#include "SSD1306.h" -#include "SH1106Wire.h" /** * Helper with OLed related functions @@ -14,6 +12,10 @@ * and generic function OLedSetContrast */ +#if defined(USES_P023) || defined(USES_P036) || defined(USES_P109) +#include "SSD1306.h" +#include "SH1106Wire.h" + #define OLED_CONTRAST_OFF 0x01 #define OLED_CONTRAST_LOW 0x40 #define OLED_CONTRAST_MED 0xCF @@ -37,4 +39,5 @@ bool OLedI2CAddressCheck(uint8_t function, const __FlashStringHelper *id, int8_t deviceAddress); +#endif #endif // ifndef HELPERS_OLED_HELPER_H diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index d7dead409..66e9c3a61 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -62,8 +62,13 @@ void run50TimesPerSecond() { #ifdef ESP32 static const NetworkInterface *lastDefaultInterface = nullptr; NetworkInterface * currentDefaultInterface = Network.getDefaultInterface(); - if (nonDefaultNetworkInterface_gotIP || lastDefaultInterface != currentDefaultInterface) { + if (nonDefaultNetworkInterface_gotIP || + networkConnectionFailed || + lastDefaultInterface != currentDefaultInterface) { nonDefaultNetworkInterface_gotIP = false; + + // TODO TD-er: Must do something else here on failed connect? + networkConnectionFailed = false; ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED, 0, dummy); lastDefaultInterface = currentDefaultInterface; } @@ -212,6 +217,7 @@ void runOncePerSecond() #endif // I2C Watchdog feed +#if FEATURE_I2C if (Settings.WDI2CAddress != 0) { #if FEATURE_I2C_MULTIPLE @@ -219,6 +225,7 @@ void runOncePerSecond() #endif // if FEATURE_I2C_MULTIPLE I2C_write8(Settings.WDI2CAddress, 0xA5); } +#endif #if FEATURE_MDNS #ifdef ESP8266 diff --git a/src/src/Helpers/SPI_Helper.cpp b/src/src/Helpers/SPI_Helper.cpp new file mode 100644 index 000000000..f850358ca --- /dev/null +++ b/src/src/Helpers/SPI_Helper.cpp @@ -0,0 +1,43 @@ +#include "../Helpers/SPI_Helper.h" + +#if FEATURE_SPI +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled) { + const uint8_t spiCount = getSPIBusCount(); + const bool spi0valid = Settings.isSPI_valid(0); + const bool spi1valid = Settings.isSPI_valid(1); + const uint8_t spiBitmap = (spi0valid ? 1 : 0) | (spi1valid ? 2 : 0); + const uint8_t spiMaxBusCount = (spiCount > 1 + ? (spi1valid ? 1 : 0) + : 0) + (spi0valid ? 1 : 0); + + if ((spiMaxBusCount > 1) || spi1valid) { // Show selector if only bus 1 is enabled + static uint8_t spiBusCount{}; + static uint8_t spiBusBitmap{}; + static int spiBusNumbers[2]; + static String spiBusAttr[2]; + + if (spiBusBitmap != spiBitmap) { + spiBusCount = 0; + spiBusNumbers[spiBusCount] = 0; + spiBusAttr[0] = spi0valid ? EMPTY_STRING : F("disabled"); + spiBusBitmap = spi0valid ? 1 : 0; + ++spiBusCount; + + if ((spiCount > 1) && spi1valid) { + spiBusNumbers[spiBusCount] = 1; + spiBusBitmap |= 2; + ++spiBusCount; + } + } + FormSelectorOptions selector(spiBusCount, + spiBusNumbers, + spiBusAttr); + selector.default_index = 0; + selector.enabled = !disabled; + selector.addFormSelector(label, id, choice); + } +} +#endif \ No newline at end of file diff --git a/src/src/Helpers/SPI_Helper.h b/src/src/Helpers/SPI_Helper.h new file mode 100644 index 000000000..430a29ad9 --- /dev/null +++ b/src/src/Helpers/SPI_Helper.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../DataStructs/SettingsStruct.h" +#include "../DataTypes/TaskIndex.h" +#include "../Globals/Plugins.h" +#include "../Globals/Settings.h" +#include "../Helpers/Hardware_device_info.h" + +#include "../WebServer/Markup_Forms.h" + +#if FEATURE_SPI +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled = false); +#endif \ No newline at end of file diff --git a/src/src/Helpers/Scheduler_NetworkTimer.cpp b/src/src/Helpers/Scheduler_NetworkTimer.cpp index 7bb085edf..d4b705e07 100644 --- a/src/src/Helpers/Scheduler_NetworkTimer.cpp +++ b/src/src/Helpers/Scheduler_NetworkTimer.cpp @@ -4,6 +4,8 @@ #include "../Globals/Settings.h" +#include "../../ESPEasy/net/_NWPlugin_Helper.h" + /*********************************************************************************************\ * Network Adapter Timer (NWPLUGIN_TASKTIMER_IN) * Can be scheduled per combo networkIndex & Par1 (20 least significant bits) @@ -11,12 +13,22 @@ void ESPEasy_Scheduler::setNetworkInitTimer(unsigned long msecFromNow, ESPEasy::net::networkIndex_t networkIndex) { - setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_INIT); + if (ESPEasy::net::getNWPluginData(networkIndex) == nullptr) { + // Only init when not yet started + setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_INIT); + } } void ESPEasy_Scheduler::setNetworkExitTimer(unsigned long msecFromNow, ESPEasy::net::networkIndex_t networkIndex) { + // First check if there are any pending INIT timers + const NWPluginTimerID timerID(networkIndex, 0, NWPlugin::Function::NWPLUGIN_INIT); + auto it = systemTimers.find(timerID.mixed_id); + if (it != systemTimers.end()) { + systemTimers.erase(it); + } + setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_EXIT); } @@ -35,11 +47,14 @@ void ESPEasy_Scheduler::setNetworkTimer(unsigned long msecFromNow const NWPluginTimerID timerID(networkIndex, Par1, function); - systemTimerStruct timer_data; + if (systemTimers.find(timerID.mixed_id) == systemTimers.end()) { + // Only set timer if not already set. + systemTimerStruct timer_data; - timer_data.fromEvent(networkIndex, Par1, Par2, Par3, Par4, Par5); - systemTimers[timerID.mixed_id] = timer_data; - setNewTimerAt(timerID, millis() + msecFromNow); + timer_data.fromEvent(networkIndex, Par1, Par2, Par3, Par4, Par5); + systemTimers[timerID.mixed_id] = timer_data; + setNewTimerAt(timerID, millis() + msecFromNow); + } } void ESPEasy_Scheduler::process_network_timer(SchedulerTimerID id) diff --git a/src/src/Helpers/SerialWriteBuffer.cpp b/src/src/Helpers/SerialWriteBuffer.cpp index 93a8737b6..0678982ff 100644 --- a/src/src/Helpers/SerialWriteBuffer.cpp +++ b/src/src/Helpers/SerialWriteBuffer.cpp @@ -34,14 +34,10 @@ String SerialWriteBuffer_t::colorize(const String& str) const { } -size_t SerialWriteBuffer_t::write_skipping(Stream& stream) +size_t SerialWriteBuffer_t::write_skipping(Print& stream) { - size_t bytesWritten{}; - // Mark with empty line we skipped the rest of the message. - bytesWritten += stream.println(F(" ...")); - bytesWritten += stream.println(); - return bytesWritten; + return stream.print(F(" ...\r\n\r\n")); } void SerialWriteBuffer_t::prepare_prefix() diff --git a/src/src/Helpers/SerialWriteBuffer.h b/src/src/Helpers/SerialWriteBuffer.h index 556561570..60d8f6a07 100644 --- a/src/src/Helpers/SerialWriteBuffer.h +++ b/src/src/Helpers/SerialWriteBuffer.h @@ -14,7 +14,7 @@ public: private: - size_t write_skipping(Stream& stream) override; + size_t write_skipping(Print& stream) override; void prepare_prefix() override; diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 0b58bb23b..dd98d12e7 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -232,11 +232,14 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose static_assert(false, "Implement processor architecture"); #endif // ifdef ESP32S2 - +#if FEATURE_I2C bool includeI2C = true; +#endif +#if FEATURE_SPI bool includeSPI = true; #if FEATURE_SD bool includeSDCard = true; +#endif #endif bool includeSerial = Settings.UseSerial; // Only need to check if Serial Port Console is enabled bool includeStatusLed = true; @@ -244,6 +247,7 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose switch (purpose) { +#if FEATURE_I2C case PinSelectPurpose::I2C: #if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -253,10 +257,13 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose #endif // if FEATURE_I2C_MULTIPLE includeI2C = false; break; +#endif +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: includeSPI = false; break; +#endif case PinSelectPurpose::Serial_input: case PinSelectPurpose::Serial_output: includeSerial = false; @@ -282,7 +289,7 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose includeResetPin = false; break; } - +#if FEATURE_I2C if (includeI2C && Settings.isI2C_pin(gpio)) { for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) { @@ -319,12 +326,25 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose } } } -#ifdef ESP8266 + #endif - if (includeSPI && Settings.isSPI_pin(gpio)) { + #if FEATURE_SPI + #ifdef ESP32 + if (includeSPI) { + if (Settings.isSPI_pin(gpio, 0u)) { + return F("SPI (bus 0)"); + } + else if (Settings.isSPI_pin(gpio, 1u)) { + return F("SPI (bus 1)"); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 + if (includeSPI && Settings.isSPI_pin(gpio)) { return F("SPI"); } -#endif // ifdef ESP8266 + #endif + #endif if (includeStatusLed && (Settings.Pin_status_led == gpio) && (-1 != gpio)) { return F("Wifi Status LED"); @@ -404,6 +424,7 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig { switch (purpose) { +#if FEATURE_I2C case PinSelectPurpose::I2C: # if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -418,6 +439,8 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig (bus_type == ESP32_BUS_TYPE_I2C_SLAVE_SCL)) { return conflict; } break; +#endif +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: @@ -427,6 +450,7 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig (bus_type == ESP32_BUS_TYPE_SPI_MASTER_SS)) { return conflict; } break; +#endif case PinSelectPurpose::Serial_input: case PinSelectPurpose::Serial_output: diff --git a/src/src/Helpers/StringGenerator_GPIO.h b/src/src/Helpers/StringGenerator_GPIO.h index e28515a8a..52597820a 100644 --- a/src/src/Helpers/StringGenerator_GPIO.h +++ b/src/src/Helpers/StringGenerator_GPIO.h @@ -22,6 +22,7 @@ enum class PinSelectPurpose : uint8_t { Generic_input, Generic_output, Generic_bidir, +#if FEATURE_I2C I2C, #if FEATURE_I2C_MULTIPLE I2C_2, @@ -29,8 +30,11 @@ enum class PinSelectPurpose : uint8_t { I2C_3, #endif #endif +#endif +#if FEATURE_SPI SPI, SPI_MISO, +#endif #if FEATURE_ETHERNET Ethernet, #endif diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 64af7cd89..5103e950e 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -220,10 +220,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) { return KeyValueStruct(F("Start AP on Connect Fail"), !Settings.DoNotStartAPfallback_ConnectFail()); } - case LabelType::WIFI_START_AP_ON_NW002_INIT: - { - return KeyValueStruct(F("Auto Start AP"), Settings.StartAP_on_NW002_init()); - } case LabelType::WIFI_NR_RECONNECT_ATTEMPTS: { return KeyValueStruct(F("Connect Retry Attempts"), Settings.ConnectFailRetryCount); @@ -480,13 +476,13 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) { return KeyValueStruct(F("Show Unit of Measure"), Settings.ShowUnitOfMeasureOnDevicesPage()); } - #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - #if FEATURE_MQTT_CONNECT_BACKGROUND +#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE +#if FEATURE_MQTT_CONNECT_BACKGROUND case LabelType::MQTT_CONNECT_IN_BACKGROUND: { return KeyValueStruct(F("MQTT Connect in background"), Settings.MQTTConnectInBackground()); } - #endif // if FEATURE_MQTT_CONNECT_BACKGROUND +#endif // if FEATURE_MQTT_CONNECT_BACKGROUND #if CONFIG_SOC_WIFI_SUPPORT_5G case LabelType::WIFI_BAND_MODE: @@ -544,7 +540,9 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } KeyValueStruct kv(F("RSSI"), WiFi.RSSI()); #if FEATURE_TASKVALUE_UNIT_OF_MEASURE - KV_SETUNIT(UOM_dBm); + if (!extendedValue) { + KV_SETUNIT(UOM_dBm); + } #endif return kv; } @@ -619,7 +617,7 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) #if FEATURE_MDNS case LabelType::M_DNS: { - const String url = NetworkGetHostname() + F(".local"); + const String url = ESPEasy::net::NetworkGetHostname() + F(".local"); if (extendedValue) { return KeyValueStruct(F("mDNS"), @@ -1025,11 +1023,11 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) KeyValueStruct kv(F("Sketch Size"), str); KV_SETID(F("sketch_size")); - if (!extendedValue) { #if FEATURE_TASKVALUE_UNIT_OF_MEASURE + if (!extendedValue) { KV_SETUNIT(UOM_kB); -#endif } +#endif return kv; } case LabelType::SKETCH_FREE: @@ -1043,18 +1041,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } case LabelType::FS_SIZE: { - String size; - - if (extendedValue) { - size = strformat( - F("%d [kB] (%d kB free)"), - SpiffsTotalBytes() / 1024, - SpiffsFreeSpace() / 1024); - } - else { - size = (SpiffsTotalBytes() >> 10); - } - KeyValueStruct kv( #ifdef USE_LITTLEFS F("Little FS Size"), @@ -1063,15 +1049,10 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) #endif // ifdef USE_LITTLEFS SpiffsTotalBytes() >> 10); KV_SETID(F("fs_size")); + #if FEATURE_TASKVALUE_UNIT_OF_MEASURE KV_SETUNIT(UOM_kB); #endif - - if (!extendedValue) { -#if FEATURE_TASKVALUE_UNIT_OF_MEASURE - KV_SETUNIT(UOM_kB); -#endif - } return kv; } case LabelType::FS_FREE: @@ -1397,10 +1378,6 @@ String getFormNote(LabelType::Enum label) break; #endif - case LabelType::WIFI_START_AP_ON_NW002_INIT: - flash_str = F("Always start AP mode when this network interface is enabled"); - break; - #endif // ifndef MINIMAL_OTA #if FEATURE_SET_WIFI_TX_PWR diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 6f89c5199..1594f63c9 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -36,7 +36,6 @@ struct LabelType { WIFI_ENABLE_CAPTIVE_PORTAL, WIFI_START_AP_NO_CREDENTIALS, WIFI_START_AP_ON_CONNECT_FAIL, - WIFI_START_AP_ON_NW002_INIT, WIFI_NR_RECONNECT_ATTEMPTS, WIFI_MAX_UPTIME_AUTO_START_AP, WIFI_AP_MINIMAL_ON_TIME, diff --git a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp index cd4a3a76d..99b3a548b 100644 --- a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp +++ b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp @@ -239,7 +239,9 @@ String serializeDomoticzJson(struct EventStruct *event) } } else { writer.write({ F("nvalue"), F("0") }); - writer.write({ F("svalue"), formatDomoticzSensorType(event) }); + writer.write({ F("svalue"), + formatDomoticzSensorType(event), + KeyValueStruct::Format::PreFormatted }); } } diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 7cd4b459b..8e103aeda 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -12,6 +12,8 @@ #include "../WebServer/ESPEasy_WebServer.h" #include "../WebServer/Markup.h" #include "../WebServer/Markup_Forms.h" +#include "../ESPEasyCore/Controller.h" +#include "../DataStructs/ControllerSettingsStruct.h" /*********************************************************************************************\ * Functions to load and store controller settings on the web page. @@ -139,7 +141,7 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } -#if FEATURE_MQTT_TLS +#if FEATURE_MQTT_TLS || FEATURE_HTTP_TLS void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description, TLS_types tls_type) { const String certFile = ControllerSettings.getCertificateFilename(tls_type); diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index 814af8073..5ddee65f9 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -2,29 +2,1497 @@ #ifdef USES_P039 -/* - P039_data_struct::P039_data_struct( - uint16_t l_conversionResult, - uint8_t l_devicefaults, - unsigned long l_timer, - bool l_sensorFault, - bool l_convReady) - : conversionResult(l_conversionResult), deviceFaults(l_devicefaults), timer(l_timer), sensorFault(l_sensorFault), convReady(l_convReady) - {} +P039_data_struct::P039_data_struct(struct EventStruct*event) { + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + _spi = 0 == spi_bus ? SPI : SPIe; + # endif // ifdef ESP32 +} + +bool P039_data_struct::begin(struct EventStruct*event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // set the slaveSelectPin as an output: + init_SPI_CS_Pin(CS_pin_no); + + // ensure MODE3 access to SPI device + _spi.setDataMode(SPI_MODE3); + + if (P039_MAX_TYPE == P039_MAX31855) { + sensorFault = false; + } + + + if (P039_MAX_TYPE == P039_MAX31856) { + // init string - content accoring to inital implementation of P039 - MAX31856 read function + // write to Adress 0x80 + // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to + // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO + uint8_t sendBuffer[11] = + { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, + 0xFF, 0x80, 0x00, 0x00 }; + + transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); + + sensorFault = false; + + // start on shot conversion for upcoming read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + } + + + if (P039_MAX_TYPE == P039_MAX31865) { + // two step initialization buffer + uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + + // write intially 0x00 to CONFIG register + write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); + + // activate 50Hz filter, clear all faults, no auto conversion, no conversion started + change8BitRegister(CS_pin_no, + MAX31865_RD_ADDRESS(MAX31865_CONFIG), + MAX31865_WR_ADDRESS(MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // configure 2/4-wire sensor connection as default + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // set HighFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); + + // set LowFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); + + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // save current timer for next calculation + timer = millis(); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + } + + /* + if (P039_MAX_TYPE == P039_LM7x) + { + // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 + } + */ + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + return success; +} + +bool P039_data_struct::read(EventStruct *event) { + bool success = false; + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + float Plugin_039_Celsius = NAN; + + switch (MaxType) { + case P039_MAX6675: + Plugin_039_Celsius = readMax6675(event); + break; + case P039_MAX31855: + Plugin_039_Celsius = readMax31855(event); + break; + case P039_MAX31856: + Plugin_039_Celsius = readMax31856(event); + break; + case P039_MAX31865: + Plugin_039_Celsius = readMax31865(event); + break; + case P039_LM7x: + Plugin_039_Celsius = readLM7x(event); + break; + } + + if (isValidFloat(Plugin_039_Celsius)) + { + UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); + + const uint8_t valueCount = getValueCountForTask(event->TaskIndex); + + for (uint8_t i = 0; i < valueCount; ++i) + { + log += strformat( + F(" %s: %s"), + Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), + formatUserVarNoCheck(event, i).c_str()); + } + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // ifndef BUILD_NO_DEBUG + + if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { + success = true; + } + } + else + { + UserVar.setFloat(event->TaskIndex, 0, NAN); + UserVar.setFloat(event->TaskIndex, 1, NAN); + + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); + } + success = false; + } + return success; +} + +bool P039_data_struct::plugin_tasktimer_in(EventStruct *event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + switch (MaxType) + { + case P039_MAX31865: + { + switch (event->Par1) + { + case MAX31865_BIAS_ON_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // activate one shot conversion + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_ONE_SHOT, + P039_SET); + + // set next state in sequence -> READ STATE + // start time to follow up on conversion and read the conversion result + convReady = false; + Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + break; + } + case MAX31865_RD_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // read conversion result + conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); + + // deactivate BIAS short after read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_RESET); + + // read fault register to get a full picture + deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); + + // mark conversion as ready + convReady = true; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, + strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + formatToHex_decimal(conversionResult).c_str(), + formatToHex_decimal(deviceFaults).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + + break; + } + case MAX31865_INIT_STATE: + default: + { + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " + "default; next state: MAX31865_BIAS_ON_STATE"), + getTaskDeviceName(event->TaskIndex).c_str())); + + // FIXME: Shouldn't this be in active code? // save current timer for next calculation + timer = millis(); + } + # endif // ifndef BUILD_NO_DEBUG + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // set next state in sequence -> BIAS ON STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + + break; + } + } + break; + } + default: + { + break; + } + } + return success; +} + +void P039_data_struct::AddMainsFrequencyFilterSelection(struct EventStruct *event) +{ + const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; + const int FToptionValues[] = { 0, 1 }; + + const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); + + selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); + addUnit(F("Hz")); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Filter power net frequency (50/60 Hz)")); + # else // ifndef LIMIT_BUILD_SIZE + addUnit(F("net frequency")); + # endif // ifndef LIMIT_BUILD_SIZE +} + +float P039_data_struct::readMax6675(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + uint8_t messageBuffer[2] = { 0 }; + uint16_t rawvalue = 0u; + + + // "transfer" 2 bytes to SPI to get 16 Bit return value + transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); + + // merge 16Bit return value from messageBuffer + rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue, + formatToHex_decimal(messageBuffer[0]).c_str(), + formatToHex_decimal(messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + // Open Thermocouple + // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the + // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin + // as possible. + // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance + const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); + + if (Plugin_039_SensorAttached) + { + // shift RAW value 3 Bits to the right to get the data + rawvalue >>= 3; + + // calculate Celsius with device resolution 0.25 K/bit + return rawvalue * 0.25f; + } + else + { + return NAN; + } +} + +float P039_data_struct::readMax31855(struct EventStruct *event) +{ + uint8_t messageBuffer[4] = { 0 }; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // "transfer" 0x0 and read the 32 Bit conversion register from the Chip + transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); + + // merge rawvalue from 4 bytes of messageBuffer + uint32_t rawvalue = + ((static_cast(messageBuffer[0]) << + 24) | + (static_cast(messageBuffer[1]) << + 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(200u)) { // reserve value derived from example log file + log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue); + + for (size_t i = 0u; i < 4; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(messageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // check for fault flags in LSB of 32 Bit messageBuffer + if (sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { + // Fault code changed, log them + sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : MAX31855 : "); + + if (sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault code :"); + + if (rawvalue & MAX31855_TC_OC) { + log += F(" Open (no connection)"); + } + + if (rawvalue & MAX31855_TC_SC) { + log += F(" Short-circuit to GND"); + } + + if (rawvalue & MAX31855_TC_SCVCC) { + log += F(" Short-circuit to Vcc"); + } + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + # endif // ifndef BUILD_NO_DEBUG + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s sensorFault: "), + formatToHex_decimal(rawvalue).c_str(), + formatToHex_decimal(sensorFault).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + } + + // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. + // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons + const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); + + if (Plugin_039_SensorAttached) + { + // Data is D[31:18] + // Shift RAW value 18 Bits to the right to get the data + rawvalue >>= 18; + + // Check for negative Values + // +25.00 0000 0001 1001 00 + // 0.00 0000 0000 0000 00 + // -0.25 1111 1111 1111 11 + // -1.00 1111 1111 1111 00 + // -250.00 1111 0000 0110 00 + // We're left with (32 - 18 =) 14 bits + int temperature = convert_two_complement(rawvalue, 14); + + // Calculate Celsius + return temperature * 0.25f; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31856(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + + uint8_t registers[MAX31856_NO_REG] = { 0 }; + uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; + + messageBuffer[0] = MAX31856_READ_ADDR_BASE; + + // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip + transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); + + // transfer data from messageBuffer and get rid of initial address uint8_t + for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { + registers[i] = messageBuffer[i + 1]; + } + + // configure device for next conversion + // activate frequency filter according to configuration + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // set averaging and TC type + write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); + + + // start on shot conversion for next read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + + + // now derive raw value from respective registers + uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); + + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 :"); + + for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + log += F(" rawvalue: "); + log += formatToHex_decimal(rawvalue); + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // ignore TC Range Bit in case Voltage Modes are used + // datasheet: + // Thermocouple Out-of-Range fault. + // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). + // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. + // Note: The TC Range bit should be ignored in voltage mode. + uint8_t sr = registers[MAX31856_SR]; + + if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { + sr &= ~MAX31856_TC_TCRANGE; + } + + + // FIXED: c.k.i. : moved static fault flag to instance data structure + + sensorFault = (sr != 0); // Set new state + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + // FIXME TD-er: Part of expression is always false (sr == 0) + const bool faultResolved = sensorFault && (sr == 0); + + if (sensorFault || faultResolved) { + String log; + + if (log.reserve(140u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 : "); + + if (!sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault :"); + + if (sr & MAX31856_TC_OC) { + log += F(" Open (no connection)"); + } + + if (sr & MAX31856_TC_OVUV) { + log += F(" Over/Under Voltage"); + } + + if (sr & MAX31856_TC_TCLOW) { + log += F(" TC Low"); + } + + if (sr & MAX31856_TC_TCLHIGH) { + log += F(" TC High"); + } + + if (sr & MAX31856_TC_CJLOW) { + log += F(" CJ Low"); + } + + if (sr & MAX31856_TC_CJHIGH) { + log += F(" CJ High"); + } + + if (sr & MAX31856_TC_TCRANGE) { + log += F(" TC Range"); + } + + if (sr & MAX31856_TC_CJRANGE) { + log += F(" CJ Range"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + const bool Plugin_039_SensorAttached = (sr == 0); + + if (Plugin_039_SensorAttached) + { + rawvalue >>= 5; // bottom 5 bits are unused + // We're left with (24 - 5 =) 19 bits + + { + float temperature = 0; + + switch (P039_TC_TYPE) + { + case 8: + { + temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) + break; + } + case 12: + { + temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) + break; + } + default: + { + temperature = convert_two_complement(rawvalue, 19); + + // Calculate Celsius + temperature /= 128.0f; + break; + } + } + + return temperature; + } + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31865(struct EventStruct *event) +{ + uint8_t registers[MAX31865_NO_REG] = { 0 }; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(80u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 :"); + log += F(" P039_data->convReady: "); + log += boolToString(convReady); + + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // read conversion result and faults from plugin data structure + // if pointer exists and conversion has been finished + if (convReady) { + rawValue = conversionResult; + registers[MAX31865_FAULT] = deviceFaults; + } + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(160u)) { // reserve value derived from example log file + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); + } + + log = F("P039 : MAX31865 :"); + + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // Prepare and start next conversion, before handling faults and rawValue + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // set frequency filter + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + + // configure read access with configuration from web interface + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // save current timer for next calculation + timer = millis(); + + // set next state to MAX31865_BIAS_ON_STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + if (registers[MAX31865_FAULT]) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 : "); + + log += F("Fault : "); + log += formatToHex_decimal(registers[MAX31865_FAULT]); + log += F(" :"); + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) + { + log += F(" Under/Over voltage"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) + { + log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) + { + log += F(" REFIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) + { + log += F(" REFIN- > 0.85 x Bias"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) + { + log += F(" RTD Low Threshold"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) + { + log += F(" RTD High Threshold"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + bool ValueValid = false; + + if (registers[MAX31865_FAULT] == 0x00u) { + ValueValid = true; + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), + formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), + FsP(boolToString(ValueValid)))); + } + + # endif // ifndef BUILD_NO_DEBUG + + if (ValueValid) + { + rawValue >>= 1; // bottom fault bits is unused + + float temperature = convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), + formatToHex_decimal(rawValue).c_str(), + temperature, + P039_RTD_TYPE, + P039_RTD_RES)); + } + + # endif // ifndef BUILD_NO_DEBUG + + // add offset handling from configuration webpage + temperature += P039_RTD_OFFSET; + + // Calculate Celsius + return temperature; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +void P039_data_struct::MAX31865_clearFaults(int8_t l_CS_pin_no) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); + + + // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) + l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); + l_reg |= MAX31865_CLEAR_FAULTS; + + // write configuration + write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); +} + +void P039_data_struct::MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) +{ + bool l_set_reset = false; + + // configure if 3 WIRE bit will be set/reset + switch (l_conType) + { + case 0: + l_set_reset = P039_RESET; + break; + case 1: + l_set_reset = P039_SET; + break; + default: + l_set_reset = P039_RESET; + break; + } + + // change to configuration register + change8BitRegister(l_CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_3WIRE, + l_set_reset); +} + +/**************************************************************************/ + +/*! + @brief Read the temperature in C from the RTD through calculation of the + resistance. Uses + http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf + technique + @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 + or 1000 + @param refResistor The value of the matching reference resistor, usually + 430 or 4300 + @returns Temperature in C */ -bool P039_data_struct::begin() + +/**************************************************************************/ +float P039_data_struct::convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) { - return false; + # define RTD_A 3.9083e-3f + # define RTD_B -5.775e-7f + + float Z1, Z2, Z3, Z4, Rt, temp; + + Rt = l_rawvalue; + Rt /= 32768u; + Rt *= refResistor; + + Z1 = -RTD_A; + Z2 = RTD_A * RTD_A - (4 * RTD_B); + Z3 = (4 * RTD_B) / RTDnominal; + Z4 = 2 * RTD_B; + + temp = Z2 + (Z3 * Rt); + temp = (sqrtf(temp) + Z1) / Z4; + + if (temp >= 0) { + return temp; + } + + Rt /= RTDnominal; + Rt *= 100; // normalize to 100 ohm + + float rpoly = Rt; + + temp = -242.02f; + temp += 2.2228f * rpoly; + rpoly *= Rt; // square + temp += 2.5859e-3f * rpoly; + rpoly *= Rt; // ^3 + temp -= 4.8260e-6f * rpoly; + rpoly *= Rt; // ^4 + temp -= 2.8183e-8f * rpoly; + rpoly *= Rt; // ^5 + temp += 1.5243e-10f * rpoly; + + return temp; } -bool P039_data_struct::read() +uint16_t P039_data_struct::getNomResistor(uint8_t l_RType) { - return false; + uint16_t l_returnValue = 100u; + + switch (l_RType) + { + case MAX31865_PT1000: + l_returnValue = 1000u; + break; + case MAX31865_PT100: // Fall through + default: + break; + } + return l_returnValue; } -bool P039_data_struct::write() +int P039_data_struct::convert_two_complement(uint32_t value, int nr_bits) { + const bool negative = (value & (1 << (nr_bits - 1))) != 0; + int nativeInt; + + if (negative) { + // Add zeroes to the left to create the proper negative native-sized integer. + nativeInt = value | ~((1 << nr_bits) - 1); + } else { + nativeInt = value; + } + return nativeInt; +} + +float P039_data_struct::readLM7x(struct EventStruct *event) { - return false; + float temperature = 0.0f; + uint16_t device_id = 0u; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) + // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register + + rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); + + temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), + formatToHex_decimal(rawValue).c_str(), + formatToHex(device_id).c_str(), + temperature)); + } + # endif // ifndef BUILD_NO_DEBUG + + return temperature; +} + +float P039_data_struct::convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) +{ + float l_returnValue = 0.0f; + float l_lsbvalue = 0.0f; + uint8_t l_noBits = 0u; + int l_intTemperature = 0; + + switch (l_LM7xsubtype) + { + case LM7x_SD70: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + case LM7x_SD71: + l_rawValue >>= 2; + l_lsbvalue = 0.03125f; + l_noBits = 14u; + break; + case LM7x_SD74: + l_rawValue >>= 3; + l_lsbvalue = 0.0625f; + l_noBits = 13u; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_rawValue >>= 4; + l_lsbvalue = 0.0625f; + l_noBits = 12u; + break; + case LM7x_SD125: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 10u; + break; + default: // use lowest resolution as fallback if no device has been configured + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + } + + l_intTemperature = convert_two_complement(l_rawValue, l_noBits); + + l_returnValue = l_intTemperature * l_lsbvalue; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, + strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex_decimal(l_LM7xsubtype).c_str(), + formatToHex_decimal(l_rawValue).c_str(), + l_noBits, + l_lsbvalue)); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) +{ + uint16_t l_returnValue = 0u; + uint16_t l_mswaitTime = 0u; + + + switch (l_LM7xsubType) + { + case LM7x_SD70: + case LM7x_SD71: + case LM7x_SD74: + l_mswaitTime = 300; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_mswaitTime = 320; + break; + case LM7x_SD125: + l_mswaitTime = 100; + break; + default: + l_mswaitTime = 500; + break; + } + + // // activate communication -> CS low + // handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + if (l_runMode) + { + // shutdown mode active -> conversion when called + uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; + + // send inital 4 bytes to wake the device and start the conversion + transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); + + // wait specific ms for conversion to be ready (TI datasheet per devices) + delay(l_mswaitTime); + + // send remaining 8 bytes to read the device ID and shutdown the device + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); + } + else + { + // shutdown mode inactive -> normal background conversion during call cycle + uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex(*(l_device_id)).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE + +/**************************************************************************/ + +/*! + @brief generic high level library to access SPI interface from plugins + with GPIO pin handled as CS - chri.kai.in 2021 + + Initial Revision - chri.kai.in 2021 + + TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library + + + **************************************************************************/ + + +/**************************************************************************/ + +/*! + + @brief Identifying the CS pin from the event basic data structure + @param event pointer to the event structure; default GPIO is chosen as GPIO 15 + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +int P039_data_struct::get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on + // ESP8266 + if (CONFIG_PIN1 != -1) { + return CONFIG_PIN1; + } + return 15; // D8 +} + +/**************************************************************************/ + +/*! + @brief Initializing GPIO as OUTPUT for CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::init_SPI_CS_Pin(int8_t l_CS_pin_no) { + // set the slaveSelectPin as an output: + pinMode(l_CS_pin_no, OUTPUT); +} + +/**************************************************************************/ + +/*! + @brief Handling GPIO as CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { + P039_CS_Delay(); // tCWH (min) >= x00ns + digitalWrite(l_CS_pin_no, l_state); + P039_CS_Delay(); // tCC (min) >= x00ns +} + +/**************************************************************************/ + +/*! + @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 8 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) +{ + uint8_t l_messageBuffer[2] = { l_address, value }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 16 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) +{ + uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 8 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +uint8_t P039_data_struct::read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[2] = { l_address, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_messageBuffer[1]; +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 16 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +uint16_t P039_data_struct::read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; + uint16_t l_returnValue; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_returnValue).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +/**************************************************************************/ + +/*! + @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_noBytesToSend number of bytes to read/write from/to SPI + @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send + and provide read bytes from the SPI bus after the call + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) +{ + // activate communication -> CS low + handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + for (size_t i = 0u; i < l_noBytesToSend; i++) + { + l_inoutMessageBuffer[i] = _spi.transfer(l_inoutMessageBuffer[i]); + } + + // stop communication -> CS high + handle_SPI_CS_Pin(l_CS_pin_no, HIGH); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char + + for (uint8_t i = 0; i < l_noBytesToSend; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset) +{ + uint16_t l_reg = 0u; + + // read in config register + l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); + + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); +} + +/**************************************************************************/ + +/*! + @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + **************************************************************************/ +void P039_data_struct::change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); + + + // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); } #endif // ifdef USES_P039 diff --git a/src/src/PluginStructs/P039_data_struct.h b/src/src/PluginStructs/P039_data_struct.h index 92305b068..836cb7ba6 100644 --- a/src/src/PluginStructs/P039_data_struct.h +++ b/src/src/PluginStructs/P039_data_struct.h @@ -5,36 +5,251 @@ #ifdef USES_P039 +# include + +# include "../Globals/SPIe.h" + +# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) +# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) + +# define P039_SET true +# define P039_RESET false + +// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we +// spend 1ms +// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device +# define P039_CS_Delay() delayMicroseconds(2u) + +# define P039_MAX_TYPE PCONFIG(0) +# define P039_TC_TYPE PCONFIG(1) +# define P039_FAM_TYPE PCONFIG(2) +# define P039_RTD_TYPE PCONFIG(3) +# define P039_CONFIG_4 PCONFIG(4) +# define P039_RTD_FILT_TYPE PCONFIG(5) +# define P039_RTD_LM_TYPE PCONFIG(6) +# define P039_RTD_LM_SHTDWN PCONFIG(7) +# define P039_RTD_RES PCONFIG_LONG(0) +# define P039_FLAGS PCONFIG_ULONG(3) +# define P039_TEMP_THRESHOLD_FLAG 0 +# define P039_RTD_OFFSET PCONFIG_FLOAT(0) +# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) + +# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value +# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT +# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value +# define P039_TC 0u +# define P039_RTD 1u + +# define P039_MAX6675 1 +# define P039_MAX31855 2 +# define P039_MAX31856 3 +# define P039_MAX31865 4 +# define P039_LM7x 5 + +// MAX 6675 related defines + +// bit masks to identify failures for MAX 6675 +# define MAX6675_TC_DEVID 0x0002u +# define MAX6675_TC_OC 0x0004u + +// MAX 31855 related defines + +// bit masks to identify failures for MAX 31855 +# define MAX31855_TC_OC 0x00000001u +# define MAX31855_TC_SC 0x00000002u +# define MAX31855_TC_SCVCC 0x00000004u +# define MAX31855_TC_GENFLT 0x00010000u + + +// MAX 31856 related defines + +// base address for read/write acces to MAX 31856 +# define MAX31856_READ_ADDR_BASE 0x00u +# define MAX31856_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31856 +# define MAX31856_CR0 0u +# define MAX31856_CR1 1u +# define MAX31856_MASK 2u +# define MAX31856_CJHF 3u +# define MAX31856_CJLF 4u +# define MAX31856_LTHFTH 5u +# define MAX31856_LTHFTL 6u +# define MAX31856_LTLFTH 7u +# define MAX31856_LTLFTL 8u +# define MAX31856_CJTO 9u +# define MAX31856_CJTH 10u +# define MAX31856_CJTL 11u +# define MAX31856_LTCBH 12u +# define MAX31856_LTCBM 13u +# define MAX31856_LTCBL 14u +# define MAX31856_SR 15u + +# define MAX31856_NO_REG 16u + +// bit masks to identify failures for MAX 31856 +# define MAX31856_TC_OC 0x01u +# define MAX31856_TC_OVUV 0x02u +# define MAX31856_TC_TCLOW 0x04u +# define MAX31856_TC_TCLHIGH 0x08u +# define MAX31856_TC_CJLOW 0x10u +# define MAX31856_TC_CJHIGH 0x20u +# define MAX31856_TC_TCRANGE 0x40u +# define MAX31856_TC_CJRANGE 0x80u + +// bit masks for access of configuration bits +# define MAX31856_SET_50HZ 0x01u +# define MAX31856_CLEAR_FAULTS 0x02u +# define MAX31856_FLT_ISR_MODE 0x04u +# define MAX31856_CJ_SENS_DISABLE 0x08u +# define MAX31856_FAULT_CTRL_MASK 0x30u +# define MAX31856_SET_ONE_SHOT 0x40u +# define MAX31856_SET_CONV_AUTO 0x80u + + +// RTD related defines + +// MAX 31865 related defines + +// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) +// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done +// alternatively ONE SHOT bit could be polled (system/SPI bus load !) +# define MAX31865_CONVERSION_TIME 70ul +# define MAX31865_BIAS_WAIT_TIME 10ul + +// MAX 31865 Main States +# define MAX31865_INIT_STATE 0u +# define MAX31865_BIAS_ON_STATE 1u +# define MAX31865_RD_STATE 2u +# define MAX31865_RDY_STATE 3u + +// sensor type +# define MAX31865_PT100 0u +# define MAX31865_PT1000 1u + +// base address for read/write acces to MAX 31865 +# define MAX31865_READ_ADDR_BASE 0x00u +# define MAX31865_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31865 +# define MAX31865_CONFIG 0u +# define MAX31865_RTD_MSB 1u +# define MAX31865_RTD_LSB 2u +# define MAX31865_HFT_MSB 3u +# define MAX31865_HFT_LSB 4u +# define MAX31865_LFT_MSB 5u +# define MAX31865_LFT_LSB 6u +# define MAX31865_FAULT 7u + +// total number of registers in MAX 31865 +# define MAX31865_NO_REG 8u + +// bit masks to identify failures for MAX 31865 +# define MAX31865_FAULT_HIGHTHRESH 0x80u +# define MAX31865_FAULT_LOWTHRESH 0x40u +# define MAX31865_FAULT_REFINLOW 0x20u +# define MAX31865_FAULT_REFINHIGH 0x10u +# define MAX31865_FAULT_RTDINLOW 0x08u +# define MAX31865_FAULT_OVUV 0x04u + +// bit masks for access of configuration bits +# define MAX31865_SET_50HZ 0x01u +# define MAX31865_CLEAR_FAULTS 0x02u +# define MAX31865_FAULT_CTRL_MASK 0x0Cu +# define MAX31865_SET_3WIRE 0x10u +# define MAX31865_SET_ONE_SHOT 0x20u +# define MAX31865_SET_CONV_AUTO 0x40u +# define MAX31865_SET_VBIAS_ON 0x80u + +// LM7x related defines + +// LM7x subtype defines +# define LM7x_SD70 0x00u +# define LM7x_SD71 0x01u +# define LM7x_SD74 0x04u +# define LM7x_SD121 0x05u +# define LM7x_SD122 0x06u +# define LM7x_SD123 0x07u +# define LM7x_SD124 0x08u +# define LM7x_SD125 0x09u + +// bit masks for access of configuration bits +# define LM7x_CONV_RDY 0x02u + struct P039_data_struct : public PluginTaskData_base { public: - /* - P039_data_struct(uint16_t conversionResult, - uint8_t deviceFaults, - unsigned long timer, - bool sensorFault, - bool convReady); - */ - - P039_data_struct() = default; + P039_data_struct() = delete; + P039_data_struct(struct EventStruct *event); virtual ~P039_data_struct() = default; - bool begin(); + bool begin(struct EventStruct*event); // Perform read and return true when an alert has been high - bool read(); + bool read(struct EventStruct *event); - // Perform write and return true when an alert has been high - bool write(); + bool plugin_tasktimer_in(struct EventStruct*event); + + static void AddMainsFrequencyFilterSelection(struct EventStruct *event); + +private: + + float readMax6675(struct EventStruct *event); + float readMax31855(struct EventStruct *event); + float readMax31856(struct EventStruct *event); + float readMax31865(struct EventStruct *event); + void MAX31865_clearFaults(int8_t l_CS_pin_no); + void MAX31865_setConType(int8_t l_CS_pin_no, + uint8_t l_conType); + float convert_to_temperature(uint32_t l_rawvalue, + float RTDnominal, + float refResistor); + uint16_t getNomResistor(uint8_t l_RType); + int convert_two_complement(uint32_t value, + int nr_bits); + float readLM7x(struct EventStruct *event); + float convertLM7xTemp(uint16_t l_rawValue, + uint16_t l_LM7xsubtype); + uint16_t readLM7xRegisters(int8_t l_CS_pin_no, + uint8_t l_LM7xsubType, + uint8_t l_runMode, + uint16_t *l_device_id); + int get_SPI_CS_Pin(struct EventStruct *event); + void init_SPI_CS_Pin(int8_t l_CS_pin_no); + void handle_SPI_CS_Pin(int8_t l_CS_pin_no, + bool l_state); + void write8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint8_t value); + void write16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint16_t value); + uint8_t read8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + uint16_t read16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + void transfer_n_ByteSPI(int8_t l_CS_pin_no, + uint8_t l_noBytesToSend, + uint8_t *l_inoutMessageBuffer); + void change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset); + void change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset); - // uint8_t mainState = 0x00u;; - // uint8_t command = 0x00u; uint16_t conversionResult = 0x0000u; uint8_t deviceFaults = 0x00u; unsigned long timer = 0; bool sensorFault = false; bool convReady = false; + + SPIClass& _spi = SPI; }; diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 3d1fe2586..4eab20b56 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -2,7 +2,7 @@ #ifdef USES_P073 -# include +#include uint8_t P073_getDefaultDigits(uint8_t displayModel, uint8_t digits) { @@ -34,7 +34,8 @@ uint8_t P073_mapCharToFontPosition(char character, const String specialChars = F(" -^=/_%@.,;:+*#!?'\"<>\\()|"); const String chnorux = F("CHNORUX"); - switch (fontset) { + switch (fontset) + { case 1: // Siekoo case 2: // Siekoo with uppercase 'CHNORUX' @@ -58,16 +59,24 @@ uint8_t P073_mapCharToFontPosition(char character, if (isDigit(character)) { position = character - '0'; - } else if (isAlpha(character)) { + } + else if (isAlpha(character)) { position = character - (isLowerCase(character) ? 'a' : 'A') + 16; } else { - switch (character) { - case ' ': position = 10; break; - case '-': position = 11; break; - case '^': position = 12; break; // degree - case '=': position = 13; break; - case '/': position = 14; break; - case '_': position = 15; break; + switch (character) + { + case ' ': position = 10; + break; + case '-': position = 11; + break; + case '^': position = 12; + break; // degree + case '=': position = 13; + break; + case '/': position = 14; + break; + case '_': position = 15; + break; } } # if P073_EXTRA_FONTS @@ -85,7 +94,8 @@ uint8_t P073_getFontChar(uint8_t index, uint8_t fontset) { # if P073_EXTRA_FONTS - switch (fontset) { + switch (fontset) + { case 1: // Siekoo case 2: // Siekoo uppercase CHNORUX return pgm_read_byte(&(SiekooCharTable[index])); @@ -143,10 +153,12 @@ void P073_display_output_selector(const __FlashStringHelper *id, int16_t value) P073_DISP_DATE, }; const FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + selector.addFormSelector(F("Display Output"), id, value); } # if P073_EXTRA_FONTS + void P073_font_selector(const __FlashStringHelper *id, int16_t value) { const __FlashStringHelper *fontset[] = { F("Default"), @@ -154,7 +166,8 @@ void P073_font_selector(const __FlashStringHelper *id, int16_t value) { F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7"), }; - const FormSelectorOptions selector(NR_ELEMENTS(fontset), fontset); + const FormSelectorOptions selector(NR_ELEMENTS(fontset), fontset); + selector.addFormSelector(F("Font set"), id, value); addFormNote(F("Check documentation for examples of the font sets.")); } @@ -202,9 +215,9 @@ void P073_data_struct::init(struct EventStruct *event) # endif // if P073_SCROLL_TEXT rightAlignTempMAX7219 = bitRead(P073_CFG_FLAGS, P073_OPTION_RIGHTALIGN); suppressLeading0 = bitRead(P073_CFG_FLAGS, P073_OPTION_SUPPRESS0); - #if P073_BLINK_DOT + # if P073_BLINK_DOT blinkdot = bitRead(P073_CFG_FLAGS, P073_OPTION_BLINK_DOT); - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT timesep = true; # if P073_EXTRA_FONTS fontset = P073_CFG_FONTSET; @@ -233,11 +246,20 @@ void P073_data_struct::init(struct EventStruct *event) } # endif // if P073_USE_74HC595 + if (0 == digits) { + digits = P073_getDefaultDigits(P073_CFG_DISPLAYTYPE); + } + if ((digits > 0) && (digits < 4)) { hideDegree = true; // Hide degree symbol on small displays } - switch (displayModel) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P073 : Digits: %d, model: %d, pins: %d, %d, %d"), digits, displayModel, pin1, pin2, pin3)); + } + + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -275,6 +297,7 @@ void P073_data_struct::init(struct EventStruct *event) } # if P073_USE_74HC595 + bool P073_data_struct::plugin_fifty_per_second(struct EventStruct *event) { # ifdef P073_DEBUG counter50++; @@ -289,9 +312,7 @@ bool P073_data_struct::plugin_fifty_per_second(struct EventStruct *event) { return false; } -bool P073_data_struct::is74HC595Matrix() { - return P073_74HC595_2_8DGT == displayModel && P073_HC595_MULTIPLEX; -} +bool P073_data_struct::is74HC595Matrix() { return P073_74HC595_2_8DGT == displayModel && P073_HC595_MULTIPLEX; } // ==================================== // ---- 74HC595 specific functions ---- @@ -642,6 +663,7 @@ void P073_data_struct::FillBufferWithString(const String& textToShow, } # if P073_SCROLL_TEXT + int P073_data_struct::getEffectiveTextLength(const String& text) { const int textLength = text.length(); int p = 0; @@ -757,17 +779,14 @@ void P073_data_struct::setScrollSpeed(uint8_t speed) { scrollPos = 0; } -bool P073_data_struct::isScrollEnabled() { - return txtScrolling && scrollAllowed; -} +bool P073_data_struct::isScrollEnabled() { return txtScrolling && scrollAllowed; } -void P073_data_struct::setScrollEnabled(bool scroll) { - scrollAllowed = scroll; -} +void P073_data_struct::setScrollEnabled(bool scroll) { scrollAllowed = scroll; } # endif // if P073_SCROLL_TEXT # if P073_7DBIN_COMMAND + void P073_data_struct::setBinaryData(const String& data) { binaryData = true; # if P073_SCROLL_TEXT @@ -783,6 +802,7 @@ void P073_data_struct::setBinaryData(const String& data) { # endif // if P073_7DBIN_COMMAND # ifdef P073_DEBUG + void P073_data_struct::LogBufferContent(String prefix) { String log; @@ -803,9 +823,7 @@ void P073_data_struct::LogBufferContent(String prefix) { # endif // ifdef P073_DEBUG // in case of error show all dashes -void P073_data_struct::FillBufferWithDash() { - memset(showbuffer, 11, sizeof(showbuffer)); -} +void P073_data_struct::FillBufferWithDash() { memset(showbuffer, 11, sizeof(showbuffer)); } void P073_data_struct::ClearBuffer() { memset(showbuffer, @@ -820,11 +838,9 @@ void P073_data_struct::ClearBuffer() { } uint8_t P073_data_struct::tm1637_getFontChar(uint8_t index, - uint8_t fontset) { - return P073_revert7bits(P073_getFontChar(index, fontset)); -} + uint8_t fontset) { return P073_revert7bits(P073_getFontChar(index, fontset)); } -bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { +bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { if (output == P073_DISP_MANUAL) { return false; } @@ -855,16 +871,17 @@ bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { ); } - #if P073_BLINK_DOT + # if P073_BLINK_DOT if (blinkdot && ((output == P073_DISP_CLOCK24BLNK) || (output == P073_DISP_CLOCK12BLNK))) { showperiods[1] = timesep; // Blink dot on second digit } - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -900,15 +917,18 @@ bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { } # if P073_SCROLL_TEXT + bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { if ((output != P073_DISP_MANUAL) || !isScrollEnabled()) { return false; } if (NextScroll()) { - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: - case P073_TM1637_4DGTDOTS: { + case P073_TM1637_4DGTDOTS: + { tm1637_ShowBuffer(0, 4 # if P073_7DBIN_COMMAND , binaryData @@ -916,7 +936,8 @@ bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { ); break; } - case P073_TM1637_6DGT: { + case P073_TM1637_6DGT: + { tm1637_SwapDigitInBuffer(0); // only needed for 6-digits displays tm1637_ShowBuffer(0, 6 # if P073_7DBIN_COMMAND @@ -925,13 +946,15 @@ bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { ); break; } - case P073_MAX7219_8DGT: { + case P073_MAX7219_8DGT: + { dotpos = -1; // avoid to display the dot max7219_ShowBuffer(); break; } # if P073_USE_74HC595 - case P073_74HC595_2_8DGT: { + case P073_74HC595_2_8DGT: + { hc595_ToOutputBuffer(); if (hc595_Sequential()) { // Sequential displays don't need continuous refreshing @@ -981,6 +1004,7 @@ enum class p073_commands_e : int8_t { c7doff, c7db, c7output, + }; bool P073_data_struct::plugin_write(struct EventStruct *event, @@ -1005,7 +1029,8 @@ bool P073_data_struct::plugin_write(struct EventStruct *event, bool success = false; bool displayon = false; - switch (cmd) { + switch (cmd) + { case p073_commands_e::c7dn: return plugin_write_7dn(event, text); case p073_commands_e::c7dt: @@ -1104,7 +1129,8 @@ bool P073_data_struct::plugin_write(struct EventStruct *event, setScrollEnabled(newScroll); # endif // if P073_SCROLL_TEXT - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1167,7 +1193,8 @@ bool P073_data_struct::plugin_write_7dn(struct EventStruct *event, } } - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(TM1637_4DIGIT, 8); @@ -1234,7 +1261,8 @@ bool P073_data_struct::plugin_write_7dt(const String& text) { FillBufferWithTemp(p073_temptemp); } - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1277,6 +1305,7 @@ bool P073_data_struct::plugin_write_7dt(const String& text) { } # if P073_7DDT_COMMAND + bool P073_data_struct::plugin_write_7ddt(const String& text) { if (output != P073_DISP_MANUAL) { return false; @@ -1301,7 +1330,8 @@ bool P073_data_struct::plugin_write_7ddt(const String& text) { } # endif // ifndef BUILD_NO_DEBUG - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1403,7 +1433,8 @@ bool P073_data_struct::plugin_write_7dst(struct EventStruct *event) { # endif // if P073_SUPPRESS_ZERO ); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -1447,7 +1478,8 @@ bool P073_data_struct::plugin_write_7dsd(struct EventStruct *event) { # endif // if P073_SUPPRESS_ZERO ); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -1494,7 +1526,8 @@ bool P073_data_struct::plugin_write_7dtext(const String& text) { { FillBufferWithString(text); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(0, 4); @@ -1522,6 +1555,7 @@ bool P073_data_struct::plugin_write_7dtext(const String& text) { } # if P073_EXTRA_FONTS + bool P073_data_struct::plugin_write_7dfont(struct EventStruct *event, const String & text) { if (!text.isEmpty()) { @@ -1545,6 +1579,7 @@ bool P073_data_struct::plugin_write_7dfont(struct EventStruct *event, # endif // if P073_EXTRA_FONTS # if P073_7DBIN_COMMAND + bool P073_data_struct::plugin_write_7dbin(const String& text) { if (!text.isEmpty()) { String data; @@ -1576,7 +1611,8 @@ bool P073_data_struct::plugin_write_7dbin(const String& text) { { FillBufferWithString(data, true); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(0, 4); @@ -1612,53 +1648,64 @@ bool P073_data_struct::plugin_write_7dbin(const String& text) { // ---- TM1637 specific functions ---- // =================================== -# define CLK_HIGH() DIRECT_pinWrite(this->pin1, HIGH) -# define CLK_LOW() DIRECT_pinWrite(this->pin1, LOW) -# define DIO_HIGH() DIRECT_PINMODE_INPUT(this->pin2) -# define DIO_LOW() DIRECT_PINMODE_OUTPUT(this->pin2) //; DIRECT_pinWrite(this->pin2, LOW) +# ifdef ESP32 +# define CLK_HIGH() DIRECT_pinWrite(this->pin1, HIGH) +# define CLK_LOW() DIRECT_pinWrite(this->pin1, LOW) +# define DIO_HIGH() DIRECT_pinWrite(this->pin2, HIGH) +# define DIO_LOW() DIRECT_PINMODE_OUTPUT(this->pin2); DIRECT_pinWrite(this->pin2, LOW) +# define DIO_INPUT() DIRECT_PINMODE_INPUT(this->pin2) +# define DIO_OUTPUT() DIRECT_PINMODE_OUTPUT(this->pin2) +# else // ifdef ESP32 +# define CLK_HIGH() pinMode(this->pin1, INPUT_PULLUP) +# define CLK_LOW() pinMode(this->pin1, OUTPUT) +# define DIO_HIGH() pinMode(this->pin2, INPUT_PULLUP) +# define DIO_LOW() pinMode(this->pin2, OUTPUT);digitalWrite(this->pin2, LOW) +# define DIO_INPUT() pinMode(this->pin2, INPUT_PULLUP) +# define DIO_OUTPUT() pinMode(this->pin2, OUTPUT) +# endif // ifdef ESP32 void P073_data_struct::tm1637_i2cStart() { # ifdef P073_DEBUG addLog(LOG_LEVEL_DEBUG, F("7DGT : Comm Start")); # endif // ifdef P073_DEBUG - DIO_HIGH(); - CLK_HIGH(); - delayMicroseconds(TM1637_CLOCKDELAY); DIO_LOW(); + delayMicroseconds(TM1637_CLOCKDELAY); } void P073_data_struct::tm1637_i2cStop() { # ifdef P073_DEBUG addLog(LOG_LEVEL_DEBUG, F("7DGT : Comm Stop")); # endif // ifdef P073_DEBUG - CLK_LOW(); - delayMicroseconds(TM1637_CLOCKDELAY); DIO_LOW(); delayMicroseconds(TM1637_CLOCKDELAY); CLK_HIGH(); delayMicroseconds(TM1637_CLOCKDELAY); DIO_HIGH(); + delayMicroseconds(TM1637_CLOCKDELAY); } -void P073_data_struct::tm1637_i2cAck() { +bool P073_data_struct::tm1637_i2cAck() { CLK_LOW(); - pinMode(pin2, INPUT_PULLUP); + DIO_INPUT(); - // DIO_HIGH(); delayMicroseconds(TM1637_CLOCKDELAY); + CLK_HIGH(); + const uint32_t start_wait = micros(); - // while(digitalRead(dio_pin)); - # ifdef P073_DEBUG - const bool dummyAck = - # endif // ifdef P073_DEBUG - DIRECT_pinRead(pin2); + const bool acknowledged = -1 != + DIRECT_measureWaitForPinState_ISR(this->pin2, start_wait, TM1637_CLOCKDELAY, 0); + + const int32_t timePassed = usecPassedSince_fast(start_wait); + if (timePassed < TM1637_CLOCKDELAY) { + delayMicroseconds(TM1637_CLOCKDELAY - timePassed); + } # ifdef P073_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String log = F("7DGT : Comm ACK="); - if (dummyAck == 0) { + if (acknowledged) { log += F("TRUE"); } else { log += F("FALSE"); @@ -1666,29 +1713,33 @@ void P073_data_struct::tm1637_i2cAck() { addLogMove(LOG_LEVEL_DEBUG, log); } # endif // ifdef P073_DEBUG - CLK_HIGH(); - delayMicroseconds(TM1637_CLOCKDELAY); - CLK_LOW(); - DIO_LOW(); + CLK_HIGH(); + + delayMicroseconds(TM1637_CLOCKDELAY); + CLK_LOW(); + delayMicroseconds(TM1637_CLOCKDELAY); + DIO_OUTPUT(); + + return acknowledged; } void P073_data_struct::tm1637_i2cWrite_ack(uint8_t bytesToPrint[], uint8_t length) { - #ifdef P073_DEBUG + # ifdef P073_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, concat(F("7DGT : TM1637 databuffer: 0x"), formatToHex_array(bytesToPrint, length))); } - #endif // ifdef P073_DEBUG + # endif // ifdef P073_DEBUG tm1637_i2cStart(); for (uint8_t i = 0; i < length; ++i) { - tm1637_i2cWrite_ack(bytesToPrint[i]); + tm1637_i2cWriteByte_ack(bytesToPrint[i]); } tm1637_i2cStop(); } -void P073_data_struct::tm1637_i2cWrite_ack(uint8_t bytetoprint) { +void P073_data_struct::tm1637_i2cWriteByte_ack(uint8_t bytetoprint) { tm1637_i2cWrite(bytetoprint); tm1637_i2cAck(); } @@ -1697,17 +1748,17 @@ void P073_data_struct::tm1637_i2cWrite(uint8_t bytetoprint) { # ifdef P073_DEBUG addLog(LOG_LEVEL_DEBUG, F("7DGT : WriteByte")); # endif // ifdef P073_DEBUG - uint8_t i; - for (i = 0; i < 8; ++i) { + for (uint8_t i = 0; i < 8; ++i) { CLK_LOW(); + delayMicroseconds(TM1637_CLOCKDELAY >> 1); if (bytetoprint & 0b00000001) { DIO_HIGH(); } else { DIO_LOW(); } - delayMicroseconds(TM1637_CLOCKDELAY); + delayMicroseconds(TM1637_CLOCKDELAY >> 1); bytetoprint = bytetoprint >> 1; CLK_HIGH(); delayMicroseconds(TM1637_CLOCKDELAY); @@ -1726,27 +1777,28 @@ void P073_data_struct::tm1637_SetPowerBrightness(uint8_t brightlvl, # ifdef P073_DEBUG addLog(LOG_LEVEL_INFO, F("7DGT : Set BRIGHT")); # endif // ifdef P073_DEBUG - uint8_t brightvalue = (brightlvl & 0b111); + brightlvl &= 0b111; if (poweron) { - brightvalue = TM1637_POWER_ON | brightvalue; + brightlvl |= TM1637_POWER_ON; } else { - brightvalue = TM1637_POWER_OFF | brightvalue; + brightlvl |= TM1637_POWER_OFF; } - const uint8_t byteToPrint = brightvalue; - tm1637_i2cWrite_ack(byteToPrint); + uint8_t bytesToPrint[]{brightlvl}; + tm1637_i2cWrite_ack(bytesToPrint, NR_ELEMENTS(bytesToPrint)); } void P073_data_struct::tm1637_InitDisplay() { - DIRECT_PINMODE_OUTPUT(pin1); - DIRECT_PINMODE_OUTPUT(pin2); - CLK_HIGH(); - DIO_HIGH(); + pinMode(this->pin1, OUTPUT); + pinMode(this->pin2, OUTPUT); + + digitalWrite(this->pin1, HIGH); + digitalWrite(this->pin2, HIGH); - const uint8_t byteToPrint = 0x40; - - tm1637_i2cWrite_ack(byteToPrint); + delayMicroseconds(TM1637_CLOCKDELAY); + uint8_t bytesToPrint[]{0x40}; + tm1637_i2cWrite_ack(bytesToPrint, NR_ELEMENTS(bytesToPrint)); tm1637_ClearDisplay(); } diff --git a/src/src/PluginStructs/P073_data_struct.h b/src/src/PluginStructs/P073_data_struct.h index 63770e6d9..97bbbdba0 100644 --- a/src/src/PluginStructs/P073_data_struct.h +++ b/src/src/PluginStructs/P073_data_struct.h @@ -105,7 +105,7 @@ # define TM1637_POWER_ON 0b10001000 # define TM1637_POWER_OFF 0b10000000 -# define TM1637_CLOCKDELAY 40 +# define TM1637_CLOCKDELAY 10 // FIXME TD-er: Maybe lower this as we can get as low as 2 usec to remain below the max 250 kHz # define TM1637_4DIGIT 4 # define TM1637_6DIGIT 2 @@ -308,9 +308,9 @@ public: bool rightAlignTempMAX7219 = false; bool suppressLeading0 = false; uint8_t fontset = 0; - #if P073_BLINK_DOT + # if P073_BLINK_DOT bool blinkdot = false; - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT # if P073_7DBIN_COMMAND bool binaryData = false; # endif // P073_7DBIN_COMMAND @@ -362,10 +362,10 @@ private: // ---- TM1637 specific functions ---- void tm1637_i2cStart(); void tm1637_i2cStop(); - void tm1637_i2cAck(); + bool tm1637_i2cAck(); void tm1637_i2cWrite_ack(uint8_t bytesToPrint[], uint8_t length); - void tm1637_i2cWrite_ack(uint8_t bytetoprint); + void tm1637_i2cWriteByte_ack(uint8_t bytetoprint); void tm1637_i2cWrite(uint8_t bytetoprint); void tm1637_ClearDisplay(); void tm1637_SetPowerBrightness(uint8_t brightlvl, @@ -404,12 +404,12 @@ private: void hc595_ShowBuffer(); void hc595_ToOutputBuffer(); void hc595_AdjustBuffer(); - bool hc595_Sequential() { - return P073_HC595_SEQUENTIAL; - } + + bool hc595_Sequential() { return P073_HC595_SEQUENTIAL; } uint8_t outputbuffer[8]{}; # endif // if P073_USE_74HC595 + }; #endif // ifdef USES_P073 diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index 927b4e36c..5e65da9bb 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -99,7 +99,8 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -108,7 +109,7 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, : _displayType(displayType), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill) + _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -165,7 +166,18 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { } else # endif // if P095_ENABLE_ILI948X { + # ifdef ESP32 // PIN(0) and PIN(1) swapped! + tft = new (std::nothrow) Adafruit_ILI9341(0 == _spi_bus ? &SPI : &SPIe, + PIN(1), + PIN(0), + PIN(2), + static_cast(_displayType), + _xpix, + _ypix); + # endif // ifdef ESP32 + # ifdef ESP8266 tft = new (std::nothrow) Adafruit_ILI9341(PIN(0), PIN(1), PIN(2), static_cast(_displayType), _xpix, _ypix); + # endif // ifdef ESP8266 if (nullptr != tft) { tft->begin(); diff --git a/src/src/PluginStructs/P095_data_struct.h b/src/src/PluginStructs/P095_data_struct.h index dec258d0e..23b575a61 100644 --- a/src/src/PluginStructs/P095_data_struct.h +++ b/src/src/PluginStructs/P095_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P095 -# include // include Adafruit graphics library -# include // include Adafruit ILI9341 TFT library +# include // include Adafruit graphics library +# include // include Adafruit ILI9341 TFT library + +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" @@ -137,10 +139,11 @@ public: String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P095_data_struct() = delete; @@ -208,6 +211,7 @@ private: uint16_t _fgcolor = ADAGFX_WHITE; uint16_t _bgcolor = ADAGFX_BLACK; bool _textBackFill = false; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P096_data_struct.cpp b/src/src/PluginStructs/P096_data_struct.cpp index 8d4b348cf..1be9706ad 100644 --- a/src/src/PluginStructs/P096_data_struct.cpp +++ b/src/src/PluginStructs/P096_data_struct.cpp @@ -75,6 +75,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor, uint16_t bgcolor, AdaGFXColorDepth colorDepth, @@ -84,7 +85,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, _xpix(width), _ypix(height), # endif // if !P096_USE_EXTENDED_SETTINGS _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _commandTrigger(commandTrigger), - _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) + _spi_bus(spi_bus), _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) { _commandTrigger.toLowerCase(); _commandTriggerCmd = _commandTrigger; @@ -124,17 +125,33 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { switch (_display) { case EPD_type_e::EPD_IL3897: - eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_UC8151D: - eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_SSD1680: - eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; # if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_WS2IN7: - eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // HSPI break; # endif // if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_MAX: diff --git a/src/src/PluginStructs/P096_data_struct.h b/src/src/PluginStructs/P096_data_struct.h index 9f6a79628..3150a3ae4 100644 --- a/src/src/PluginStructs/P096_data_struct.h +++ b/src/src/PluginStructs/P096_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P096 +# include "../Globals/SPIe.h" + # include // include Adafruit graphics library # include // include Adafruit Lolin eInk/ePaper library @@ -110,11 +112,12 @@ public: uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, AdaGFXColorDepth colorDepth = AdaGFXColorDepth::Monochrome, bool textBackFill = true); - P096_data_struct() = delete; + P096_data_struct() = delete; virtual ~P096_data_struct(); bool plugin_init(struct EventStruct *event); @@ -146,6 +149,7 @@ private: uint8_t _fontscaling; AdaGFXTextPrintMode _textmode; String _commandTrigger; + uint8_t _spi_bus; uint16_t _fgcolor; uint16_t _bgcolor; AdaGFXColorDepth _colorDepth; diff --git a/src/src/PluginStructs/P099_data_struct.cpp b/src/src/PluginStructs/P099_data_struct.cpp index 03fbbaa5a..7c1746426 100644 --- a/src/src/PluginStructs/P099_data_struct.cpp +++ b/src/src/PluginStructs/P099_data_struct.cpp @@ -42,7 +42,8 @@ bool P099_data_struct::init(taskIndex_t taskIndex, bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res) { + uint16_t ts_y_res, + uint8_t spi_bus) { reset(); _address_ts_cs = cs; @@ -54,8 +55,13 @@ bool P099_data_struct::init(taskIndex_t taskIndex, _useCalibration = useCalibration; _ts_x_res = ts_x_res; _ts_y_res = ts_y_res; + _spi_bus = spi_bus; - touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs); + touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); if (touchscreen != nullptr) { touchscreen->setRotation(_rotation); @@ -328,7 +334,7 @@ bool P099_data_struct::plugin_write(struct EventStruct *event, const String& str subcommand = parseString(string, 2); if (equals(command, F("touch"))) { - int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); + int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); if (command_i == -1) { // No matching subcommand found diff --git a/src/src/PluginStructs/P099_data_struct.h b/src/src/PluginStructs/P099_data_struct.h index 89814a6c1..abb73cb8c 100644 --- a/src/src/PluginStructs/P099_data_struct.h +++ b/src/src/PluginStructs/P099_data_struct.h @@ -8,64 +8,66 @@ # include +# include "../Globals/SPIe.h" + // #define PLUGIN_099_DEBUG // Additional debugging information // Define default values for both ESP32/lolin32 and D1 Mini # ifdef ESP32 # define P099_TS_CS 12 # else // ESP8266/ESP8285 - # define P099_TS_CS 0 // D3 + # define P099_TS_CS 0 // D3 # endif // ESP32 -# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 -# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 - // bytes calibration coordinates +# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 +# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 + // bytes calibration coordinates -# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function -# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function +# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function +# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function -#define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS -#define P099_CONFIG_STATE PCONFIG(0) -#define P099_CONFIG_CS_PIN PIN(0) -#define P099_CONFIG_TRESHOLD PCONFIG(1) -#define P099_CONFIG_ROTATION PCONFIG(2) -#define P099_CONFIG_X_RES PCONFIG(3) -#define P099_CONFIG_Y_RES PCONFIG(4) -#define P099_CONFIG_OBJECTCOUNT PCONFIG(5) -#define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) -#define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags +# define P099_CONFIG_STATE PCONFIG(0) +# define P099_CONFIG_CS_PIN PIN(0) +# define P099_CONFIG_TRESHOLD PCONFIG(1) +# define P099_CONFIG_ROTATION PCONFIG(2) +# define P099_CONFIG_X_RES PCONFIG(3) +# define P099_CONFIG_Y_RES PCONFIG(4) +# define P099_CONFIG_OBJECTCOUNT PCONFIG(5) +# define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) +# define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags -#define P099_VALUE_X UserVar[event->BaseVarIndex + 0] -#define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] -#define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] +# define P099_VALUE_X UserVar[event->BaseVarIndex + 0] +# define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] +# define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] -#define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) -#define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) -#define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) +# define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) +# define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) +# define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) -#define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch -#define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 -#define P099_TS_SEND_XY true // Enable X/Y events -#define P099_TS_SEND_Z false // Disable Z events -#define P099_TS_SEND_OBJECTNAME true // Enable objectname events -#define P099_TS_USE_CALIBRATION false // Disable calibration -#define P099_TS_LOG_CALIBRATION true // Enable calibration logging -#define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. -#define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on -#define P099_TS_Y_RES 320 -#define P099_INIT_OBJECTCOUNT 8 // Initial setting -#define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function +# define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch +# define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 +# define P099_TS_SEND_XY true // Enable X/Y events +# define P099_TS_SEND_Z false // Disable Z events +# define P099_TS_SEND_OBJECTNAME true // Enable objectname events +# define P099_TS_USE_CALIBRATION false // Disable calibration +# define P099_TS_LOG_CALIBRATION true // Enable calibration logging +# define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. +# define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on +# define P099_TS_Y_RES 320 +# define P099_INIT_OBJECTCOUNT 8 // Initial setting +# define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function -#define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that - // turn up -#define P099_TOUCH_Y_INVALID 4095 -#define P099_TOUCH_Z_INVALID 255 +# define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that + // turn up +# define P099_TOUCH_Y_INVALID 4095 +# define P099_TOUCH_Z_INVALID 255 // Data structure @@ -84,7 +86,8 @@ struct P099_data_struct : public PluginTaskData_base bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res); + uint16_t ts_y_res, + uint8_t spi_bus); bool isInitialized() const; void loadTouchObjects(taskIndex_t taskIndex); bool touched(); @@ -105,7 +108,8 @@ struct P099_data_struct : public PluginTaskData_base void scaleRawToCalibrated(uint16_t& x, uint16_t& y); - bool plugin_write(struct EventStruct *event,const String& string); + bool plugin_write(struct EventStruct *event, + const String & string); // This is initialized by calling init() XPT2046_Touchscreen *touchscreen = nullptr; @@ -118,6 +122,7 @@ struct P099_data_struct : public PluginTaskData_base bool _useCalibration = 0; uint16_t _ts_x_res = 0; uint16_t _ts_y_res = 0; + uint8_t _spi_bus = 0; // This is filled during checking of a touchobject uint32_t SurfaceAreas[P099_MaxObjectCount] = { 0 }; diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 3255bbdfd..c577376c6 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -33,8 +33,14 @@ P104_data_struct::P104_data_struct(MD_MAX72XX::moduleType_t _mod, uint8_t _modules, uint8_t _zonesCount) : mod(_mod), taskIndex(_taskIndex), cs_pin(_cs_pin), modules(_modules), expectedZones(_zonesCount) { - if (Settings.isSPI_valid()) { - P = new (std::nothrow) MD_Parola(mod, cs_pin, modules); + const uint8_t spi_bus = Settings.getSPIBusForTask(taskIndex); + + if (Settings.isSPI_valid(spi_bus)) { + P = new (std::nothrow) MD_Parola(mod, + # ifdef ESP32 + 0 == spi_bus ? SPI : SPIe, + # endif // ifdef ESP32 + cs_pin, modules); } else { addLog(LOG_LEVEL_ERROR, F("DOTMATRIX: Required SPI not enabled. Initialization aborted!")); } @@ -117,6 +123,7 @@ bool P104_data_struct::begin() { - ... - Max. allowed total custom settings size = 4096 */ + /************************************** * loadSettings *************************************/ @@ -337,57 +344,73 @@ void P104_data_struct::configureZones() { P->setCharSpacing(currentZone, P104_NORMAL_CHAR_SPACING); // Set default font spacing - switch (it->font) { + switch (it->font) + { # ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT - case P104_DOUBLE_HEIGHT_FONT_ID: { + case P104_DOUBLE_HEIGHT_FONT_ID: + { P->setFont(currentZone, numeric7SegDouble); P->setCharSpacing(currentZone, P104_DOUBLE_CHAR_SPACING); // double spacing as well break; } # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT # ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT - case P104_FULL_DOUBLEHEIGHT_FONT_ID: { + case P104_FULL_DOUBLEHEIGHT_FONT_ID: + { P->setFont(currentZone, BigFont); P->setCharSpacing(currentZone, P104_DOUBLE_CHAR_SPACING); // double spacing as well break; } # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT # ifdef P104_USE_VERTICAL_FONT - case P104_VERTICAL_FONT_ID: { + case P104_VERTICAL_FONT_ID: + { P->setFont(currentZone, _fontVertical); break; } # endif // ifdef P104_USE_VERTICAL_FONT # ifdef P104_USE_EXT_ASCII_FONT - case P104_EXT_ASCII_FONT_ID: { + case P104_EXT_ASCII_FONT_ID: + { P->setFont(currentZone, ExtASCII); break; } # endif // ifdef P104_USE_EXT_ASCII_FONT # ifdef P104_USE_ARABIC_FONT - case P104_ARABIC_FONT_ID: { + case P104_ARABIC_FONT_ID: + { P->setFont(currentZone, fontArabic); break; } # endif // ifdef P104_USE_ARABIC_FONT # ifdef P104_USE_GREEK_FONT - case P104_GREEK_FONT_ID: { + case P104_GREEK_FONT_ID: + { P->setFont(currentZone, fontGreek); break; } # endif // ifdef P104_USE_GREEK_FONT # ifdef P104_USE_KATAKANA_FONT - case P104_KATAKANA_FONT_ID: { + case P104_KATAKANA_FONT_ID: + { P->setFont(currentZone, fontKatakana); break; } # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + case P104_CYRILLIC_EXT_FONT_ID: + { + P->setFont(currentZone, font_cyr_ext); + break; + } + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT // Extend above this comment with more fonts if/when available, // case P104_DEFAULT_FONT_ID: and default: clauses should be the last options. // This should also make sure the default font is set if a no longer available font was selected case P104_DEFAULT_FONT_ID: - default: { + default: + { P->setFont(currentZone, nullptr); // default font break; } @@ -789,6 +812,7 @@ void P104_data_struct::displayBarGraph(uint8_t zone, # endif // ifdef P104_USE_BAR_GRAPH # ifdef P104_USE_DOT_SET + void P104_data_struct::displayDots(uint8_t zone, const P104_zone_struct& zstruct, const String & dots) { @@ -841,7 +865,8 @@ void P104_data_struct::displayDots(uint8_t zone, bool isAnimationAvailable(uint8_t animation, bool noneIsAllowed = false) { textEffect_t selection = static_cast(animation); - switch (selection) { + switch (selection) + { case PA_NO_EFFECT: { return noneIsAllowed; @@ -988,6 +1013,7 @@ enum class p104_subcommands_e { specialeffect, // subcommand: specialeffect,, (0..3) speed, // subcommand: speed,, (0..P104_MAX_SPEED_PAUSE_VALUE) # endif // ifdef P104_USE_COMMANDS + }; /******************************************************* @@ -1038,7 +1064,8 @@ bool P104_data_struct::handlePluginWrite(taskIndex_t taskIndex, // subcommands are processed in the same order as they are presented in the UI for (auto it = zones.begin(); it != zones.end() && !success; ++it) { if ((it->zone == zoneIndex)) { // This zone - switch (subcommands_e) { + switch (subcommands_e) + { case p104_subcommands_e::clear: // subcommand: clear, { @@ -1186,6 +1213,9 @@ bool P104_data_struct::handlePluginWrite(taskIndex_t taskIndex, # ifdef P104_USE_KATAKANA_FONT || (value4 == P104_KATAKANA_FONT_ID) # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + || (value4 == P104_CYRILLIC_EXT_FONT_ID) + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT ) { reconfigure = (it->font != value4); @@ -1407,7 +1437,8 @@ void P104_data_struct::getDate(char *psz, # ifdef P104_USE_DATETIME_OPTIONS if (showYear) { - switch (dateFmt) { + switch (dateFmt) + { case P104_DATE_FORMAT_US: d = node_time.month(); m = node_time.day(); @@ -1474,7 +1505,8 @@ uint8_t P104_data_struct::getDateTime(char *psz, # ifdef P104_USE_DATETIME_OPTIONS - switch (dateFmt) { + switch (dateFmt) + { case P104_DATE_FORMAT_US: d = node_time.month(); m = node_time.day(); @@ -1500,6 +1532,7 @@ uint8_t P104_data_struct::getDateTime(char *psz, } # if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + void P104_data_struct::createHString(String& string) { const uint16_t stringLen = string.length(); @@ -1545,7 +1578,8 @@ bool P104_data_struct::handlePluginOncePerSecond(struct EventStruct *event) { redisplay = false; if (P->getZoneStatus(it->zone - 1)) { // Animations done? - switch (it->content) { + switch (it->content) + { case P104_CONTENT_TIME: // time case P104_CONTENT_TIME_SEC: // time sec { @@ -1982,10 +2016,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { }; const int orderOptions[] = { 0, 1 }; FormSelectorOptions selector_zoneordering(2, orderTypes, orderOptions); - selector.reloadonchange = true; - # ifdef P104_USE_TOOLTIPS - selector.tooltip = zonetip; - # endif // ifdef P104_USE_TOOLTIPS + selector_zoneordering.reloadonchange = true; selector_zoneordering.addFormSelector(F("Zone order"), F("zoneorder"), bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0); selector_zoneordering.reloadonchange = true; @@ -2131,6 +2162,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { # ifdef P104_USE_KATAKANA_FONT , F("Katakana (7)") # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + , F("Cyrillic ext. (8)") + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT }; const int fontOptions[] = { P104_DEFAULT_FONT_ID @@ -2155,6 +2189,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { # ifdef P104_USE_KATAKANA_FONT , P104_KATAKANA_FONT_ID # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + , P104_CYRILLIC_EXT_FONT_ID + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT }; constexpr int fontCount = NR_ELEMENTS(fontTypes); @@ -2558,25 +2595,39 @@ bool P104_data_struct::webform_save(struct EventStruct *event) { P104_zone_struct::P104_zone_struct(uint8_t _zone) : text(F("\"\"")), zone(_zone) {} - bool P104_zone_struct::getIntValue(uint8_t offset, int32_t& value) const { - switch (offset) { - case P104_OFFSET_SIZE: value = size; break; + switch (offset) + { + case P104_OFFSET_SIZE: value = size; + break; case P104_OFFSET_TEXT: return false; - case P104_OFFSET_CONTENT: value = content; break; - case P104_OFFSET_ALIGNMENT: value = alignment; break; - case P104_OFFSET_ANIM_IN: value = animationIn; break; - case P104_OFFSET_SPEED: value = speed; break; - case P104_OFFSET_ANIM_OUT: value = animationOut; break; - case P104_OFFSET_PAUSE: value = pause; break; - case P104_OFFSET_FONT: value = font; break; - case P104_OFFSET_LAYOUT: value = layout; break; - case P104_OFFSET_SPEC_EFFECT: value = specialEffect; break; - case P104_OFFSET_OFFSET: value = offset; break; - case P104_OFFSET_BRIGHTNESS: value = brightness; break; - case P104_OFFSET_REPEATDELAY: value = repeatDelay; break; - case P104_OFFSET_INVERTED: value = inverted; break; + case P104_OFFSET_CONTENT: value = content; + break; + case P104_OFFSET_ALIGNMENT: value = alignment; + break; + case P104_OFFSET_ANIM_IN: value = animationIn; + break; + case P104_OFFSET_SPEED: value = speed; + break; + case P104_OFFSET_ANIM_OUT: value = animationOut; + break; + case P104_OFFSET_PAUSE: value = pause; + break; + case P104_OFFSET_FONT: value = font; + break; + case P104_OFFSET_LAYOUT: value = layout; + break; + case P104_OFFSET_SPEC_EFFECT: value = specialEffect; + break; + case P104_OFFSET_OFFSET: value = offset; + break; + case P104_OFFSET_BRIGHTNESS: value = brightness; + break; + case P104_OFFSET_REPEATDELAY: value = repeatDelay; + break; + case P104_OFFSET_INVERTED: value = inverted; + break; default: return false; @@ -2586,22 +2637,37 @@ bool P104_zone_struct::getIntValue(uint8_t offset, int32_t& value) const bool P104_zone_struct::setIntValue(uint8_t offset, int32_t value) { - switch (offset) { - case P104_OFFSET_SIZE: size = value; break; + switch (offset) + { + case P104_OFFSET_SIZE: size = value; + break; case P104_OFFSET_TEXT: return false; - case P104_OFFSET_CONTENT: content = value; break; - case P104_OFFSET_ALIGNMENT: alignment = value; break; - case P104_OFFSET_ANIM_IN: animationIn = value; break; - case P104_OFFSET_SPEED: speed = value; break; - case P104_OFFSET_ANIM_OUT: animationOut = value; break; - case P104_OFFSET_PAUSE: pause = value; break; - case P104_OFFSET_FONT: font = value; break; - case P104_OFFSET_LAYOUT: layout = value; break; - case P104_OFFSET_SPEC_EFFECT: specialEffect = value; break; - case P104_OFFSET_OFFSET: offset = value; break; - case P104_OFFSET_BRIGHTNESS: brightness = value; break; - case P104_OFFSET_REPEATDELAY: repeatDelay = value; break; - case P104_OFFSET_INVERTED: inverted = value; break; + case P104_OFFSET_CONTENT: content = value; + break; + case P104_OFFSET_ALIGNMENT: alignment = value; + break; + case P104_OFFSET_ANIM_IN: animationIn = value; + break; + case P104_OFFSET_SPEED: speed = value; + break; + case P104_OFFSET_ANIM_OUT: animationOut = value; + break; + case P104_OFFSET_PAUSE: pause = value; + break; + case P104_OFFSET_FONT: font = value; + break; + case P104_OFFSET_LAYOUT: layout = value; + break; + case P104_OFFSET_SPEC_EFFECT: specialEffect = value; + break; + case P104_OFFSET_OFFSET: offset = value; + break; + case P104_OFFSET_BRIGHTNESS: brightness = value; + break; + case P104_OFFSET_REPEATDELAY: repeatDelay = value; + break; + case P104_OFFSET_INVERTED: inverted = value; + break; default: return false; diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index f50975328..ff874ae72 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -16,6 +16,8 @@ # include "../Helpers/Misc.h" # include "../Helpers/StringParser.h" +#include "../Globals/SPIe.h" + # include // # if defined(PLUGIN_SET_MAX) || defined(PLUGIN_BUILD_CUSTOM) || ((defined(PLUGIN_DISPLAY_A_COLLECTION) || @@ -25,23 +27,24 @@ // # endif // if defined(PLUGIN_SET_MAX) || defined(PLUGIN_BUILD_CUSTOM) || ((defined(PLUGIN_DISPLAY_A_COLLECTION) || // defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP32)) # endif // ifdef ESP32 -# define P104_USE_FULL_DOUBLEHEIGHT_FONT // Enables the use of a full (lower ascii only) set double height font -# define P104_USE_VERTICAL_FONT // Enables the use of a vertical font -# define P104_USE_EXT_ASCII_FONT // Enables the use of an extended ascii font -# define P104_USE_ARABIC_FONT // Enables the use of a Arabic font (see usage in MD_Parola examples) -# define P104_USE_GREEK_FONT // Enables the use of a Greek font (see usage in MD_Parola examples) -# define P104_USE_KATAKANA_FONT // Enables the use of a Katakana font (see usage in MD_Parola examples) -# define P104_USE_COMMANDS // Enables the use of all commands, not just clear, txt, settxt and update -# define P104_USE_DATETIME_OPTIONS // Enables extra date/time options -# define P104_USE_BAR_GRAPH // Enables the use of Bar-graph feature -# define P104_USE_ZONE_ACTIONS // Enables the use of Actions per zone (New above/New below/Delete) -# define P104_USE_ZONE_ORDERING // Enables the use of Zone ordering (Numeric order (1..n)/Display order (n..1)) -# define P104_USE_DOT_SET // Enables the use of Dot-set feature +# define P104_USE_FULL_DOUBLEHEIGHT_FONT // Enables the use of a full (lower ascii only) set double height font +# define P104_USE_VERTICAL_FONT // Enables the use of a vertical font +# define P104_USE_EXT_ASCII_FONT // Enables the use of an extended ascii font +# define P104_USE_ARABIC_FONT // Enables the use of an Arabic font (see usage in MD_Parola examples) +# define P104_USE_GREEK_FONT // Enables the use of a Greek font (see usage in MD_Parola examples) +# define P104_USE_KATAKANA_FONT // Enables the use of a Katakana font (see usage in MD_Parola examples) +# define P104_USE_CYRILLIC_EXT_FONT // Enables the use of an ASCII font with Cyrillic extended characters +# define P104_USE_COMMANDS // Enables the use of all commands, not just clear, txt, settxt and update +# define P104_USE_DATETIME_OPTIONS // Enables extra date/time options +# define P104_USE_BAR_GRAPH // Enables the use of Bar-graph feature +# define P104_USE_ZONE_ACTIONS // Enables the use of Actions per zone (New above/New below/Delete) +# define P104_USE_ZONE_ORDERING // Enables the use of Zone ordering (Numeric order (1..n)/Display order (n..1)) +# define P104_USE_DOT_SET // Enables the use of Dot-set feature -# define P104_ADD_SETTINGS_NOTES // Adds some notes on the Settings page +# define P104_ADD_SETTINGS_NOTES // Adds some notes on the Settings page # if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) -# define P104_FEATURE_STORAGE_V3 1 // Only enable saving in storage for ESP32 +# define P104_FEATURE_STORAGE_V3 1 // Only enable saving in storage for ESP32 # else // if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) # define P104_FEATURE_STORAGE_V3 0 # endif // if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) @@ -69,6 +72,9 @@ # ifdef P104_USE_KATAKANA_FONT # undef P104_USE_KATAKANA_FONT # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT +# undef P104_USE_CYRILLIC_EXT_FONT +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT // # ifdef P104_USE_COMMANDS // # undef P104_USE_COMMANDS // # endif // ifdef P104_USE_COMMANDS @@ -85,7 +91,8 @@ # undef P104_DEBUG_DEV # endif // ifdef P104_DEBUG_DEV # define P104_MEDIUM_ANIMATIONS -# endif // if (defined(PLUGIN_DISPLAY_A_COLLECTION) || defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) +# endif // if (defined(PLUGIN_DISPLAY_A_COLLECTION) || defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP8266) && +// !defined(LIMIT_BUILD_SIZE) // # define P104_MINIMAL_ANIMATIONS // disable most animations // # define P104_MEDIUM_ANIMATIONS // disable some complex animations @@ -140,6 +147,9 @@ # ifdef P104_USE_KATAKANA_FONT # undef P104_USE_KATAKANA_FONT # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT +# undef P104_USE_CYRILLIC_EXT_FONT +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT # endif // ifdef LIMIT_BUILD_SIZE # if defined(P104_USE_TOOLTIPS) && !FEATURE_TOOLTIPS @@ -317,6 +327,13 @@ const uint8_t P104_DOUBLE_CHAR_SPACING = 2; // Character spacing for double-heig # include "../Static/Fonts/P104_font_katakana.h" # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT + +# define P104_CYRILLIC_EXT_FONT_ID 8 + +# include "../Static/Fonts/P104_font_cyr_ext.h" +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT + struct P104_zone_struct { P104_zone_struct() = delete; // Not used, so leave out explicitly P104_zone_struct(uint8_t _zone); @@ -350,6 +367,7 @@ struct P104_zone_struct { int32_t& value) const; bool setIntValue(uint8_t offset, int32_t value); + }; # ifdef P104_USE_BAR_GRAPH @@ -363,7 +381,9 @@ struct P104_bargraph_struct { uint8_t graph; uint8_t barType = 0u; uint8_t direction = 0u; + }; + # endif // ifdef P104_USE_BAR_GRAPH struct P104_data_struct : public PluginTaskData_base { @@ -379,20 +399,17 @@ struct P104_data_struct : public PluginTaskData_base { void loadSettings(); bool webform_load(struct EventStruct *event); bool webform_save(struct EventStruct *event); - String getError() { - return error; - } - void configureZones(); + String getError() { return error; } - void setZones(uint16_t _zones) { - expectedZones = _zones; - } + void configureZones(); - bool handlePluginWrite(taskIndex_t taskIndex, - const String& string); - bool handlePluginOncePerSecond(struct EventStruct *event); - void checkRepeatTimer(uint8_t z); + void setZones(uint16_t _zones) { expectedZones = _zones; } + + bool handlePluginWrite(taskIndex_t taskIndex, + const String& string); + bool handlePluginOncePerSecond(struct EventStruct *event); + void checkRepeatTimer(uint8_t z); MD_Parola *P = nullptr; @@ -490,9 +507,13 @@ private: struct { uint16_t P104_dataSize; char P104_data[P104_SETTINGS_BUFFER_V2 + 1]; + }; + uint8_t P104_storeThis[P104_SETTINGS_BUFFER_V2 + 1 + sizeof(uint16_t)]{}; + }; + }; #endif // ifdef USES_P104 diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index 821b38605..2db1671a4 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -11,10 +11,11 @@ # include -P111_data_struct::P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin) - : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin) +P111_data_struct::P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus) + : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin), _spi_bus(spi_bus) {} P111_data_struct::~P111_data_struct() { @@ -29,7 +30,12 @@ P111_data_struct::~P111_data_struct() { void P111_data_struct::init() { delete mfrc522; - mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin); // Instantiate a MFRC522 + // Instantiate a MFRC522 + mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); if (mfrc522 != nullptr) { mfrc522->PCD_Init(); // Initialize MFRC522 reader @@ -251,7 +257,10 @@ uint8_t P111_data_struct::readPassiveTargetID(uint8_t *uid, return P111_NO_ERROR; } -void P111_data_struct::mfrc522_interrupt(P111_data_struct *self) { self->_irq_pin_time_micros = getMicros64(); } +void P111_data_struct::mfrc522_interrupt(P111_data_struct *self) +{ + self->_irq_pin_time_micros = getMicros64(); +} /********************************************************************************************* * Handle regular read and reset processing diff --git a/src/src/PluginStructs/P111_data_struct.h b/src/src/PluginStructs/P111_data_struct.h index 72da64742..7180fc878 100644 --- a/src/src/PluginStructs/P111_data_struct.h +++ b/src/src/PluginStructs/P111_data_struct.h @@ -6,6 +6,8 @@ # include +# include "../Globals/SPIe.h" + # define P111_CS_PIN PIN(0) # define P111_RST_PIN PIN(1) # define P111_IRQ_PIN PIN(2) @@ -28,18 +30,20 @@ enum class P111_initPhases : uint8_t { ResetDelay1 = 0x01, ResetDelay2 = 0x02, Undefined = 0xFF + }; struct P111_data_struct : public PluginTaskData_base { - P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin); + P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus); P111_data_struct() = delete; virtual ~P111_data_struct(); - void init(); - bool plugin_ten_per_second(struct EventStruct *event); - bool plugin_fifty_per_second(struct EventStruct *event); + void init(); + bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_fifty_per_second(struct EventStruct *event); String PCD_getVersion(uint8_t& v); @@ -51,21 +55,22 @@ private: uint8_t counter = 0; - String getCardName(); - uint8_t readCardStatus(uint64_t *key, - bool *removedTag); - bool reset(int8_t csPin, - int8_t resetPin); - uint8_t readPassiveTargetID(uint8_t *uid, - uint8_t *uidLength); + String getCardName(); + uint8_t readCardStatus(uint64_t *key, + bool *removedTag); + bool reset(int8_t csPin, + int8_t resetPin); + uint8_t readPassiveTargetID(uint8_t *uid, + uint8_t *uidLength); - static void mfrc522_interrupt(P111_data_struct * self); + static void mfrc522_interrupt(P111_data_struct *self); int32_t timeToWait = 0; - int8_t _csPin; - int8_t _rstPin; - int8_t _irqPin; + int8_t _csPin; + int8_t _rstPin; + int8_t _irqPin; + uint8_t _spi_bus; uint8_t errorCount = 0; bool removedState = true; // On startup, there will usually not be a tag nearby @@ -74,6 +79,7 @@ private: int64_t _last_served_irq_pin_time_micros{}; ESPEASY_VOLATILE(int64_t) _irq_pin_time_micros = -1; + }; #endif // ifdef USES_P111 diff --git a/src/src/PluginStructs/P116_data_struct.cpp b/src/src/PluginStructs/P116_data_struct.cpp index ad65da83e..94a75fabd 100644 --- a/src/src/PluginStructs/P116_data_struct.cpp +++ b/src/src/PluginStructs/P116_data_struct.cpp @@ -128,7 +128,8 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -136,7 +137,7 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, ) : _device(device), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), - _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill) + _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -197,14 +198,14 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_BLACKTAB135x240; // 135x240px } - // fall through + // fall through case ST77xx_type_e::ST7735s_172x320: if (initRoptions == 0xFF) { initRoptions = INITR_BLACKTAB172x320; // 172x320px } - // fall through + // fall through case ST77xx_type_e::ST77xxs_170x320: if (initRoptions == 0xFF) { @@ -233,7 +234,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_MINI160x80; // 80x160px } + # ifdef ESP32 + st7735 = new (std::nothrow) Adafruit_ST7735(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7735 = new (std::nothrow) Adafruit_ST7735(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8166 if (nullptr != st7735) { st7735->initR(initRoptions); // initialize a ST7735s chip @@ -251,7 +257,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { case ST77xx_type_e::ST7789vw3_135x240: # endif // if P116_EXTRA_ST7789 { + # ifdef ESP32 + st7789 = new (std::nothrow) Adafruit_ST7789(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7789 = new (std::nothrow) Adafruit_ST7789(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7789) { uint8_t init_seq = 0; // Default/original initialisation @@ -273,7 +284,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { } case ST77xx_type_e::ST7796s_320x480: { + # ifdef ESP32 + st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7796) { st7796->begin(); diff --git a/src/src/PluginStructs/P116_data_struct.h b/src/src/PluginStructs/P116_data_struct.h index b70d3a381..5a27290a3 100644 --- a/src/src/PluginStructs/P116_data_struct.h +++ b/src/src/PluginStructs/P116_data_struct.h @@ -32,9 +32,11 @@ # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" -# define P116_Nlines 24 // The number of different lines which can be displayed +# include "../Globals/SPIe.h" + +# define P116_Nlines 24 // The number of different lines which can be displayed # define P116_Nchars 60 -# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled +# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled // # define P116_SHOW_SPLASH // Enable to show splash (text) @@ -137,10 +139,11 @@ public: String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P116_data_struct() = delete; @@ -197,6 +200,7 @@ private: uint16_t _fgcolor; uint16_t _bgcolor; bool _textBackFill; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P118_data_struct.cpp b/src/src/PluginStructs/P118_data_struct.cpp index 9b2db6e8a..6c9a0b4e3 100644 --- a/src/src/PluginStructs/P118_data_struct.cpp +++ b/src/src/PluginStructs/P118_data_struct.cpp @@ -5,11 +5,12 @@ // **************************************************************************/ // Constructor // **************************************************************************/ -P118_data_struct::P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog) - : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog) {} +P118_data_struct::P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus) + : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog), _spi_bus(spi_bus) {} // **************************************************************************/ // Destructor @@ -30,9 +31,13 @@ bool P118_data_struct::plugin_init(struct EventStruct *event) { int8_t spi_pins[3]; uint32_t startInit = 0; - if (Settings.getSPI_pins(spi_pins) && validGpio(spi_pins[1])) { + if (Settings.getSPI_pins(spi_pins, _spi_bus) && validGpio(spi_pins[1])) { startInit = millis(); - _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1]); // Pass CS and MISO + _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1] + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPIe // defaults and SPI bus for ESP32 only + # endif // ifdef ESP32 + ); // Pass CS and MISO } else { addLog(LOG_LEVEL_ERROR, F("ITHO: SPI configuration not correct!")); } diff --git a/src/src/PluginStructs/P118_data_struct.h b/src/src/PluginStructs/P118_data_struct.h index fe6bba777..c06ca7003 100644 --- a/src/src/PluginStructs/P118_data_struct.h +++ b/src/src/PluginStructs/P118_data_struct.h @@ -9,6 +9,8 @@ # include "IthoCC1101.h" # include "IthoPacket.h" +# include "../Globals/SPIe.h" + # define P118_DEBUG_LOG // Enable for some (extra) logging # ifndef P118_FEATURE_ORCON # define P118_FEATURE_ORCON 1 // Enable use of Orcon commands @@ -60,10 +62,11 @@ struct PLUGIN_118_ExtraSettingsStruct { struct P118_data_struct : public PluginTaskData_base { public: - P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog); + P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus); P118_data_struct() = delete; virtual ~P118_data_struct(); @@ -104,10 +107,11 @@ private: int _OldLastIDindex = 0; bool _InitRunned = false; - int8_t _csPin = -1; - int8_t _irqPin = -1; - bool _log = false; - bool _rfLog = false; + int8_t _csPin = -1; + int8_t _irqPin = -1; + bool _log = false; + bool _rfLog = false; + uint8_t _spi_bus; PLUGIN_118_ExtraSettingsStruct _ExtraSettings; diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index ae63cc684..ff8c79bcb 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -4,6 +4,7 @@ # define P120_RAD_TO_DEG 57.295779f // 180.0/M_PI +# include "../Globals/SPIe.h" P120_data_struct::P120_data_struct(uint8_t aSize) : _aSize(aSize) @@ -235,7 +236,14 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { if (i2c_mode) { adxl345 = new (std::nothrow) ADXL345(_i2c_addr); // Init using I2C } else { - adxl345 = new (std::nothrow) ADXL345(_cs_pin); // Init using SPI + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 + adxl345 = new (std::nothrow) ADXL345(_cs_pin + # ifdef ESP32 + , 0 == spi_bus ? SPI : SPIe + # endif // ifdef ESP32 + ); // Init using SPI } if (initialized()) { @@ -537,7 +545,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { FormSelectorOptions selector(4, rangeOptions, rangeValues); selector.default_index = P120_RANGE_16G; selector.addFormSelector(F("Range"), F("range"), - get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); + get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); addUnit('g'); } @@ -631,7 +639,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("10"), F("50") }; int frequencyValues[] = { P120_FREQUENCY_10, P120_FREQUENCY_50 }; - const FormSelectorOptions selector( 2, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(2, frequencyOptions, frequencyValues); selector.addFormSelector(F("Measuring frequency"), F("frequency"), P120_FREQUENCY); addUnit(F("Hz")); addFormNote(F("Values X/Y/Z are updated 1x per second, Controller updates & Value-events are based on 'Interval' setting.")); diff --git a/src/src/PluginStructs/P132_data_struct.cpp b/src/src/PluginStructs/P132_data_struct.cpp index 0f7cb4c31..3239e25e3 100644 --- a/src/src/PluginStructs/P132_data_struct.cpp +++ b/src/src/PluginStructs/P132_data_struct.cpp @@ -8,34 +8,169 @@ int Plugin_132_QueryVType(uint8_t value_nr) { const bool odd = value_nr % 2; return static_cast(odd ? Sensor_VType::SENSOR_TYPE_CURRENT_ONLY : Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY); } - return static_cast(Sensor_VType::SENSOR_TYPE_NONE); + return static_cast(Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY); } # endif // if FEATURE_MQTT_DISCOVER +# if P132_EXTENDED +const __FlashStringHelper* toString(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return F("INA3221"); + case P132_DeviceType::Ina219: return F("INA219"); + case P132_DeviceType::Ina226: return F("INA226"); + case P132_DeviceType::Ina228: return F("INA228"); + case P132_DeviceType::Ina230: return F("INA230"); + case P132_DeviceType::Ina231: return F("INA231"); + case P132_DeviceType::Ina260: return F("INA260"); + case P132_DeviceType::InaUnknown: return F(""); + } + return F(""); +} + +const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return INA3221_0; + case P132_DeviceType::Ina219: return INA219; + case P132_DeviceType::Ina226: return INA226; + case P132_DeviceType::Ina228: return INA228; + case P132_DeviceType::Ina230: return INA230; + case P132_DeviceType::Ina231: return INA231; + case P132_DeviceType::Ina260: return INA260; + case P132_DeviceType::InaUnknown: return INA_UNKNOWN; + } + return INA_UNKNOWN; +} + +const P132_DeviceType P132_INATypeToDeviceType(uint8_t inaType) { + switch (inaType) { + case INA219: return P132_DeviceType::Ina219; + case INA226: return P132_DeviceType::Ina226; + case INA228: return P132_DeviceType::Ina228; + case INA230: return P132_DeviceType::Ina230; + case INA231: return P132_DeviceType::Ina231; + case INA260: return P132_DeviceType::Ina260; + case INA3221_0: + case INA3221_1: + case INA3221_2: return P132_DeviceType::Ina3221; + } + return P132_DeviceType::Ina3221; +} + +const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return 9; // Voltage 1..3/Current 1..3/Power 1..3 + case P132_DeviceType::Ina219: + case P132_DeviceType::Ina226: + case P132_DeviceType::Ina228: + case P132_DeviceType::Ina230: + case P132_DeviceType::Ina231: + case P132_DeviceType::Ina260: return 3; // Voltage/Current/Power + case P132_DeviceType::InaUnknown: return 0; + } + return 0; +} + +# endif // if P132_EXTENDED + // **************************************************************************/ // Constructor // **************************************************************************/ +# if P132_EXTENDED +P132_data_struct::P132_data_struct(struct EventStruct *event) { + P132_DeviceType altType = P132_DeviceType::InaUnknown; + + _deviceType = static_cast(P132_INA_TYPE); + + // INA226 and INA231 are interchangeable + if (P132_DeviceType::Ina226 == _deviceType) { altType = P132_DeviceType::Ina231; } + + if (P132_DeviceType::Ina231 == _deviceType) { altType = P132_DeviceType::Ina226; } + + _i2c_address = P132_I2C_ADDR; + INA = new INA_Class(16); // Max 16 addresses can be used, trade-off: INA3221 uses 3 slots... + + if (nullptr == INA) { + addLog(LOG_LEVEL_ERROR, F("INA Class initialization failed")); + } else { + uint16_t maxCurrent = max((uint16_t)1u, (uint16_t)P132_MAX_CURRENT); + const uint32_t shunt = P132_CFG_VERSION != P132_GET_CFG_VERSION ? (100 / P132_SHUNT) * 1000 : P132_SHUNT_V2; + const uint8_t count = INA->begin(maxCurrent, shunt); + uint8_t search = 0; + + while (search < count) { + const P132_DeviceType foundType = P132_INATypeToDeviceType(INA->getDeviceType(search)); + const uint8_t addr = INA->getDeviceAddress(search); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("INA : Detected: %s at 0x%x, device index: %d"), + INA->getDeviceName(search), INA->getDeviceAddress(search), search)); + } + # endif// ifndef BUILD_NO_DEBUG + + if (((foundType == _deviceType) || (foundType == altType)) && (addr == _i2c_address)) { + _device = search; + break; // Found, done searching + } + + if (P132_DeviceType::Ina3221 == foundType) { + search += 2; // Skip 'sub'-devices for INA3321 + } + ++search; + } + + if (0xFF == _device) { + delete INA; + INA = nullptr; + addLog(LOG_LEVEL_ERROR, strformat(F("INA : Configured %s at 0x%x not found."), + FsP(toString(_deviceType)), _i2c_address)); + } else + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("INA : Found: %s at 0x%x, device index: %d"), + INA->getDeviceName(_device), INA->getDeviceAddress(_device), _device)); + } + } + setCalibration(event); +} + +# else // if P132_EXTENDED P132_data_struct::P132_data_struct(struct EventStruct *event) { _i2c_address = P132_I2C_ADDR; setCalibration_INA3221(event); } +# endif // if P132_EXTENDED + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +# if P132_EXTENDED +P132_data_struct::~P132_data_struct() { + delete INA; + INA = nullptr; +} + +# endif // if P132_EXTENDED + // **************************************************************************/ // Gets the raw bus voltage (7FF8 / 32760) LSB 8mV // **************************************************************************/ +# if !P132_EXTENDED + int16_t P132_data_struct::getBusVoltage_raw(byte reg) { uint16_t value = I2C_read16_reg(_i2c_address, reg); // Shift to the right 3 to drop CNVR and OVF and multiply by LSB 8 mV - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: get raw bus %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return (int16_t)((value >> 3) * 8); } @@ -45,67 +180,153 @@ int16_t P132_data_struct::getBusVoltage_raw(byte reg) { int16_t P132_data_struct::getShuntVoltage_raw(byte reg) { uint16_t value = I2C_read16_reg(_i2c_address, reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG String log = strformat(F("INA3221: get raw shunt voltage %d value2 - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG // Shift to the right 3 to drop CNVR and OVF and multiply by LSB if (value > 32767) { // check value is negative // value = 0; // no negative measure value = ((value >> 3) | 0xE000); // correct int16_t value - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" value_neg - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG } else { value = (value >> 3); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" value_pos - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG } - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" reg - "), reg); addLog(LOG_LEVEL_DEBUG, log); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value; } +# endif // if !P132_EXTENDED + // **************************************************************************/ -// Gets the shunt voltage in mV (32760 so +-163.8 mV) 7ff8 LSB 40uV +// Gets the shunt voltage in mV // **************************************************************************/ +# if P132_EXTENDED +float P132_data_struct::getShuntVoltage_mV(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getShuntMicroVolts(_device + reg) * 0.001f; +} + +# else // if P132_EXTENDED float P132_data_struct::getShuntVoltage_mV(byte reg) { int16_t value = getShuntVoltage_raw(reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: shunt voltage in mV * 0.04 %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value * 0.04f; } +# endif // if P132_EXTENDED + // **************************************************************************/ // Gets the Bus voltage in volts // **************************************************************************/ +# if P132_EXTENDED +float P132_data_struct::getBusVoltage_V(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMilliVolts(_device + reg) * 0.001f; +} + +# else // if P132_EXTENDED float P132_data_struct::getBusVoltage_V(byte reg) { int16_t value = getBusVoltage_raw(reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: get bus voltage %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value * 0.001f; } +# endif // if P132_EXTENDED + +# if P132_EXTENDED + // **************************************************************************/ -// Configures to INA3221 +// Gets the Bus current in milliampere // **************************************************************************/ +float P132_data_struct::getBusCurrent_mA(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMicroAmps(_device + reg) * 0.001f; +} + +// **************************************************************************/ +// Gets the Bus power in milliwatt +// **************************************************************************/ +float P132_data_struct::getBusPower_mW(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMicroWatts(_device + reg) * 0.001f; +} + +bool P132_data_struct::conversionFinished(uint8_t reg) { + if (!isInitialized()) { + return false; + } + return INA->conversionFinished(_device + reg); +} + +# endif // if P132_EXTENDED + +// **************************************************************************/ +// Configures the INA +// **************************************************************************/ +# if P132_EXTENDED +void P132_data_struct::setCalibration(struct EventStruct *event) { + if (!isInitialized()) { + return; + } + + if (P132_DeviceType::Ina219 != _deviceType) { // Averaging set via conversion + INA->setAveraging(getAverageBitsToFactor(P132_GET_AVERAGE, _deviceType), _device); + } + const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + INA->setBusConversion(getConversionBitsToFactor(convB, _deviceType), _device); + INA->setShuntConversion(getConversionBitsToFactor(convS, _deviceType), _device); + + # ifndef BUILD_NO_DEBUG + + // Config register and manufacurer id + const uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read config + const uint16_t mfgid = I2C_read16_reg(_i2c_address, 0xFE); // read manufacturer ID + const uint16_t dieid = I2C_read16_reg(_i2c_address, 0xFF); // read Die ID + + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, + strformat(F("INA : init I2C: 0x%02x mfg: 0x%x, did: 0x%x config: 0x%x, 0b%s"), + _i2c_address, mfgid, dieid, config, String(config, BIN).c_str())); + } + # endif // ifndef BUILD_NO_DEBUG +} + +# else // if P132_EXTENDED void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { // Set Config register uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read, chip default: 0x7127 @@ -115,14 +336,14 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { set3BitToUL(config, INA3221_CONVERSION_BUS_BIT, P132_GET_CONVERSION_B); set3BitToUL(config, INA3221_CONVERSION_SHUNT_BIT, P132_GET_CONVERSION_S); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("INA3221: init I2C: 0x%02x mfg: 0x%x, config: 0x%x, 0b%s"), _i2c_address, mfgid, config, String(config, BIN).c_str())); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG if (mfgid != 0x5449) { addLog(LOG_LEVEL_ERROR, F("INA3221: Invalid Manufacturer ID! (0x5449)")); @@ -131,4 +352,61 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { I2C_write16_reg(_i2c_address, 0x00, static_cast(config)); } +# endif // if P132_EXTENDED + +# if P132_EXTENDED +uint32_t P132_data_struct::getAverageBitsToFactor(uint8_t bits, P132_DeviceType deviceType) { + if (P132_DeviceType::Ina219 == deviceType) { + if (bits) { + return 1 << bits; + } + return 0u; + } + + switch (bits) { + case 0b000: return 0u; + case 0b001: return 4u; + case 0b010: return 16u; + case 0b011: return 64u; + case 0b100: return 128u; + case 0b101: return 256u; + case 0b110: return 512u; + case 0b111: return 1024u; + } + return 0u; +} + +uint32_t P132_data_struct::getConversionBitsToFactor(uint8_t bits, P132_DeviceType deviceType) { + if (P132_DeviceType::Ina219 == deviceType) { + switch (bits) { + case 0b0000: return 0u; + case 0b0001: return 148u; + case 0b0010: return 276u; + case 0b1000: return 532u; + case 0b1001: return 1060u; + case 0b1010: return 2130u; + case 0b1011: return 4260u; + case 0b1100: return 8510u; + case 0b1101: return 17020u; + case 0b1110: return 34050u; + case 0b1111: return 68100u; + } + return 532u; + } + + switch (bits) { + case 0b0000: return 0u; + case 0b0001: return 204u; + case 0b0010: return 332u; + case 0b0011: return 588u; + case 0b0100: return 1100u; + case 0b0101: return 2116u; + case 0b0110: return 4156u; + case 0b0111: return 8244u; + } + return 1100u; +} + +# endif // if P132_EXTENDED + #endif // ifdef USES_P132 diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 49be9469d..b7200d6a5 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -4,24 +4,61 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P132 +# ifndef P132_EXTENDED +# ifdef ESP8266 +# ifdef PLUGIN_ENERGY_COLLECTION +# define P132_EXTENDED 1 // Enable for ESP8266 Energy collection, as for now it has enough space +# else // ifdef PLUGIN_ENERGY_COLLECTION +# define P132_EXTENDED 0 +# endif // ifdef PLUGIN_ENERGY_COLLECTION +# endif // ifdef ESP8266 +# ifdef ESP32 +# define P132_EXTENDED 1 +# endif // ifdef ESP32 +# endif // ifndef P132_EXTENDED + // # define P132_DEBUG_LOG // Enable for some (extra) logging +# if P132_EXTENDED +# define PLUGIN_NAME_132 "Energy (DC) - INA3221/INA226/INA228/INA231/INA260" +# else // if P132_EXTENDED +# define PLUGIN_NAME_132 "Energy (DC) - INA3221" +# endif // if P132_EXTENDED + # define P132_CONFIG_BASE 2 // Better not change this... +# define P132_INA_TYPE PCONFIG(0) +# define P132_INA_PREVIOUS PCONFIG(7) # define P132_I2C_ADDR PCONFIG(1) # define P132_VALUE_1 PCONFIG(P132_CONFIG_BASE) # define P132_VALUE_2 PCONFIG(P132_CONFIG_BASE + 1) # define P132_VALUE_3 PCONFIG(P132_CONFIG_BASE + 2) # define P132_VALUE_4 PCONFIG(P132_CONFIG_BASE + 3) # define P132_SHUNT PCONFIG(6) +# define P132_MAX_CURRENT PCONFIG_LONG(1) +# define P132_SHUNT_V2 PCONFIG_ULONG(2) -# define P132_CONFIG_FLAGS PCONFIG_LONG(0) -# define P132_FLAG_AVERAGE 0 -# define P132_FLAG_CONVERSION_B 3 -# define P132_FLAG_CONVERSION_S 6 +# define P132_CFG_VERSION 0x1 // Config version: 0 = V1, 1 = V2 -# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE) -# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B) -# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S) +# define P132_CONFIG_FLAGS PCONFIG_ULONG(0) +# define P132_FLAG_AVERAGE 0 // 3 bits +# define P132_FLAG_CONVERSION_B 3 // 3 bits, V1 config +# define P132_FLAG_CONVERSION_S 6 // 3 bits, V1 config +# define P132_FLAG_CFG_VERSION 9 // 2 bits +# define P132_FLAG_V2_CONVERSION_B 11 // 4 bits, V2 config +# define P132_FLAG_V2_CONVERSION_S 15 // 4 bits, V2 config + +# define P132_SET_AVERAGE(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE, (S)) +# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE) +# define P132_SET_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B, (S)) +# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B) +# define P132_SET_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S, (S)) +# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S) +# define P132_SET_CFG_VERSION(S) set2BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION, (S)) +# define P132_GET_CFG_VERSION get2BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION) +# define P132_SET_V2_CONVERSION_B(S) set4BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B, (S)) +# define P132_GET_V2_CONVERSION_B get4BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B) +# define P132_SET_V2_CONVERSION_S(S) set4BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S, (S)) +# define P132_GET_V2_CONVERSION_S get4BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S) # define INA3221_AVERAGE_BIT 9 # define INA3221_CONVERSION_BUS_BIT 6 @@ -31,23 +68,70 @@ int Plugin_132_QueryVType(uint8_t value_nr); # endif // if FEATURE_MQTT_DISCOVER +# if P132_EXTENDED +# include + +enum class P132_DeviceType : uint8_t { + Ina3221 = 0u, + Ina219 = 1u, + Ina226 = 2u, + Ina228 = 3u, + Ina230 = 4u, + Ina231 = 5u, + Ina260 = 6u, + InaUnknown = 255u, +}; + +const __FlashStringHelper* toString(P132_DeviceType deviceType); +const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType); +const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType); +# endif // if P132_EXTENDED + struct P132_data_struct : public PluginTaskData_base { public: P132_data_struct(struct EventStruct *event); - P132_data_struct() = delete; + P132_data_struct() = delete; + # if P132_EXTENDED + virtual ~P132_data_struct(); + # else // if P132_EXTENDED virtual ~P132_data_struct() = default; + # endif // if P132_EXTENDED - float getShuntVoltage_mV(byte reg); - float getBusVoltage_V(byte reg); + float getShuntVoltage_mV(uint8_t reg); + float getBusVoltage_V(uint8_t reg); + # if P132_EXTENDED + float getBusCurrent_mA(uint8_t reg); + float getBusPower_mW(uint8_t reg); + bool conversionFinished(uint8_t reg); - void setCalibration_INA3221(struct EventStruct *event); + void setCalibration(struct EventStruct *event); + + bool isInitialized() const { + return nullptr != INA; + } + + # else // if P132_EXTENDED + void setCalibration_INA3221(struct EventStruct *event); + # endif // if P132_EXTENDED private: - int16_t getBusVoltage_raw(byte reg); - int16_t getShuntVoltage_raw(byte reg); + # if P132_EXTENDED + INA_Class *INA = nullptr; + P132_DeviceType _deviceType = P132_DeviceType::Ina3221; // Old default + uint8_t _device = 0xFF; + + + uint32_t getAverageBitsToFactor(uint8_t bits, + P132_DeviceType deviceType); + uint32_t getConversionBitsToFactor(uint8_t bits, + P132_DeviceType deviceType); + # else // if P132_EXTENDED + int16_t getBusVoltage_raw(byte reg); + int16_t getShuntVoltage_raw(byte reg); + # endif // if P132_EXTENDED int8_t _i2c_address; }; diff --git a/src/src/PluginStructs/P141_data_struct.cpp b/src/src/PluginStructs/P141_data_struct.cpp index 74a23c95a..71f1300b7 100644 --- a/src/src/PluginStructs/P141_data_struct.cpp +++ b/src/src/PluginStructs/P141_data_struct.cpp @@ -27,7 +27,8 @@ P141_data_struct::P141_data_struct(uint8_t rotation, uint16_t fgcolor, uint16_t bgcolor, bool textBackFill, - bool displayInverted + bool displayInverted, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -36,7 +37,7 @@ P141_data_struct::P141_data_struct(uint8_t rotation, : _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _contrast(contrast), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill), _displayInverted(displayInverted) + _textBackFill(textBackFill), _displayInverted(displayInverted), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -66,7 +67,11 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { if (nullptr == pcd8544) { addLog(LOG_LEVEL_INFO, F("PCD8544: Init start.")); - pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN); + pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPIe + # endif // ifdef ESP32 + ); # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { diff --git a/src/src/PluginStructs/P141_data_struct.h b/src/src/PluginStructs/P141_data_struct.h index 649eb89d8..abfbd6ff6 100644 --- a/src/src/PluginStructs/P141_data_struct.h +++ b/src/src/PluginStructs/P141_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P141 -# include // include Adafruit graphics library -# include // include Adafruit PCD8544 LCD library +# include // include Adafruit graphics library +# include // include Adafruit PCD8544 LCD library + +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object @@ -90,13 +92,14 @@ public: uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, bool textBackFill = true, - bool displayInverted = false + bool displayInverted = false, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); - P141_data_struct() = delete; + P141_data_struct() = delete; virtual ~P141_data_struct(); bool plugin_init(struct EventStruct *event); @@ -151,6 +154,7 @@ private: uint16_t _bgcolor; bool _textBackFill; bool _displayInverted; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED diff --git a/src/src/PluginStructs/P154_data_struct.cpp b/src/src/PluginStructs/P154_data_struct.cpp index 589dcc718..c64871f48 100644 --- a/src/src/PluginStructs/P154_data_struct.cpp +++ b/src/src/PluginStructs/P154_data_struct.cpp @@ -10,7 +10,11 @@ P154_data_struct::P154_data_struct(struct EventStruct *event) : i2cAddress(P154_I2C_ADDR), elevation(P154_ALTITUDE), csPin(PIN(0)) -{} +{ + # ifdef ESP32 + _spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 +} bool P154_data_struct::begin(bool _i2cMode) { @@ -20,7 +24,11 @@ bool P154_data_struct::begin(bool _i2cMode) return false; } - if (!i2cMode && !bmp.begin_SPI(csPin)) { + if (!i2cMode && !bmp.begin_SPI(csPin + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPIe + # endif // ifdef ESP32 + )) { return false; } diff --git a/src/src/PluginStructs/P154_data_struct.h b/src/src/PluginStructs/P154_data_struct.h index 4c507ff01..375372e3b 100644 --- a/src/src/PluginStructs/P154_data_struct.h +++ b/src/src/PluginStructs/P154_data_struct.h @@ -7,6 +7,8 @@ # include # include +#include "../Globals/SPIe.h" + # define P154_I2C_ADDR PCONFIG(0) # define P154_ALTITUDE PCONFIG(1) @@ -36,6 +38,7 @@ private: uint8_t i2cAddress; int16_t elevation{}; int16_t csPin{}; + uint8_t _spi_bus{}; bool initialized = false; bool i2cMode = true; diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index 809b020eb..65dc96280 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -9,18 +9,23 @@ // Needed also here for PlatformIO's library finder as the .h file // is in a directory which is excluded in the src_filter -P162_data_struct::P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin) - : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin) -{} +P162_data_struct::P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus) + : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin), _spi_bus(spi_bus) +{ + # ifdef ESP32 + _spi = 0 == _spi_bus ? SPI : SPIe; + # endif // ifdef ESP32 +} P162_data_struct::~P162_data_struct() { // } bool P162_data_struct::plugin_init(struct EventStruct *event) { - if (validGpio(_csPin) && Settings.isSPI_valid()) { + if (validGpio(_csPin) && Settings.isSPI_valid(_spi_bus)) { pinMode(_csPin, OUTPUT); _initialized = true; } @@ -168,8 +173,8 @@ void P162_data_struct::write_pot(uint8_t cmd, digitalWrite(_csPin, LOW); // send the command and value via SPI: - SPI.transfer(cmd); - SPI.transfer(val); + _spi.transfer(cmd); + _spi.transfer(val); // Set the CS pin high to execute the command: digitalWrite(_csPin, HIGH); diff --git a/src/src/PluginStructs/P162_data_struct.h b/src/src/PluginStructs/P162_data_struct.h index b26916a3b..b6fdadab6 100644 --- a/src/src/PluginStructs/P162_data_struct.h +++ b/src/src/PluginStructs/P162_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P162 +# include "../Globals/SPIe.h" + # define P162_CS_PIN PIN(0) # define P162_RST_PIN PIN(1) # define P162_SHD_PIN PIN(2) @@ -33,9 +35,10 @@ const uint8_t P162_BOTH_POT_SHUTDOWN = 0x23; const uint8_t P162_RESET_VALUE = 0x80; // Pot setting on power-up/reset struct P162_data_struct : public PluginTaskData_base { - P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin); + P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus); P162_data_struct() = delete; virtual ~P162_data_struct(); @@ -45,6 +48,7 @@ struct P162_data_struct : public PluginTaskData_base { private: + SPIClass& _spi = SPI; bool hw_reset(); void write_pot(uint8_t cmd, uint8_t val); @@ -57,6 +61,7 @@ private: int8_t _shdPin; uint8_t _shdState = HIGH; bool _initialized = false; + uint8_t _spi_bus; }; #endif // ifdef USES_P162 diff --git a/src/src/Static/Fonts/P104_font_cyr_ext.h b/src/src/Static/Fonts/P104_font_cyr_ext.h new file mode 100644 index 000000000..84e89bf82 --- /dev/null +++ b/src/src/Static/Fonts/P104_font_cyr_ext.h @@ -0,0 +1,261 @@ +#pragma once + +MD_MAX72XX::fontType_t font_cyr_ext[] PROGMEM = // ASCII with Cyrillic extension +{ + 0, // 0 - Empty Cell + 5, 62, 91, 79, 91, 62, // 1 - Sad Smiley + 5, 62, 107, 79, 107, 62, // 2 - Happy Smiley + 5, 28, 62, 124, 62, 28, // 3 - Heart + 5, 24, 60, 126, 60, 24, // 4 - Diamond + 5, 28, 87, 125, 87, 28, // 5 - Clubs + 5, 28, 94, 127, 94, 28, // 6 - Spades + 3, 24, 60, 24, // 7 - Bullet Point + 5, 255, 231, 195, 231, 255, // 8 - Rev Bullet Point + 3, 24, 36, 24, // 9 - Hollow Bullet Point + 5, 255, 231, 219, 231, 255, // 10 - Rev Hollow BP + 5, 48, 72, 58, 6, 14, // 11 - Male + 5, 38, 41, 121, 41, 38, // 12 - Female + 5, 64, 127, 5, 5, 7, // 13 - Music Note 1 + 5, 64, 127, 5, 37, 63, // 14 - Music Note 2 + 5, 90, 60, 231, 60, 90, // 15 - Snowflake + 5, 127, 62, 28, 28, 8, // 16 - Right Pointer + 5, 8, 28, 28, 62, 127, // 17 - Left Pointer + 5, 20, 34, 127, 34, 20, // 18 - UpDown Arrows + 5, 95, 95, 0, 95, 95, // 19 - Double Exclamation + 5, 6, 9, 127, 1, 127, // 20 - Paragraph Mark + 4, 102, 137, 149, 106, // 21 - Section Mark + 5, 96, 96, 96, 96, 96, // 22 - Double Underline + 5, 148, 162, 255, 162, 148, // 23 - UpDown Underlined + 5, 8, 4, 126, 4, 8, // 24 - Up Arrow + 5, 16, 32, 126, 32, 16, // 25 - Down Arrow + 5, 8, 8, 42, 28, 8, // 26 - Right Arrow + 5, 8, 28, 42, 8, 8, // 27 - Left Arrow + 5, 30, 16, 16, 16, 16, // 28 - Angled + 5, 12, 30, 12, 30, 12, // 29 - Squashed # + 5, 48, 56, 62, 56, 48, // 30 - Up Pointer + 5, 6, 14, 62, 14, 6, // 31 - Down Pointer + 2, 0, 0, // 32 - Space + 1, 95, // 33 - ! + 3, 3, 0, 3, // 34 - " + 5, 20, 62, 20, 62, 20, // 35 - # + 4, 36, 106, 43, 18, // 36 - $ + 5, 99, 19, 8, 100, 99, // 37 - % + 5, 54, 73, 86, 32, 80, // 38 - & + 1, 3, // 39 - ' + 3, 28, 34, 65, // 40 - ( + 3, 65, 34, 28, // 41 - ) + 5, 40, 24, 14, 24, 40, // 42 - * + 5, 8, 8, 62, 8, 8, // 43 - + + 2, 176, 112, // 44 - , + 4, 8, 8, 8, 8, // 45 - - + 2, 96, 96, // 46 - . + 4, 96, 24, 6, 1, // 47 - / + 4, 62, 65, 65, 62, // 48 - 0 + 3, 66, 127, 64, // 49 - 1 + 4, 98, 81, 73, 70, // 50 - 2 + 4, 34, 65, 73, 54, // 51 - 3 + 4, 24, 20, 18, 127, // 52 - 4 + 4, 39, 69, 69, 57, // 53 - 5 + 4, 62, 73, 73, 48, // 54 - 6 + 4, 97, 17, 9, 7, // 55 - 7 + 4, 54, 73, 73, 54, // 56 - 8 + 4, 6, 73, 73, 62, // 57 - 9 + 2, 108, 108, // 58 - : + 2, 108, 236, // 59 - ; + 3, 16, 40, 68, // 60 - < + 3, 20, 20, 20, // 61 - = + 3, 68, 40, 16, // 62 - > + 4, 2, 89, 9, 6, // 63 - ? + 5, 62, 73, 85, 93, 14, // 64 - @ + 4, 126, 17, 17, 126, // 65 - A + 4, 127, 73, 73, 54, // 66 - B + 4, 62, 65, 65, 34, // 67 - C + 4, 127, 65, 65, 62, // 68 - D + 4, 127, 73, 73, 65, // 69 - E + 4, 127, 9, 9, 1, // 70 - F + 4, 62, 65, 73, 122, // 71 - G + 4, 127, 8, 8, 127, // 72 - H + 3, 65, 127, 65, // 73 - I + 4, 48, 64, 65, 63, // 74 - J + 4, 127, 8, 20, 99, // 75 - K + 4, 127, 64, 64, 64, // 76 - L + 5, 127, 2, 12, 2, 127, // 77 - M + 5, 127, 4, 8, 16, 127, // 78 - N + 4, 62, 65, 65, 62, // 79 - O + 4, 127, 9, 9, 6, // 80 - P + 4, 62, 65, 65, 190, // 81 - Q + 4, 127, 9, 9, 118, // 82 - R + 4, 70, 73, 73, 50, // 83 - S + 5, 1, 1, 127, 1, 1, // 84 - T + 4, 63, 64, 64, 63, // 85 - U + 5, 15, 48, 64, 48, 15, // 86 - V + 5, 63, 64, 56, 64, 63, // 87 - W + 5, 99, 20, 8, 20, 99, // 88 - X + 5, 7, 8, 112, 8, 7, // 89 - Y + 4, 97, 81, 73, 71, // 90 - Z + 2, 127, 65, // 91 - [ + 4, 1, 6, 24, 96, // 92 - \ backslash + 2, 65, 127, // 93 - ] + 3, 2, 1, 2, // 94 - ^ + 4, 64, 64, 64, 64, // 95 - _ + 2, 1, 2, // 96 - ` + 4, 32, 84, 84, 120, // 97 - a + 4, 127, 68, 68, 56, // 98 - b + 4, 56, 68, 68, 40, // 99 - c + 4, 56, 68, 68, 127, // 100 - d + 4, 56, 84, 84, 24, // 101 - e + 3, 4, 126, 5, // 102 - f + 4, 152, 164, 164, 120, // 103 - g + 4, 127, 4, 4, 120, // 104 - h + 3, 68, 125, 64, // 105 - i + 4, 64, 128, 132, 125, // 106 - j + 4, 127, 16, 40, 68, // 107 - k + 3, 65, 127, 64, // 108 - l + 5, 124, 4, 124, 4, 120, // 109 - m + 4, 124, 4, 4, 120, // 110 - n + 4, 56, 68, 68, 56, // 111 - o + 4, 252, 36, 36, 24, // 112 - p + 4, 24, 36, 36, 252, // 113 - q + 4, 124, 8, 4, 4, // 114 - r + 4, 72, 84, 84, 36, // 115 - s + 3, 4, 63, 68, // 116 - t + 4, 60, 64, 64, 124, // 117 - u + 5, 28, 32, 64, 32, 28, // 118 - v + 5, 60, 64, 60, 64, 60, // 119 - w + 5, 68, 40, 16, 40, 68, // 120 - x + 4, 156, 160, 160, 124, // 121 - y + 3, 100, 84, 76, // 122 - z + 3, 8, 54, 65, // 123 - { + 1, 127, // 124 - | + 3, 65, 54, 8, // 125 - } + 4, 8, 4, 8, 4, // 126 - ~ + 0, // 127 - 0 + 5, 252, 36, 36, 36, 24, // 128 - 'р' + 5, 120, 132, 132, 132, 72, // 129 - 'с' + 5, 4, 4, 252, 4, 4, // 130 - 'т' + 5, 76, 144, 144, 144, 124, // 131 - 'у' + 5, 56, 68, 254, 68, 56, // 132 - 'ф' + 5, 196, 40, 16, 40, 196, // 133 - 'х' + 5, 124, 64, 64, 124, 192, // 134 - 'ц' + 5, 28, 32, 32, 32, 252, // 135 - 'ч' + 5, 252, 128, 252, 128, 252, // 136 - 'ш' + 5, 124, 64, 124, 64, 252, // 137 - 'щ' + 5, 4, 252, 144, 144, 96, // 138 - 'ъ' + 5, 252, 144, 240, 0, 252, // 139 - 'ы' + 4, 252, 144, 144, 96, // 140 - 'ь' + 5, 72, 132, 148, 148, 120, // 141 - 'э' + 5, 252, 32, 252, 132, 252, // 142 - 'ю' + 5, 24, 164, 100, 36, 252, // 143 - 'я' + 5, 248, 36, 34, 33, 255, // 144 - 'А' + 5, 255, 137, 137, 137, 112, // 145 - 'Б' + 5, 255, 137, 137, 137, 118, // 146 - 'В' + 4, 255, 1, 1, 3, // 147 - 'Г' + 6, 192, 126, 65, 65, 127, 192, // 148 - 'Д' + 4, 255, 137, 137, 129, // 149 - 'Е' + 5, 247, 8, 255, 8, 247, // 150 - 'Ж' + 5, 66, 129, 137, 137, 118, // 151 - 'З' + 5, 255, 32, 16, 8, 255, // 152 - 'И' + 5, 254, 32, 17, 8, 254, // 153 - 'Й' + 5, 255, 8, 20, 34, 193, // 154 - 'К' + 5, 128, 124, 2, 1, 255, // 155 - 'Л' + 5, 255, 4, 24, 4, 255, // 156 - 'М' + 5, 255, 8, 8, 8, 255, // 157 - 'Н' + 5, 126, 129, 129, 129, 126, // 158 - 'О' + 5, 255, 1, 1, 1, 255, // 159 - 'П' + 5, 255, 17, 17, 17, 14, // 160 - 'Р' + 5, 126, 129, 129, 129, 66, // 161 - 'С' + 5, 3, 1, 255, 1, 3, // 162 - 'Т' + 5, 71, 136, 144, 144, 127, // 163 - 'У' + 5, 60, 66, 255, 66, 60, // 164 - 'Ф' + 5, 227, 20, 8, 20, 227, // 165 - 'Х' + 5, 127, 64, 64, 127, 192, // 166 - 'Ц' + 5, 15, 16, 16, 16, 255, // 167 - 'Ч' + 5, 255, 128, 254, 128, 255, // 168 - 'Ш' + 5, 127, 64, 126, 64, 255, // 169 - 'Щ' + 5, 1, 255, 136, 136, 112, // 170 - 'Ъ' + 5, 255, 132, 120, 0, 255, // 171 - 'Ы' + 4, 255, 136, 136, 112, // 172 - 'Ь' + 5, 66, 129, 137, 137, 126, // 173 - 'Э' + 6, 255, 16, 126, 129, 129, 126, // 174 - 'Ю' + 5, 142, 81, 49, 17, 255, // 175 - 'Я' + 5, 96, 148, 148, 248, 128, // 176 - 'а' + 4, 252, 148, 148, 96, // 177 - 'б' + 4, 252, 148, 148, 104, // 178 - 'в' + 4, 252, 4, 4, 12, // 179 - 'г' + 5, 192, 124, 68, 124, 192, // 180 - 'д' + 5, 120, 164, 164, 164, 24, // 181 - 'е' + 5, 236, 16, 252, 16, 236, // 182 - 'ж' + 5, 72, 132, 148, 148, 104, // 183 - 'з' + 5, 252, 64, 32, 16, 252, // 184 - 'и' + 5, 252, 64, 34, 16, 252, // 185 - 'й' + 5, 252, 16, 40, 68, 128, // 186 - 'к' + 5, 128, 240, 8, 4, 252, // 187 - 'л' + 5, 252, 16, 224, 16, 252, // 188 - 'м' + 5, 252, 16, 16, 16, 252, // 189 - 'н' + 5, 120, 132, 132, 132, 120, // 190 - 'о' + 5, 252, 4, 4, 4, 252, // 191 - 'п' + 4, 250, 168, 168, 170, // 192 - Ё + 4, 114, 168, 168, 146, // 193 - ё + 5, 0, 0, 0, 255, 16, // 194 - Left T + 5, 16, 16, 16, 16, 16, // 195 - Top side + 5, 16, 16, 16, 255, 16, // 196 - Center + + 5, 0, 0, 0, 255, 20, // 197 - Left side double H + 5, 0, 0, 255, 0, 255, // 198 - Left side double + 5, 0, 0, 31, 16, 23, // 199 - Bot Left double V + 5, 0, 0, 252, 4, 244, // 200 - Top Left double V + 5, 20, 20, 23, 16, 23, // 201 - Bot T double + 5, 20, 20, 244, 4, 244, // 202 - Top T double + 5, 0, 0, 255, 0, 247, // 203 - Left Side double spl + 5, 20, 20, 20, 20, 20, // 204 - Center double + 5, 20, 20, 247, 0, 247, // 205 - Center + double + 5, 20, 20, 20, 23, 20, // 206 - Bot T double H + 5, 16, 16, 31, 16, 31, // 207 - Bot Right double V + 0, // 208 - for rus fonts UTF-8 + 0, // 209 - for rus fonts UTF-8 + 5, 0, 0, 31, 16, 31, // 210 - Bot Left double V + 5, 0, 0, 0, 31, 20, // 211 - Bot Right double H + 5, 0, 0, 0, 252, 20, // 212 - Top Right double H + 5, 0, 0, 240, 16, 240, // 213 - Top Right double V + 5, 16, 16, 255, 16, 255, // 214 - Center + double V + 5, 20, 20, 20, 255, 20, // 215 - Center + double H + 5, 16, 16, 16, 16, 31, // 216 - Bot Right + 5, 0, 0, 0, 240, 16, // 217 - Top Left + 5, 255, 255, 255, 255, 255, // 218 - Full Block + 5, 240, 240, 240, 240, 240, // 219 - Half Block Bottom + 3, 255, 255, 255, // 220 - Half Block LHS + 5, 0, 0, 0, 255, 255, // 221 - Half Block RHS + 5, 15, 15, 15, 15, 15, // 222 - Half Block Top + 5, 56, 68, 68, 56, 68, // 223 - Alpha + 5, 124, 42, 42, 62, 20, // 224 - Beta + 5, 126, 2, 2, 6, 6, // 225 - Gamma + 5, 2, 126, 2, 126, 2, // 226 - Pi + 5, 99, 85, 73, 65, 99, // 227 - Sigma + 5, 56, 68, 68, 60, 4, // 228 - Theta + 5, 64, 126, 32, 30, 32, // 229 - mu + 5, 6, 2, 126, 2, 2, // 230 - Tau + 5, 153, 165, 231, 165, 153, // 231 - 0 + 5, 28, 42, 73, 42, 28, // 232 - 0 + 5, 76, 114, 1, 114, 76, // 233 - 0 + 5, 48, 74, 77, 77, 48, // 234 - 0 + 5, 48, 72, 120, 72, 48, // 235 - 0 + 5, 188, 98, 90, 70, 61, // 236 - Zero Slashed + 4, 62, 73, 73, 73, // 237 - 0 + 5, 126, 1, 1, 1, 126, // 238 - 0 + 5, 42, 42, 42, 42, 42, // 239 - 3 Bar Equals + 5, 68, 68, 95, 68, 68, // 240 - +/- + 5, 64, 81, 74, 68, 64, // 241 - >= + 5, 64, 68, 74, 81, 64, // 242 - <= + 5, 0, 0, 255, 1, 3, // 243 - Top of Integral + 3, 224, 128, 255, // 244 - Bot of Integral + 5, 8, 8, 107, 107, 8, // 245 - Divide + 5, 54, 18, 54, 36, 54, // 246 - Wavy = + 5, 6, 15, 9, 15, 6, // 247 - Degree + 4, 0, 0, 24, 24, // 248 - Math Product + 4, 0, 0, 16, 16, // 249 - Short Dash + 5, 48, 64, 255, 1, 1, // 250 - Square Root + 5, 0, 31, 1, 1, 30, // 251 - Superscript n + 5, 0, 25, 29, 23, 18, // 252 - Superscript 2 + 5, 0, 60, 60, 60, 60, // 253 - Centered Square + 5, 255, 129, 129, 129, 255, // 254 - Full Frame + 5, 255, 255, 255, 255, 255, // 255 - Full Block +}; diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index a0995dbed..6e49234d6 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -112,50 +112,51 @@ static const char DATA_GITHUB_CLIPBOARD_JS[] PROGMEM = { #ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ // For the gzipped data, see below in the #else block static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { -".logviewer,tbody{overflow:auto}.container,.normal td{height:30px}.container,.container input:checked~.checkmark:after,.container2,.c" -"ontainer2 input:checked~.dotmark:after,.sub{display:block}:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;--bdc1:silver;--bg4:#e" -"8e8e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--bdc2:#444 #444 #fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10" -":#07d;--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0}[data-theme=dark]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#5454" -"54;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;--c5:#c3" -"c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}@media (prefers-color-scheme:dark){[data-theme=auto]{--bg1:#444;-" -"-bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#f" -"ff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}" -"}* :not(font,a),.closebtn,.div_r,.wide,h1,h6,textarea{color:var(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;positi" -"on:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt" -";margin:0}h1{font-size:16pt;margin:8px 0}h3{background-color:var(--bg2);margin:-5px;padding:6px}h6{font-size:10pt}pre{font:1em monos" -"pace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}.checkmark,input,select{background-col" -"or:var(--bg1)}#toastmessage,.button.help,.checkmark,.dotmark,.logviewer,hr,input,select,textarea{border:.1px solid gray}input[disabl" -"ed],input[disabled]:hover{background-color:var(--bg7)}.button.link.wide{display:inline-block;text-align:center}.button.link.red{back" -"ground-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius" -":5px}.button:hover{background:#369}input,select,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-widt" -"h:85px;width:95%}.sub,.widenumber{max-width:100px}textarea.wide{max-width:1000px}.xwide{width:300px}.container,.container2{cursor:po" -"inter;font-size:12pt;padding-left:35px}.container{margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disable" -"d{background-color:grey}.checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #f" -"ff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3)" -";border-radius:5px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;te" -"xt-align:center;visibility:hidden;z-index:1}.container2{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotma" -"rk{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2 .dotmark:after{backgrou" -"nd:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}textarea{font-family:'Lucida Console',Monaco,monospace;max-height:60" -"vh;width:-webkit-fill-available;width:-moz-available}#toastmessage.show{animation:.5s fadein,.5s 2.5s fadeout;visibility:visible}.co" -"ntainer input,.container2 input{cursor:pointer;opacity:0;position:absolute}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%" -";opacity:.9}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}.level_1{color:#fcff95}.lev" -"el_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color:#f2ab39}.level_9{color:#f50}.logviewer{background-color:#272727;color:#f1f1" -"f1;height:60vh}.multirow th,.normal th{background-color:#444;border-color:var(--bdc1);color:var(--c1);padding:5px;width:100vw}.multi" -"row,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.normal tr{padding:5px}.multirow td{border-color:var(--bdc1);height:" -"30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}.sub{border-spacing:0}.even tr:nth-child(2n),.tworow tr:nth" -"-child(4n+3),.tworow tr:nth-child(4n+4),textarea{background-color:var(--bg4)}.menu.active,body{background-color:var(--bg6)}.apheader" -",.headermenu{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style:italic}.headermenu{background-color:var(--bg11);border-bott" -"om:.1px solid #444;height:100px;position:fixed;top:0;z-index:1}.bodymenu{height:100vh;padding-top:100px}.menubar{position:inherit;to" -"p:54px;width:100%}a.menu{align-items:center;display:flex;height:46px;justify-content:center}.menu{color:var(--c2);border:1px solid t" -"ransparent;border-radius:5px 5px 0 0;padding:10px 10px 8px;white-space:nowrap}.menu.active{border-color:var(--bdc2);color:var(--c3);" -"border-width:.1px}.menu:hover{background:var(--bg7);color:var(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_" -"r{background-color:var(--bg8);border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20" -"px}.highlight td{background-color:var(--bg5)}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.c" -"losebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x" -":auto;padding:10px 0}footer{padding:0 5px;word-break:break-all}.closebtn,.div_r{float:right;color:var(--c6)}a,h1,h6{color:var(--c5)}" -".button,.container input:checked~.checkmark:not(.disabled),.container2 input:checked~.dotmark{background-color:var(--bg10)}canvas{fi" -"lter:invert(var(--iv)) hue-rotate(var(--ro))}@media screen and (max-width:830px){.showmenulabel{display:none}a.menu{width:11%}}@medi" -"a screen and (max-width:450px){.normal{min-width:300px}input.wide:focus{left:4px;position:absolute;z-index:1}}" +".logviewer,tbody{overflow:auto}.container,.normal td{height:30px}.container,.container input:checked~.checkmark:after,.container2,.co" +"ntainer2 input:checked~.dotmark:after,.sub{display:block}.menu,h1{white-space:nowrap}:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;" +"--bdc1:silver;--bg4:#e8e8e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10:#07d;" +"--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0;--tpm2:140px}[data-theme=dark]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;" +"--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:" +"#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}@media (prefers-color-scheme:dark){[data-theme=auto]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;" +"--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;" +"--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}}* :not(font,a),.closebtn,.div_r,.wide,h1,h6,textarea{color:var" +"(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;position:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}" +"*{box-sizing:border-box;font-family:sans-serif;font-size:12pt;margin:0}h1{font-size:16pt}h3{background-color:var(--bg2);margin:-5px;padding:6px}" +"h6{font-size:10pt}pre{font:1em monospace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}.checkmark," +"input,select{background-color:var(--bg1)}#toastmessage,.button.help,.checkmark,.dotmark,.logviewer,hr,input,select,textarea{border:.1px solid " +"gray}input[disabled],input[disabled]:hover{background-color:var(--bg7)}.button.link.wide{display:inline-block;text-align:center}.button.link" +".red{background-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius:" +"5px}.button:hover{background:#369}input,select,summary,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-width:" +"85px;width:95%}.sub,.widenumber{max-width:100px}textarea.wide{max-width:1000px}.xwide{width:300px}.container,.container2{cursor:pointer;font-" +"size:12pt;padding-left:35px}.container{margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disabled{background-color:grey}" +".checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #fff;border-width:0 3px 3px 0;height:" +"10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3);border-radius:5px;bottom:30%;color:#fff;font-" +"size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibility:hidden;z-index:1}.container2" +"{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotmark{background-color:#eee;border-radius:50%;height:26px;left:0;po" +"sition:absolute;top:0;width:26px}.container2 .dotmark:after{background:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}textarea" +"{font-family:'Lucida Console',Monaco,monospace;max-height:60vh;width:-webkit-fill-available;width:-moz-available}#toastmessage.show{animation:" +".5s fadein,.5s 2.5s fadeout;visibility:visible}.container input,.container2 input{cursor:pointer;opacity:0;position:absolute}@keyframes fadein" +"{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}" +".level_1{color:#fcff95}.level_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color:#f2ab39}.level_9{color:#f50}.logviewer{background-color:" +"#272727;color:#f1f1f1;height:60vh}.multirow th,.normal th{background-color:#444;border-color:var(--bdc1);color:var(--c1);padding:5px;width:" +"100vw}.multirow,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.normal tr{padding:5px}.multirow td{border-color:var(--bdc1);height:" +"30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}.sub{border-spacing:0}.even tr:nth-child(2n),.tworow tr:nth-child(4n+3)" +",.tworow tr:nth-child(4n+4),textarea{background-color:var(--bg4)}.menu.active,body{background-color:var(--bg6)}.apheader,.headermenu,.headermenu2" +"{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style:italic}.headermenu,.headermenu2{background-color:var(--bg11);position:fixed;top:0;" +"z-index:1;box-shadow:0 0 10px 1px rgb(0 0 0 / 30%)}.headermenu{height:100px;border-bottom:.1px solid #444}.headermenu2{height:var(--tpm2)}" +".bodymenu{min-height:100vh;padding-top:100px}.bodymenu2{min-height:100vh;padding-top:var(--tpm2)}.menubar,.menubar2{display:flex;position:inherit;" +"overflow-x:scroll;scrollbar-width:none}[class^=menubar]::-webkit-scrollbar{display:none}.menubar{top:54px;max-width:1180px}.menubar2{top:99px;" +"background:var(--bg2);width:100vw;left:0;border-block:.1px solid #222}a.menu{align-items:center;display:flex;height:46px;justify-content:center;" +"gap:2px;width:100%}.menubar2 a.menu{max-width:70px;height:41px;border-bottom:0}.menu{color:var(--c2);border:1px solid transparent;border-radius:" +"5px 5px 0 0;padding:10px 10px 8px}.menu.active{border-color:#222;color:var(--c3);border-width:.1px}.menu:hover{background:var(--bg7);color:var" +"(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_r{background-color:var(--bg8);border-radius:4px;margin:2px;padding:1px " +"10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20px}.highlight td{background-color:var(--bg5)}.div_br{clear:both}.alert{background-" +"color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}" +".closebtn:hover{color:#000}section{overflow-x:auto;padding:10px 0}summary{cursor:pointer}footer{padding:0 5px;word-break:break-all}.closebtn," +".div_r{float:right;color:var(--c6)}a,h1,h6{color:var(--c5)}.button,.container input:checked~.checkmark:not(.disabled),.container2 input:" +"checked~.dotmark{background-color:var(--bg10)}canvas{filter:invert(var(--iv)) hue-rotate(var(--ro))}.tc1{width:15vw}@media screen and (max-width:" +"1280px){.tc1{width:25vw}}@media screen and (max-width:975px){.showmenulabel{display:none}.menubar{width:100%;max-width:700px;left:0}}@media " +"screen and (max-width:450px){.normal{min-width:300px}input.wide:focus{left:4px;position:absolute;z-index:1}}" }; #else // ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ // - upload the minified .js file to https://www.mischianti.org/online-converter-file-to-cpp-gzip-byte-array-3/ @@ -165,136 +166,147 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // - Modify the line "const uint8_t espeasy_default_min_css_gz[] ..." // to "static const char DATA_ESPEASY_DEFAULT_MIN_CSS_GZ[] ..." // ------------------ -//File: espeasy_default.min.css.gz, Size: 2017 -static const int espeasy_default_min_css_gz_len = 2017; +//File: espeasy_default.min.css.gz, Size: 2199 +static const int espeasy_default_min_css_gz_len = 2199; static const char DATA_ESPEASY_DEFAULT_MIN_CSS_GZ[] PROGMEM = { -0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xD5, 0x18, 0xDB, 0x8E, 0xAB, 0x36, -0xF0, 0x57, 0x90, 0x56, 0xAB, 0xB3, 0xB4, 0x01, 0x99, 0x5B, 0x2E, 0x44, 0x95, 0x2A, 0xF5, 0xB5, -0xFD, 0x82, 0xAA, 0x3A, 0x32, 0x60, 0x82, 0xBB, 0x04, 0x47, 0xC6, 0xB9, 0x1D, 0xE4, 0x7E, 0x7B, -0xC7, 0x06, 0x83, 0x21, 0xC9, 0xD1, 0x79, 0xE9, 0x43, 0x17, 0xED, 0xAE, 0x3D, 0x1E, 0xCF, 0x8C, -0xE7, 0x6E, 0xFB, 0x35, 0x3B, 0x5C, 0x28, 0xB9, 0x12, 0xBE, 0x12, 0x19, 0x2B, 0xEE, 0x1D, 0xBB, -0x10, 0x5E, 0xD6, 0xEC, 0x9A, 0xE2, 0xB3, 0x60, 0xD2, 0xCF, 0x59, 0x23, 0x30, 0x6D, 0x60, 0xD9, -0x6F, 0x18, 0x3F, 0xE2, 0xDA, 0x11, 0x45, 0x57, 0x11, 0x7A, 0xA8, 0x44, 0x1A, 0xA1, 0xD3, 0x6D, -0x86, 0x31, 0x0E, 0x1D, 0xDA, 0x9C, 0xCE, 0x22, 0xCD, 0x2B, 0x92, 0x7F, 0x92, 0xE2, 0x1F, 0x5F, -0x0F, 0x8E, 0x98, 0x7F, 0xA6, 0xB8, 0x14, 0x33, 0xCC, 0xD0, 0x1E, 0x2F, 0xB7, 0x15, 0x4C, 0xD8, -0x9B, 0xDA, 0x73, 0xD6, 0x15, 0xB4, 0x3D, 0xD5, 0xF8, 0x9E, 0x66, 0x35, 0xCB, 0x3F, 0x65, 0xCA, -0x19, 0x13, 0x9D, 0xE7, 0x65, 0x87, 0x20, 0x7D, 0x23, 0x84, 0xEC, 0xD5, 0x30, 0x4C, 0xDF, 0x72, -0xAC, 0x3E, 0x3D, 0x8B, 0xD2, 0x37, 0xB4, 0x29, 0x60, 0x98, 0x03, 0x4A, 0x59, 0x96, 0x0A, 0x58, -0xC0, 0xB8, 0xA5, 0x35, 0x9C, 0x54, 0xA3, 0xC4, 0xB0, 0x77, 0xAB, 0x3E, 0x3D, 0x4B, 0xD2, 0xB7, -0x22, 0x2B, 0x4B, 0x84, 0x36, 0x89, 0xDA, 0x05, 0xD4, 0xE2, 0x38, 0xD6, 0x2B, 0x6B, 0x8B, 0x40, -0x0F, 0x76, 0xFA, 0x3F, 0x3D, 0x34, 0x57, 0xAC, 0x10, 0xD2, 0xA8, 0x9B, 0xF4, 0x2D, 0x2B, 0xD4, -0xA7, 0x67, 0x5B, 0x58, 0x08, 0x77, 0x59, 0xA4, 0xC8, 0xE4, 0x71, 0x4A, 0x9B, 0x8A, 0x70, 0x2A, -0xD4, 0x24, 0x31, 0xD2, 0xC1, 0x09, 0x90, 0x35, 0x56, 0xB2, 0x6E, 0xD5, 0xA7, 0x90, 0x46, 0xBE, -0x9C, 0xA5, 0xA8, 0x20, 0x07, 0x18, 0xD1, 0x4B, 0x8A, 0xE4, 0x9F, 0x05, 0x16, 0xD8, 0x13, 0x15, -0x39, 0x92, 0x5F, 0x0A, 0xD0, 0xD3, 0x5F, 0x46, 0x13, 0x46, 0x60, 0x10, 0x32, 0x24, 0xEA, 0x1B, -0x35, 0xA1, 0x7F, 0xB6, 0xC9, 0xA0, 0x8E, 0xA2, 0x54, 0x9F, 0xD1, 0xC8, 0x5B, 0x12, 0xAB, 0xCF, -0xA8, 0xC4, 0xDA, 0x09, 0x52, 0x26, 0x49, 0x84, 0x34, 0x55, 0x75, 0xF2, 0x41, 0x0B, 0x4A, 0x21, -0x61, 0x18, 0x1A, 0x85, 0x08, 0x8E, 0x9B, 0xF6, 0x84, 0x39, 0x69, 0xC4, 0xA0, 0x0D, 0x83, 0xB7, -0x31, 0x9C, 0x23, 0x64, 0xF4, 0x11, 0x21, 0xA0, 0x58, 0xF4, 0xFA, 0x78, 0xCB, 0x23, 0xF5, 0x0D, -0xFA, 0x18, 0x27, 0xBD, 0x4A, 0xE2, 0x78, 0x8D, 0x36, 0x78, 0xD4, 0xCA, 0x38, 0x55, 0x5A, 0x19, -0x51, 0x41, 0x31, 0xC1, 0x76, 0x52, 0x4D, 0x20, 0x7F, 0x3D, 0x92, 0x82, 0x62, 0xE7, 0xE3, 0xC4, -0x49, 0x49, 0x78, 0xEB, 0xE5, 0xAC, 0x66, 0xDC, 0x6B, 0x73, 0xA5, 0xAB, 0x54, 0xE9, 0xCA, 0xED, -0x6C, 0xED, 0x29, 0x5F, 0xFF, 0x5F, 0x6A, 0x2F, 0x3B, 0xEC, 0x0C, 0x95, 0xFF, 0x46, 0x91, 0xF2, -0x27, 0x27, 0x6D, 0x98, 0xF8, 0x28, 0x21, 0x48, 0x57, 0xD8, 0x85, 0x68, 0xAD, 0x59, 0x4B, 0x32, -0xD1, 0xAC, 0xFC, 0x82, 0x5E, 0xBE, 0x42, 0x54, 0x5E, 0x69, 0x41, 0x56, 0x55, 0xB0, 0xAA, 0xD6, -0x2B, 0x41, 0x6E, 0x02, 0x4E, 0x80, 0x3B, 0xAD, 0xEE, 0xF4, 0x82, 0xF9, 0x87, 0x92, 0xCB, 0x95, -0x8F, 0x09, 0x60, 0x16, 0xDA, 0x9D, 0x4A, 0x01, 0x70, 0xF0, 0xF4, 0xCB, 0x97, 0xBD, 0x09, 0xF0, -0x86, 0x35, 0x64, 0x7F, 0x62, 0x2D, 0x15, 0x94, 0x35, 0x29, 0xCE, 0x5A, 0x56, 0x9F, 0x05, 0x91, -0x7E, 0x76, 0x16, 0x82, 0x01, 0xF7, 0x23, 0x69, 0xCE, 0x9D, 0x62, 0xE8, 0x15, 0x24, 0x67, 0x1C, -0x6B, 0x34, 0xB5, 0x49, 0x6A, 0xC1, 0xEA, 0x01, 0x03, 0x52, 0x19, 0x16, 0x69, 0x4D, 0x4A, 0x21, -0x7F, 0xEA, 0x32, 0x76, 0xF3, 0x5A, 0xFA, 0x8D, 0x36, 0x87, 0x34, 0x63, 0xBC, 0x20, 0xDC, 0x03, -0xC8, 0x5E, 0x1D, 0xCD, 0x2B, 0xF1, 0x91, 0xD6, 0xF7, 0xB4, 0x05, 0x23, 0x78, 0x2D, 0x44, 0x67, -0xD9, 0x83, 0x01, 0x9B, 0xA4, 0x41, 0x78, 0x12, 0x7B, 0x90, 0xF6, 0x40, 0x1B, 0x88, 0xBA, 0x2A, -0xE8, 0xAC, 0xA5, 0xF5, 0xB4, 0xB4, 0x3D, 0xDD, 0x1C, 0x58, 0x8E, 0xBA, 0x0C, 0xE7, 0x9F, 0x07, -0xCE, 0xCE, 0x4D, 0xE1, 0xD9, 0x8A, 0x00, 0x8F, 0x72, 0x0D, 0xAE, 0x97, 0x9C, 0x6E, 0xFB, 0x13, -0x2E, 0x0A, 0x25, 0xCB, 0x1A, 0xD2, 0x68, 0xB5, 0xB6, 0xA9, 0xA2, 0x93, 0x90, 0xE0, 0xBA, 0x1A, -0x94, 0x06, 0xE4, 0xE8, 0x1C, 0x59, 0xC3, 0xC0, 0x3B, 0x72, 0xB2, 0x1A, 0x47, 0x46, 0x15, 0x5D, -0x7F, 0x94, 0x14, 0xED, 0x87, 0x33, 0x71, 0x5C, 0xD0, 0x73, 0x9B, 0x2A, 0x0E, 0x33, 0x3B, 0xAC, -0x47, 0xEE, 0x36, 0x73, 0x18, 0x3B, 0x41, 0xA2, 0x13, 0xB9, 0xB1, 0xD1, 0x4A, 0xA7, 0xE1, 0x55, -0x4B, 0x6A, 0x92, 0x8B, 0xD7, 0xA7, 0x09, 0x5C, 0xF9, 0x26, 0x18, 0x6E, 0xC5, 0x91, 0xB4, 0x2D, -0x3E, 0x90, 0xD5, 0x20, 0x90, 0x5F, 0x91, 0xFA, 0xB4, 0xB2, 0xC8, 0x19, 0x63, 0xAF, 0xFC, 0xA9, -0xD8, 0x54, 0x7C, 0xC6, 0x65, 0x72, 0x9D, 0xE1, 0x34, 0x7E, 0x00, 0x72, 0x81, 0xCD, 0x69, 0xE1, -0x1C, 0x38, 0xBE, 0x4B, 0x8D, 0xFC, 0x27, 0x38, 0x07, 0xCE, 0x6A, 0x52, 0xFC, 0xB5, 0x5A, 0xCC, -0xD3, 0x4A, 0xD5, 0xAD, 0xD7, 0xB2, 0x6E, 0x5C, 0xA3, 0x2E, 0xBF, 0xA6, 0xCD, 0xA7, 0x76, 0xDA, -0xB1, 0x96, 0xD0, 0x06, 0x60, 0xC4, 0xD3, 0x25, 0x65, 0xAF, 0x5D, 0x0A, 0xD7, 0xF4, 0xD0, 0xA4, -0x39, 0xB8, 0x24, 0xE1, 0xF3, 0x8D, 0x9C, 0x14, 0x8F, 0x5C, 0x00, 0x28, 0xED, 0xC3, 0x77, 0x0B, -0x4B, 0xA0, 0xF7, 0x99, 0x93, 0x8D, 0x26, 0x1C, 0x8D, 0x10, 0xC2, 0x61, 0x37, 0xAF, 0x6D, 0xB0, -0xD4, 0x8E, 0x65, 0x62, 0xC3, 0xF7, 0x41, 0x01, 0x90, 0x24, 0xD6, 0x3B, 0xF9, 0x9C, 0xCA, 0xE0, -0x06, 0xB1, 0xE5, 0x06, 0x30, 0x76, 0xB6, 0x8A, 0x9C, 0xD6, 0x4C, 0x8F, 0x00, 0xD1, 0x01, 0xA4, -0x8F, 0xDA, 0x5D, 0x8E, 0xF8, 0xE6, 0xC1, 0x92, 0xA8, 0xA0, 0xEA, 0x23, 0x35, 0x87, 0xE5, 0x7E, -0xBE, 0x55, 0xCB, 0xFD, 0x70, 0x97, 0xBC, 0x4B, 0x55, 0xA5, 0xFB, 0xA4, 0xD0, 0x9C, 0x8F, 0x19, -0x88, 0x34, 0xED, 0x0C, 0xD4, 0x4E, 0x69, 0xA4, 0x30, 0x9C, 0xAC, 0x55, 0xDD, 0x4F, 0xDC, 0x34, -0xDC, 0xE2, 0xF5, 0xBC, 0xC5, 0x08, 0xBB, 0xFC, 0xCC, 0x5B, 0x50, 0xFE, 0x89, 0x51, 0x65, 0xA6, -0x65, 0xBC, 0x0E, 0x07, 0xF3, 0x54, 0xE8, 0xA7, 0x51, 0x32, 0x23, 0x63, 0xCE, 0xA7, 0xD7, 0x62, -0x7D, 0x3A, 0x3D, 0x17, 0xEC, 0x04, 0x61, 0x34, 0x26, 0x1D, 0x4E, 0x6A, 0x48, 0x2B, 0x17, 0xB2, -0x3F, 0x43, 0x46, 0xF0, 0x7A, 0x25, 0x0E, 0x29, 0x66, 0xB4, 0x93, 0x6F, 0x5C, 0xF0, 0xD1, 0x2B, -0x0E, 0x9C, 0xDC, 0x2D, 0x4C, 0xD3, 0x35, 0x85, 0x4A, 0x5F, 0x9A, 0x33, 0x7A, 0xCC, 0x6F, 0xFB, -0x5E, 0x84, 0xFE, 0xF4, 0xE1, 0x5C, 0x6A, 0x67, 0x99, 0x45, 0x4D, 0xAC, 0xF4, 0x61, 0xA2, 0xEB, -0xC5, 0xE0, 0x1F, 0xFD, 0x7E, 0xE4, 0x44, 0x60, 0x55, 0xF5, 0x8B, 0xF6, 0x03, 0xF3, 0x00, 0x19, -0xE6, 0xE0, 0x6E, 0x9A, 0x59, 0xA4, 0xFE, 0xAB, 0xDA, 0x53, 0x42, 0x7B, 0x07, 0xFD, 0x94, 0xC0, -0x82, 0x7C, 0xC4, 0x09, 0x94, 0x00, 0x77, 0x10, 0x43, 0x49, 0x31, 0x0B, 0xF4, 0xD7, 0x61, 0x16, -0xB9, 0x4F, 0x92, 0xD0, 0xE0, 0x45, 0x11, 0x44, 0x41, 0x8F, 0xAD, 0x25, 0xB5, 0xCC, 0xB5, 0x31, -0x32, 0x85, 0xDB, 0x70, 0x32, 0x86, 0x86, 0x78, 0x81, 0x56, 0xD7, 0xE4, 0x6D, 0x61, 0x82, 0x2C, -0xAF, 0x85, 0xF4, 0x7B, 0x9B, 0x94, 0x58, 0xD2, 0x1B, 0x29, 0x1E, 0x03, 0x78, 0x7F, 0xA1, 0x2D, -0xCD, 0x68, 0x4D, 0xC5, 0x3D, 0xAD, 0x68, 0x01, 0x8E, 0xB9, 0xFF, 0xE6, 0xD1, 0xA6, 0x20, 0x37, -0xA8, 0x6F, 0xB6, 0x43, 0xCD, 0xBD, 0x3E, 0x44, 0x0B, 0x59, 0x76, 0x36, 0xAF, 0xEF, 0xF8, 0xC6, -0x90, 0xED, 0x1E, 0xB5, 0xA4, 0x3B, 0xD4, 0xC7, 0xD4, 0x60, 0xFC, 0x62, 0xFD, 0xC3, 0x7E, 0xB1, -0x9E, 0xF9, 0x45, 0xE8, 0x2C, 0xAA, 0xA9, 0x9D, 0x04, 0x2C, 0xA7, 0x78, 0x64, 0xB9, 0x35, 0x1C, -0xB7, 0x83, 0x33, 0x6C, 0xC7, 0x50, 0xDE, 0x5A, 0x91, 0xDA, 0xD9, 0xC9, 0xEB, 0xCB, 0xEF, 0xE7, -0x9C, 0x16, 0xD8, 0xF9, 0x8D, 0x35, 0x20, 0x1A, 0xF9, 0xB2, 0xFA, 0x83, 0x35, 0x38, 0x67, 0x53, -0x5D, 0xD2, 0xA9, 0x62, 0x60, 0xB0, 0x46, 0x97, 0x6A, 0x20, 0xE8, 0x5D, 0x49, 0xF6, 0x49, 0x81, -0x0C, 0xAD, 0x6B, 0x0F, 0x5F, 0x30, 0xAD, 0x55, 0xD8, 0x98, 0xC5, 0x23, 0xFB, 0x36, 0x01, 0xE7, -0xDE, 0xE6, 0xB7, 0x15, 0xBB, 0x76, 0xB8, 0xA1, 0xC7, 0xBE, 0xC6, 0xFB, 0x49, 0xEB, 0x94, 0xB8, -0x20, 0x14, 0x9A, 0x00, 0x18, 0x86, 0x66, 0xCE, 0xCE, 0xC2, 0xB6, 0xB4, 0x1E, 0xD6, 0x44, 0x2E, -0x2F, 0x25, 0x8F, 0xF7, 0x8D, 0x65, 0x22, 0x61, 0x70, 0x0A, 0x45, 0xE1, 0x89, 0x19, 0xE4, 0xAF, -0x9F, 0xE4, 0x5E, 0x72, 0x0C, 0xA2, 0x0D, 0x32, 0x74, 0x25, 0x67, 0xC7, 0x6E, 0x74, 0x99, 0xF7, -0x69, 0xB7, 0x14, 0xAC, 0xB3, 0x5C, 0xDF, 0xC0, 0xFD, 0x9D, 0x5C, 0x52, 0x01, 0xC9, 0x67, 0x64, -0x16, 0xE8, 0x13, 0x1D, 0x64, 0x51, 0x97, 0x7E, 0x4D, 0x2E, 0xA4, 0xFE, 0x8A, 0x3A, 0x13, 0x54, -0x81, 0xFA, 0x0C, 0x38, 0x18, 0xC1, 0x79, 0x59, 0xEE, 0x12, 0x03, 0x0E, 0x0D, 0x78, 0x57, 0xE4, -0xD0, 0x19, 0x1B, 0x70, 0x64, 0xC0, 0x38, 0x2E, 0xF3, 0xCD, 0xCE, 0x80, 0xE3, 0x91, 0x48, 0x88, -0xB3, 0x68, 0x04, 0xEF, 0x46, 0x70, 0x82, 0xE4, 0x54, 0xD3, 0x9F, 0x38, 0x7C, 0xB8, 0x51, 0xDF, -0x7E, 0x26, 0xE1, 0xDE, 0xF2, 0x0D, 0xE9, 0x1F, 0xCF, 0xB5, 0xA0, 0x9C, 0x5D, 0x1D, 0x51, 0x4D, -0x37, 0xCC, 0xEA, 0x09, 0x29, 0xD5, 0x95, 0x0F, 0x8E, 0x3C, 0xCB, 0x39, 0xD0, 0x7B, 0xBB, 0xF3, -0x3E, 0x07, 0xE6, 0x56, 0x73, 0xB3, 0x1F, 0xAB, 0xCB, 0xE5, 0x3A, 0xB1, 0x33, 0xBC, 0x4C, 0x41, -0x55, 0x9E, 0xAB, 0x36, 0xA0, 0x09, 0xFD, 0xDD, 0x16, 0xCE, 0xBE, 0xFE, 0x4E, 0x43, 0xDE, 0x59, -0x8C, 0x6C, 0xF4, 0xA2, 0x7B, 0x29, 0xAA, 0x75, 0x77, 0xB6, 0x8B, 0xEF, 0xB3, 0xBE, 0x43, 0x5C, -0x59, 0x4F, 0x6D, 0x35, 0x0E, 0xAB, 0xCE, 0xC2, 0xD3, 0x5D, 0xAD, 0xBE, 0x15, 0x2F, 0x4F, 0x21, -0x7D, 0x30, 0x54, 0x03, 0x02, 0xA6, 0x8D, 0xA8, 0xBC, 0xBC, 0xA2, 0x75, 0xF1, 0x11, 0x36, 0xEE, -0x44, 0xC7, 0x5E, 0x88, 0x9B, 0x9F, 0xA3, 0xD7, 0x4B, 0xB1, 0x6B, 0x75, 0x1F, 0xAF, 0xF2, 0xBE, -0x6A, 0xF1, 0x55, 0xBB, 0xED, 0xE3, 0x5C, 0x25, 0xC5, 0x95, 0x7E, 0x49, 0x78, 0x89, 0xBD, 0x06, -0x6C, 0x7C, 0xAA, 0x08, 0xB8, 0x3E, 0xE8, 0xB5, 0xFF, 0xAF, 0x9B, 0x75, 0xA3, 0x0F, 0xD5, 0x49, -0x07, 0xE1, 0xD2, 0x76, 0x70, 0x0D, 0x21, 0xC6, 0xF3, 0x36, 0x99, 0xFA, 0x86, 0x22, 0x22, 0xEE, -0x35, 0x49, 0xA9, 0x00, 0xA5, 0xE4, 0xD2, 0x26, 0xF7, 0xBA, 0x73, 0x0D, 0xC6, 0x3A, 0x35, 0x44, -0x96, 0xD5, 0x6E, 0x6A, 0x4F, 0x1B, 0xCB, 0x25, 0x7A, 0x52, 0x60, 0x74, 0x2A, 0xB6, 0x4A, 0x88, -0x3A, 0xAD, 0x66, 0x38, 0xED, 0x82, 0xB4, 0x67, 0x1A, 0x10, 0x85, 0xDE, 0x77, 0x3E, 0x5A, 0x45, -0x19, 0x06, 0xAF, 0x31, 0x04, 0xCD, 0x4B, 0x80, 0xC2, 0x49, 0x62, 0xFB, 0xC0, 0xEF, 0x12, 0xF7, -0x17, 0x18, 0x6D, 0x6A, 0x8F, 0x0A, 0x72, 0x6C, 0x4D, 0x3D, 0x33, 0x7D, 0x6B, 0x59, 0x93, 0x9B, -0x91, 0x34, 0x56, 0xD5, 0xE3, 0xEF, 0x73, 0x2B, 0x68, 0x79, 0xF7, 0xCC, 0x75, 0xCA, 0x38, 0x92, -0x26, 0x34, 0x8B, 0x91, 0xD0, 0x28, 0x20, 0x9D, 0x0E, 0x6E, 0xDF, 0x40, 0x1F, 0xAA, 0xB8, 0xA3, -0x7E, 0x11, 0x34, 0x12, 0x63, 0xE9, 0x45, 0xCA, 0x48, 0xA8, 0x6F, 0x1B, 0xF7, 0xD7, 0x0A, 0x24, -0xD4, 0xFE, 0x47, 0xA0, 0x0A, 0x5E, 0x39, 0x3E, 0xCD, 0x1C, 0xE2, 0x45, 0x38, 0x84, 0x8B, 0xC8, -0x9D, 0xDA, 0x87, 0x5E, 0x0F, 0xCA, 0x2A, 0x3D, 0x9D, 0xC7, 0xF6, 0x76, 0xEA, 0xEC, 0x97, 0x34, -0xFA, 0x1D, 0x5F, 0x87, 0xDB, 0x91, 0x7D, 0x9F, 0x94, 0x3E, 0x00, 0x06, 0x0F, 0xEA, 0x1F, 0x64, -0x00, 0x52, 0x96, 0x9D, 0xD5, 0xC5, 0xEB, 0x4B, 0xED, 0x6B, 0xCF, 0xD9, 0x2E, 0x1B, 0x9C, 0xA9, -0x7B, 0x54, 0x4D, 0xFC, 0xA4, 0x9D, 0x41, 0x39, 0xE0, 0xE8, 0x35, 0xE1, 0x50, 0x74, 0xAE, 0x98, -0x37, 0xB0, 0xD0, 0x59, 0x1D, 0xD0, 0xBC, 0xDD, 0x08, 0xEC, 0x4B, 0x59, 0xA8, 0xB7, 0x56, 0x60, -0xD9, 0x5A, 0x59, 0x57, 0x27, 0x94, 0x57, 0x22, 0x25, 0x6E, 0x2F, 0x75, 0x06, 0xD7, 0xE8, 0x9A, -0x60, 0x0E, 0x57, 0x5B, 0x51, 0x0D, 0x7C, 0x9F, 0xE4, 0xD1, 0x32, 0x8E, 0xA3, 0x68, 0x2D, 0x47, -0x81, 0x9E, 0x60, 0x94, 0x39, 0x0E, 0x36, 0x72, 0xBC, 0xE9, 0xBF, 0x6E, 0xB4, 0x43, 0x75, 0x64, -0x7D, 0x77, 0x32, 0x3D, 0xCC, 0xB2, 0x67, 0xD2, 0xA7, 0xD2, 0x8E, 0xD5, 0x3B, 0xBC, 0x1F, 0xB5, -0x13, 0xE1, 0xC1, 0xA8, 0xC6, 0x20, 0x08, 0xC9, 0x16, 0xBA, 0x28, 0x40, 0x1B, 0x1F, 0x21, 0xBD, -0x9B, 0x7E, 0x86, 0x9C, 0x3B, 0x1D, 0x92, 0x25, 0x63, 0xAA, 0xCF, 0x31, 0x50, 0xE4, 0xE8, 0x3C, -0x0F, 0x76, 0xF1, 0x32, 0x48, 0x53, 0x9F, 0xA9, 0xFE, 0x0B, 0x49, 0xB2, 0x96, 0xCB, 0xE7, 0x8A, -0xE1, 0x3D, 0x80, 0x2B, 0x69, 0x97, 0xB7, 0x63, 0x89, 0xFB, 0x67, 0x8C, 0x79, 0xA4, 0x24, 0xEE, -0xF4, 0xE6, 0xF0, 0x03, 0xEF, 0x9B, 0xEA, 0xAD, 0x64, 0xBC, 0x13, 0xB8, 0x3F, 0xF0, 0xB8, 0xF9, -0x9D, 0x2C, 0x85, 0x5C, 0x99, 0xE3, 0xE6, 0x82, 0xDB, 0x0E, 0x9A, 0x26, 0x38, 0x30, 0xA4, 0x0B, -0xD0, 0x8B, 0xF8, 0xE8, 0x11, 0xE8, 0xC5, 0x75, 0x9D, 0xEA, 0x4C, 0xBC, 0xA1, 0x67, 0xEF, 0xA1, -0x9C, 0xB9, 0xAE, 0x79, 0xFD, 0x6A, 0x73, 0x4E, 0xA0, 0x0A, 0xE0, 0xA6, 0x70, 0x3E, 0xA6, 0x7B, -0xD6, 0x66, 0x0B, 0x4A, 0x74, 0x3B, 0xDD, 0x56, 0xA9, 0x40, 0x81, 0x96, 0x8B, 0xD4, 0xF3, 0x30, -0x19, 0x52, 0xCF, 0x90, 0x8C, 0xC2, 0x77, 0xF9, 0x7D, 0x8A, 0x71, 0xD2, 0x53, 0x1C, 0x4A, 0xEA, -0xD4, 0xAC, 0xF7, 0xD7, 0x37, 0x7D, 0x6E, 0x7D, 0xDB, 0x4B, 0x4B, 0x96, 0x9F, 0xDB, 0x6E, 0xBC, -0x76, 0x3D, 0xB6, 0xB9, 0x53, 0x56, 0x95, 0xFF, 0x02, 0x39, 0x7E, 0xE4, 0xC5, 0x9A, 0x16, 0x00, -0x00 +0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xCD, 0x18, 0xDB, 0x8E, 0xA3, 0x3A, +0xF2, 0x57, 0x90, 0x5A, 0xAD, 0x69, 0xCE, 0x86, 0x2C, 0xD7, 0x5C, 0x1C, 0x1D, 0xE9, 0x48, 0xFB, +0xBA, 0xFB, 0x05, 0xA3, 0xD9, 0x91, 0x01, 0x13, 0x7C, 0x9A, 0xE0, 0x08, 0x9C, 0x4B, 0x0F, 0x62, +0xBF, 0x7D, 0xAB, 0x8C, 0x0D, 0x86, 0x74, 0x66, 0xE7, 0x65, 0xA5, 0x33, 0xA8, 0x27, 0xBE, 0x94, +0xAB, 0xCA, 0x75, 0x2F, 0xAF, 0x2B, 0x71, 0xBC, 0x72, 0x76, 0x63, 0xCD, 0x4A, 0xA6, 0x22, 0xFF, +0xE8, 0xC4, 0x95, 0x35, 0x45, 0x25, 0x6E, 0x84, 0x5E, 0xA4, 0xE8, 0xD7, 0x99, 0xA8, 0x25, 0xE5, +0x35, 0x6C, 0xAF, 0x6B, 0xD1, 0x9C, 0x68, 0xE5, 0xC8, 0xBC, 0x2B, 0x19, 0x3F, 0x96, 0x92, 0x44, +0xFE, 0xF9, 0x3E, 0x83, 0x18, 0x87, 0x0E, 0xAF, 0xCF, 0x17, 0x49, 0xB2, 0x92, 0x65, 0xEF, 0x2C, +0xFF, 0xCF, 0x5A, 0x0D, 0x4E, 0xB4, 0x79, 0x27, 0xB4, 0x90, 0x33, 0xC8, 0xD0, 0x1E, 0x2F, 0x8F, +0xE5, 0x42, 0xDA, 0x87, 0xDA, 0x4B, 0xDA, 0xE5, 0xBC, 0x3D, 0x57, 0xF4, 0x83, 0xA4, 0x95, 0xC8, +0xDE, 0xFB, 0xF5, 0x89, 0xD5, 0x97, 0x55, 0x19, 0x74, 0xB7, 0x92, 0x4B, 0xE6, 0xB5, 0x67, 0x9A, +0x31, 0x52, 0x8B, 0x5B, 0x43, 0xCF, 0x3D, 0x69, 0x84, 0x90, 0x9D, 0xE7, 0xA5, 0xC7, 0x80, 0xBC, +0x30, 0xC6, 0x0E, 0x38, 0x0C, 0xC9, 0x4B, 0x46, 0xF1, 0x53, 0xB3, 0x88, 0xBC, 0xF8, 0xDB, 0x1C, +0x86, 0x19, 0x80, 0x14, 0x45, 0x81, 0x8B, 0x39, 0x8C, 0x5B, 0x5E, 0x81, 0x14, 0x14, 0x48, 0x0C, +0x67, 0x77, 0xF8, 0xA9, 0x59, 0x42, 0x5E, 0xF2, 0xB4, 0x28, 0x7C, 0x7F, 0x9B, 0xE0, 0x29, 0xC0, +0x16, 0xC7, 0xB1, 0xDA, 0xD9, 0x18, 0x04, 0x19, 0x22, 0xF5, 0x7D, 0xB5, 0xB8, 0x25, 0x2F, 0x69, +0x8E, 0x9F, 0x9A, 0xED, 0x60, 0x23, 0xDC, 0xA7, 0x11, 0x1E, 0xC8, 0x62, 0xC2, 0xEB, 0x92, 0x35, +0x5C, 0xE2, 0x24, 0x31, 0x7C, 0x00, 0xAF, 0xBE, 0x35, 0x46, 0xAE, 0x76, 0xF8, 0x21, 0xD0, 0x48, +0xA1, 0x11, 0xC4, 0xCF, 0xD9, 0x11, 0x46, 0xFC, 0x4A, 0x90, 0x90, 0x3C, 0x9F, 0x42, 0x12, 0xC4, +0xA8, 0x8D, 0xAF, 0x39, 0x95, 0xD4, 0x93, 0x25, 0x3B, 0xB1, 0xDF, 0x73, 0x10, 0xDD, 0x37, 0x23, +0x00, 0xC3, 0x27, 0xB0, 0x1C, 0x32, 0xFC, 0x46, 0x01, 0xA8, 0x7F, 0xBB, 0x44, 0x4B, 0x21, 0x2F, +0xF0, 0x33, 0x82, 0x78, 0x49, 0x62, 0xFC, 0x8C, 0x24, 0xAC, 0x93, 0xC0, 0x72, 0x92, 0x44, 0xBE, +0xC2, 0x8A, 0x72, 0xD0, 0xD2, 0x43, 0x39, 0x84, 0x61, 0xA8, 0xE5, 0x60, 0x16, 0xB7, 0x86, 0x4C, +0xE4, 0x1B, 0x49, 0x44, 0x3E, 0x1C, 0xCF, 0x07, 0x49, 0xBC, 0x64, 0x11, 0x7E, 0x5A, 0x12, 0xE3, +0x64, 0x10, 0x46, 0x1C, 0x6F, 0xFC, 0x2D, 0x1D, 0xE5, 0x31, 0x4E, 0x51, 0x1E, 0x23, 0x28, 0x88, +0x24, 0xD8, 0x4D, 0x42, 0x09, 0xFA, 0x3F, 0x4E, 0x2C, 0xE7, 0xD4, 0x79, 0x3B, 0x37, 0xAC, 0x60, +0x4D, 0xEB, 0x65, 0xA2, 0x12, 0x8D, 0xD7, 0x66, 0x28, 0x18, 0x82, 0x82, 0x71, 0x3B, 0x5B, 0x54, +0x68, 0xEB, 0x7F, 0x7D, 0x51, 0xA5, 0xC7, 0xFD, 0x78, 0xE4, 0xFF, 0x22, 0xB5, 0xFE, 0x37, 0x07, +0xBC, 0x47, 0xBE, 0x15, 0xE0, 0x91, 0x2B, 0xEA, 0x82, 0x6B, 0x56, 0xA2, 0x65, 0xA9, 0xAC, 0x57, +0xEB, 0x9C, 0x5F, 0xBF, 0x83, 0x0B, 0xDE, 0x78, 0xCE, 0xC0, 0xDF, 0x56, 0xE5, 0x66, 0x25, 0xD9, +0x5D, 0xD2, 0x86, 0xD1, 0x4E, 0xC9, 0x96, 0x5C, 0x69, 0xF3, 0x86, 0x7C, 0xB9, 0xFD, 0xA3, 0xB7, +0xCF, 0xFC, 0xB8, 0x43, 0x7F, 0x67, 0xB5, 0x24, 0x5F, 0xBE, 0x1C, 0x8C, 0x37, 0xD7, 0xA2, 0x66, +0x87, 0xB3, 0x68, 0xB9, 0xE4, 0xA2, 0x26, 0x34, 0x6D, 0x45, 0x75, 0x91, 0xAC, 0x5F, 0xA7, 0x17, +0x29, 0x05, 0x50, 0x47, 0x37, 0xEF, 0x90, 0xA0, 0x97, 0xB3, 0x4C, 0x34, 0x54, 0x81, 0xE1, 0xA1, +0x5E, 0x31, 0x56, 0x69, 0x08, 0x88, 0x5B, 0x54, 0x92, 0x8A, 0x15, 0xB2, 0xFF, 0xAD, 0x4B, 0xC5, +0xDD, 0x6B, 0xF9, 0x0F, 0x5E, 0x1F, 0x49, 0x2A, 0x9A, 0x9C, 0x35, 0x1E, 0xAC, 0x1C, 0xF0, 0x6A, +0x5E, 0x41, 0x4F, 0xBC, 0xFA, 0x20, 0x2D, 0xAD, 0x5B, 0xAF, 0x05, 0x27, 0x2C, 0x86, 0x65, 0x80, +0x66, 0x24, 0x08, 0xCF, 0xF2, 0x00, 0xDC, 0x1E, 0x79, 0x4D, 0xFC, 0x1E, 0x42, 0x8B, 0xB5, 0xB5, +0x39, 0xCB, 0xBE, 0x8C, 0xBA, 0x94, 0x66, 0xEF, 0xC7, 0x46, 0x5C, 0xEA, 0xDC, 0xB3, 0xEF, 0x0E, +0x16, 0xE3, 0x9A, 0x93, 0x5E, 0x72, 0xBE, 0x1F, 0xCE, 0x34, 0xCF, 0x91, 0xFC, 0x06, 0x1C, 0xB3, +0xDC, 0xD8, 0x88, 0x7C, 0x40, 0x04, 0xA6, 0xA9, 0x96, 0x48, 0xC0, 0x4E, 0xCE, 0x49, 0xD4, 0x42, +0x45, 0xAF, 0xD5, 0x38, 0x32, 0xB7, 0xEF, 0x06, 0xEE, 0xC1, 0xD3, 0xF5, 0x35, 0x1A, 0x9A, 0xF3, +0x4B, 0x4B, 0x90, 0xC2, 0x4C, 0xF4, 0x9B, 0x91, 0xBA, 0x4D, 0x1C, 0xC6, 0x4E, 0x90, 0xA8, 0x40, +0x6D, 0xD4, 0xB2, 0x52, 0x61, 0x76, 0xD5, 0xB2, 0x8A, 0x65, 0xF2, 0xF9, 0x6D, 0x02, 0xB7, 0x7F, +0x91, 0x82, 0xB6, 0xF2, 0xC4, 0xDA, 0x96, 0x1E, 0xD9, 0x4A, 0x33, 0xB4, 0x2E, 0x59, 0x75, 0x5E, +0x59, 0xE8, 0x8C, 0x7E, 0x57, 0xEB, 0x29, 0x99, 0x94, 0xCD, 0x8C, 0xCA, 0x64, 0x2D, 0xFA, 0x36, +0xEB, 0x00, 0xF8, 0x02, 0x35, 0xF3, 0xDC, 0x39, 0x36, 0xF4, 0xA3, 0x57, 0xC0, 0x5F, 0xC1, 0x1E, +0x68, 0x5A, 0xB1, 0xFC, 0xDB, 0x6A, 0x31, 0x27, 0x25, 0xE6, 0xA5, 0xE7, 0xBC, 0x6E, 0x5D, 0x23, +0xAE, 0x75, 0xC5, 0xEB, 0x77, 0x65, 0xA7, 0x63, 0xAE, 0xE0, 0x35, 0xAC, 0x31, 0x4F, 0xA5, 0x8C, +0x83, 0xB2, 0x22, 0x5A, 0xF1, 0x63, 0x4D, 0x32, 0xB0, 0x42, 0xD6, 0xCC, 0x0F, 0x36, 0x2C, 0x7F, +0xA4, 0x02, 0x8B, 0xBD, 0x7D, 0xF9, 0x6E, 0xA1, 0x09, 0xFF, 0x75, 0x66, 0x57, 0xA3, 0x0A, 0x47, +0x25, 0x84, 0x70, 0xD9, 0xED, 0x73, 0x1D, 0x2C, 0xA5, 0x63, 0xA9, 0xD8, 0xD0, 0x7D, 0x10, 0x00, +0xC4, 0x85, 0xCD, 0xBE, 0x9F, 0x61, 0x69, 0x2F, 0x27, 0x40, 0xFD, 0x31, 0x61, 0xD3, 0xE6, 0x10, +0x5B, 0xE6, 0x00, 0x63, 0x67, 0x87, 0x68, 0x95, 0x84, 0x06, 0x00, 0x70, 0x0C, 0x20, 0x71, 0x52, +0x66, 0x73, 0xA2, 0x77, 0x0F, 0xB6, 0x64, 0x09, 0xD9, 0xDD, 0xC7, 0x39, 0x6C, 0x0F, 0xF3, 0x1D, +0x6E, 0x0F, 0xC3, 0x7D, 0xF2, 0xDA, 0x63, 0x36, 0x1E, 0xE2, 0x41, 0x7D, 0x39, 0xA5, 0xC0, 0xDA, +0x74, 0x32, 0xC0, 0x93, 0xBD, 0xE1, 0xC2, 0x50, 0xB2, 0x76, 0x55, 0xDD, 0x70, 0x57, 0xEB, 0x16, +0xAD, 0xCF, 0x4B, 0x89, 0xB0, 0xCB, 0x2E, 0x4D, 0x0B, 0x4A, 0x38, 0x0B, 0x8E, 0xEA, 0x5A, 0xBA, +0xAA, 0xBE, 0x98, 0x87, 0x5E, 0x4F, 0xA2, 0x64, 0x86, 0xC6, 0xDC, 0x4F, 0xED, 0xC5, 0xEA, 0x76, +0x6A, 0x2E, 0xC5, 0x19, 0xDC, 0x69, 0x8C, 0x37, 0x0D, 0xAB, 0x20, 0xA2, 0x5C, 0xD9, 0xE1, 0x02, +0xC1, 0xC0, 0x1B, 0x84, 0xA9, 0xA3, 0xCB, 0xA8, 0xAF, 0xB5, 0x31, 0xC5, 0x47, 0xEB, 0x38, 0x36, +0xEC, 0xC3, 0x82, 0x34, 0xD5, 0x51, 0x88, 0xF2, 0x52, 0x94, 0xFD, 0xC7, 0xD0, 0x76, 0x18, 0x58, +0x18, 0x6E, 0x1F, 0xCE, 0xB9, 0x76, 0x96, 0x01, 0xD4, 0xF8, 0xCC, 0xE0, 0x2E, 0x2A, 0x55, 0x68, +0x3B, 0x19, 0xCE, 0xFB, 0x4E, 0x04, 0x5A, 0xC5, 0x3F, 0xFF, 0xA0, 0x89, 0x07, 0xBE, 0x21, 0x0E, +0x66, 0xA7, 0x88, 0x45, 0xF8, 0xDB, 0x40, 0xC4, 0x2B, 0xA0, 0x8C, 0x83, 0xDA, 0x48, 0x52, 0xC9, +0xDE, 0xE2, 0x04, 0xA2, 0xBF, 0xAB, 0xD9, 0x40, 0x2E, 0x66, 0x0E, 0xFF, 0xDC, 0xDD, 0x22, 0xF7, +0x93, 0x60, 0xA4, 0xAD, 0x28, 0x02, 0x6F, 0x18, 0xA0, 0x15, 0xA7, 0x96, 0xBA, 0xB6, 0x86, 0xA7, +0x70, 0x17, 0x4E, 0xCA, 0x50, 0x2B, 0x5E, 0xA0, 0xC4, 0x35, 0x59, 0x5B, 0x98, 0xF8, 0x96, 0xD5, +0x42, 0xE4, 0xBD, 0x4F, 0x42, 0x2C, 0xF8, 0x9D, 0xE5, 0x8F, 0x8E, 0x7C, 0xB8, 0xF2, 0x96, 0xA7, +0xBC, 0xE2, 0xF2, 0x83, 0x94, 0x3C, 0x07, 0xC3, 0x3C, 0xFC, 0xF0, 0x78, 0x9D, 0xB3, 0x3B, 0xA4, +0x36, 0xDB, 0xA0, 0xE6, 0x56, 0x1F, 0xFA, 0x0B, 0x5E, 0xF6, 0x36, 0xAD, 0x9F, 0xD8, 0x86, 0x8E, +0x7A, 0x8F, 0x52, 0x52, 0xD5, 0xE6, 0x63, 0x88, 0x30, 0x76, 0xB1, 0xF9, 0x65, 0xBB, 0xD8, 0xCC, +0xEC, 0x22, 0x74, 0x16, 0x89, 0xD4, 0x0E, 0x06, 0x96, 0x51, 0x3C, 0x92, 0xDC, 0x19, 0x8A, 0x3B, +0x6D, 0x0C, 0xBB, 0xD1, 0x95, 0x77, 0x96, 0xA7, 0x76, 0x76, 0x10, 0xFB, 0xF2, 0xCF, 0x4B, 0xC6, +0x73, 0xEA, 0xFC, 0x43, 0xD4, 0xC0, 0x1A, 0xFB, 0xB2, 0xFA, 0x97, 0xA8, 0x69, 0x26, 0xA6, 0xFC, +0xA4, 0x42, 0x85, 0x26, 0xB0, 0xF1, 0xAF, 0xA5, 0x46, 0xE8, 0xDD, 0x58, 0xFA, 0xCE, 0x01, 0x0D, +0xAF, 0x2A, 0x8F, 0x5E, 0x29, 0xAF, 0xD0, 0x6D, 0xCC, 0xE6, 0x49, 0xFC, 0x98, 0x16, 0xE7, 0xD6, +0xB6, 0x6E, 0x4B, 0x71, 0xEB, 0x68, 0xCD, 0x4F, 0x43, 0x7A, 0x5F, 0x27, 0xAD, 0x53, 0xD0, 0x9C, +0x71, 0xC8, 0xFF, 0x30, 0x0C, 0xCD, 0x5C, 0x5C, 0xA4, 0xAD, 0x69, 0x35, 0xAC, 0x58, 0xBF, 0x6C, +0x3E, 0x1E, 0xFB, 0x8A, 0x65, 0x20, 0x11, 0x70, 0x0B, 0xC4, 0xF0, 0x89, 0x1A, 0xFA, 0x3F, 0xDE, +0xD9, 0x47, 0xD1, 0x50, 0x60, 0x4D, 0xF3, 0xD0, 0x15, 0x8D, 0x38, 0x75, 0xA3, 0xC9, 0xBC, 0x4E, +0xA7, 0x7B, 0x29, 0x3A, 0xCB, 0xF4, 0xCD, 0xFA, 0x7A, 0xDF, 0x2F, 0xB1, 0x00, 0xE7, 0x33, 0x34, +0x0B, 0xF0, 0x09, 0x8F, 0x6F, 0x61, 0xEF, 0xD7, 0x15, 0xBB, 0xB2, 0xEA, 0xBB, 0xDF, 0x19, 0xA7, +0x0A, 0xF0, 0x33, 0xCB, 0xC1, 0xB8, 0x9C, 0x15, 0xC5, 0x3E, 0x31, 0xCB, 0xA1, 0x59, 0xDE, 0xE7, +0x19, 0x54, 0xC0, 0x66, 0x39, 0x32, 0xCB, 0x34, 0x2E, 0xB2, 0xED, 0xDE, 0x2C, 0xC7, 0x23, 0x92, +0x90, 0xA6, 0xD1, 0xB8, 0xBC, 0x1F, 0x97, 0x13, 0xBF, 0x9F, 0x72, 0xFB, 0x27, 0x06, 0x1F, 0x6E, +0xF1, 0x3B, 0xCC, 0x38, 0x3C, 0x58, 0xB6, 0x01, 0x8D, 0xDA, 0xA5, 0x92, 0xBC, 0x11, 0x37, 0x47, +0x96, 0x53, 0x27, 0x59, 0x7E, 0x82, 0x0A, 0xAB, 0x6F, 0x6D, 0xC8, 0xB3, 0x98, 0x03, 0x35, 0xB6, +0x3B, 0xAF, 0x77, 0x60, 0x6E, 0x15, 0x39, 0x87, 0x31, 0xBB, 0x5C, 0x6F, 0x13, 0x39, 0x43, 0xCB, +0x24, 0x56, 0xB4, 0x5C, 0x3C, 0xE0, 0x4F, 0xE0, 0xAF, 0x36, 0x73, 0x76, 0x9B, 0x3B, 0x0D, 0x9B, +0xCE, 0x22, 0x64, 0x83, 0xE7, 0xDD, 0x53, 0x56, 0xAD, 0x1E, 0xD9, 0x4E, 0xBE, 0x9F, 0xD5, 0x1F, +0xF2, 0x26, 0x06, 0x6C, 0xAB, 0x71, 0x58, 0x76, 0x16, 0x9C, 0x2A, 0x68, 0x55, 0xF7, 0xBB, 0xBC, +0x45, 0xBF, 0x06, 0x45, 0xD5, 0xC0, 0x20, 0xA9, 0x65, 0xE9, 0x65, 0x25, 0xAF, 0xF2, 0xB7, 0xB0, +0x76, 0x27, 0x3C, 0xF6, 0x46, 0x5C, 0xFF, 0x2D, 0x7A, 0xBE, 0x15, 0xBB, 0x56, 0x15, 0xF2, 0x2C, +0xEE, 0x63, 0x75, 0x8F, 0x95, 0xF6, 0x9A, 0x66, 0x18, 0x14, 0x57, 0xEA, 0xC5, 0xE0, 0x29, 0xF4, +0x06, 0xA0, 0xE9, 0xB9, 0x64, 0x60, 0xFA, 0x20, 0xD7, 0xE1, 0x57, 0x35, 0xEC, 0xD6, 0x38, 0x1C, +0x45, 0xBB, 0xC3, 0x42, 0x35, 0x5C, 0x2A, 0x12, 0xDA, 0x11, 0x66, 0xCC, 0x70, 0x9B, 0xE2, 0xA7, +0x33, 0x8A, 0xFC, 0xA8, 0x18, 0xE1, 0x12, 0x24, 0x94, 0xF5, 0x4F, 0x71, 0x3F, 0xAF, 0x6D, 0xD1, +0x78, 0x16, 0xF9, 0x44, 0x45, 0xDE, 0x31, 0x63, 0x1C, 0x54, 0xFF, 0x50, 0xD2, 0x5C, 0xDC, 0x20, +0xC1, 0xFA, 0x0E, 0x66, 0x54, 0x07, 0x8B, 0xD6, 0xE6, 0x98, 0xBE, 0xE1, 0x82, 0xEF, 0xFC, 0xDD, +0x01, 0x0F, 0x76, 0x6D, 0xEA, 0xDD, 0x98, 0x7E, 0x7D, 0x95, 0x10, 0x75, 0xDB, 0xA1, 0x9C, 0xDA, +0xAA, 0x78, 0xD1, 0xC8, 0xFB, 0x19, 0xA3, 0xFA, 0xDC, 0xC0, 0x1E, 0xF6, 0xF3, 0x58, 0xCF, 0x82, +0x70, 0x15, 0x52, 0xCC, 0x8A, 0x13, 0x62, 0x88, 0xB4, 0xA6, 0xE6, 0x41, 0x96, 0x87, 0x62, 0x6B, +0x04, 0x0E, 0x7F, 0x0E, 0x3D, 0x23, 0x80, 0xF0, 0x29, 0x6D, 0x56, 0x66, 0x10, 0x8E, 0x15, 0x73, +0x51, 0x31, 0x2B, 0x07, 0x9A, 0x97, 0x0A, 0xF3, 0x34, 0xE4, 0xDD, 0x49, 0x9B, 0x35, 0xA2, 0xAA, +0x0E, 0xC3, 0x0F, 0x1C, 0xD5, 0x59, 0x5B, 0x25, 0xC6, 0xAF, 0x59, 0x45, 0xDB, 0xF6, 0xDF, 0xBF, +0x6B, 0xAC, 0xDF, 0xC8, 0x98, 0x13, 0x46, 0xF0, 0xCE, 0x6E, 0xFC, 0x46, 0x46, 0x3A, 0xE4, 0x30, +0x89, 0x67, 0x55, 0x68, 0x00, 0x1D, 0xAA, 0xF2, 0x39, 0xC3, 0x22, 0xC2, 0xEC, 0x31, 0x45, 0x5B, +0xD9, 0xCF, 0xEA, 0xBF, 0x2C, 0xE3, 0x31, 0x49, 0xD6, 0xA8, 0x01, 0xCB, 0xFF, 0x99, 0x16, 0xA0, +0x95, 0xEE, 0xE9, 0xD0, 0x39, 0x2A, 0x47, 0xF3, 0xB8, 0x64, 0xA7, 0xD6, 0x54, 0x13, 0x33, 0x59, +0x68, 0x81, 0xC6, 0x98, 0xBB, 0xFF, 0xBC, 0xB4, 0x92, 0x17, 0x1F, 0x9E, 0xE9, 0x63, 0x35, 0xFC, +0x91, 0x9E, 0xC9, 0xCC, 0x7C, 0x5F, 0x27, 0xAE, 0x1D, 0x4D, 0x66, 0xBA, 0xD7, 0x16, 0x0D, 0xC4, +0x60, 0x0D, 0x1E, 0x8C, 0xC5, 0x1F, 0xCE, 0xCE, 0x7B, 0xEB, 0xD0, 0x54, 0x5D, 0x64, 0xBA, 0x84, +0x2A, 0xE9, 0xCE, 0xE0, 0xB5, 0xB5, 0x7C, 0x2C, 0xC9, 0x1C, 0xFC, 0x03, 0x4B, 0x9D, 0xEA, 0x28, +0x65, 0xC4, 0xBE, 0xE9, 0x01, 0x2C, 0x57, 0x9E, 0x07, 0x32, 0xF5, 0xCA, 0x30, 0xA3, 0x3D, 0x55, +0x7C, 0x03, 0xFF, 0x28, 0xC7, 0x01, 0xC1, 0x63, 0x67, 0x32, 0x35, 0x65, 0x4B, 0x1C, 0xC3, 0x89, +0xEF, 0xBA, 0xB1, 0x9D, 0x1B, 0x01, 0x2C, 0x68, 0xE2, 0xC3, 0x2B, 0x19, 0xAC, 0x14, 0x45, 0x67, +0x35, 0x60, 0xEA, 0x09, 0xE2, 0xB9, 0x4B, 0xEF, 0x96, 0x35, 0xE9, 0x54, 0xF0, 0x2B, 0xBD, 0x8C, +0x32, 0xD0, 0x22, 0x80, 0xD8, 0x54, 0xB1, 0x06, 0xEA, 0x84, 0x1B, 0x6D, 0x6A, 0xD8, 0xE8, 0xAC, +0xA2, 0x75, 0x5E, 0x21, 0x06, 0x76, 0x3F, 0x1D, 0xAA, 0xA3, 0x25, 0x28, 0xAE, 0x42, 0xE5, 0xA9, +0x1C, 0xF0, 0x8C, 0xA5, 0xC4, 0x1D, 0xB8, 0x4E, 0x9B, 0x2E, 0xAB, 0x18, 0x6D, 0x08, 0x20, 0x2C, +0x35, 0xDD, 0x4F, 0x52, 0x5F, 0x11, 0xC7, 0x51, 0xB4, 0xE9, 0x47, 0x86, 0x3E, 0x81, 0x28, 0x32, +0x1A, 0x6C, 0xFB, 0xF1, 0x5D, 0xE6, 0x79, 0x6F, 0x14, 0xE2, 0x95, 0x55, 0xDB, 0x6B, 0xCA, 0xCE, +0x65, 0x99, 0xAB, 0x6E, 0xA5, 0xCC, 0x67, 0xF0, 0xF2, 0x75, 0xD4, 0x4E, 0x88, 0xB5, 0x52, 0x8D, +0x42, 0x7C, 0xBF, 0x6F, 0xA1, 0xF0, 0x05, 0xB0, 0xCE, 0x0A, 0x02, 0xF8, 0x6A, 0x36, 0x37, 0x2D, +0x00, 0x1B, 0x7A, 0xD0, 0x05, 0x63, 0x7D, 0x21, 0x04, 0x56, 0xAC, 0x06, 0xD8, 0x77, 0x54, 0xC6, +0x06, 0x75, 0x79, 0x29, 0x24, 0x9C, 0x77, 0xA2, 0xFE, 0x87, 0x74, 0x57, 0xF5, 0xCB, 0x37, 0x27, +0xFD, 0xA8, 0xD3, 0xE0, 0x25, 0x96, 0xEF, 0x1D, 0x3D, 0x1D, 0xDE, 0xA2, 0xE6, 0x6E, 0x92, 0xB8, +0xD3, 0xC3, 0xD1, 0x2F, 0xBC, 0x48, 0xE3, 0x83, 0xD7, 0xD8, 0xDD, 0xB9, 0xBF, 0xF0, 0x1C, 0xFD, +0x93, 0xAC, 0xE2, 0xBB, 0x7D, 0x46, 0xEB, 0x2B, 0x6D, 0x3B, 0x28, 0x7F, 0xE1, 0xC2, 0x10, 0x3A, +0x41, 0x5C, 0xF2, 0x6D, 0x00, 0xE0, 0x57, 0xD7, 0x75, 0xCA, 0x0B, 0xF3, 0x74, 0xF7, 0x35, 0xAC, +0x36, 0xC2, 0x05, 0x86, 0x65, 0x16, 0xE8, 0x5E, 0x38, 0x48, 0x20, 0xEF, 0xE9, 0xF7, 0x4B, 0x08, +0x98, 0x0C, 0xF2, 0x3B, 0xAD, 0x73, 0xE7, 0xCD, 0x8A, 0x89, 0x21, 0xC6, 0x44, 0xB7, 0xB3, 0x0E, +0x85, 0x78, 0xE8, 0xE7, 0xA7, 0xF6, 0xDB, 0x44, 0x1D, 0xC2, 0x32, 0x1B, 0xBD, 0x10, 0x4A, 0x70, +0x56, 0x3D, 0x09, 0xC4, 0x53, 0x0C, 0x3B, 0xD8, 0x21, 0x6B, 0x6C, 0x2A, 0xFD, 0xFF, 0x41, 0x2B, +0x4E, 0x06, 0x06, 0x75, 0xF1, 0x35, 0xB5, 0x75, 0x43, 0xA3, 0xAF, 0xE4, 0xAA, 0xDE, 0x05, 0x48, +0x21, 0xB2, 0x4B, 0xDB, 0x8D, 0x0D, 0xFA, 0x63, 0x43, 0x34, 0xB5, 0x70, 0xFD, 0x7F, 0x01, 0x4A, +0x10, 0x98, 0x03, 0xAC, 0x18, 0x00, 0x00 }; #endif // ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ #endif // ifdef EMBED_ESPEASY_DEFAULT_MIN_CSS @@ -341,12 +353,12 @@ static const char jsClipboardCopyPart2[] PROGMEM = { // "if(t==null){i= max_loop + 1;" "if(t==null){i=101;" "}else{" - "cb+=t.innerHTML.replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'" + "const visibleLines=Array.from(t.children).filter((e=>null!==e.offsetParent)).map((e=>e.innerHTML));visibleLines.length>0&&(cb+=visibleLines.join('\\n').replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'" }; //Fix HTML static const char jsClipboardCopyPart3[] PROGMEM = { - "';}}" + "');}}" "cb=cb.replace(/<\\/[Dd][Ii][Vv]\\s*\\/?>/gim,'\\n');" "cb=cb.replace(/<[^>]*>/gim,'');" "var ti=document.createElement('textarea');" @@ -381,38 +393,91 @@ static const char jsSplitMultipleFields[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { "function elId(e){return document.getElementById(e)}" - "function loopDeLoop(e,s){var o,a,n=0;isNaN(s)&&(s=1),null==e&&(e=1e3)," - "i=setInterval(function(){if(n>0){clearInterval(i);return}" - "++s>1||fetch('/json?view=sensorupdate').then(function(s){var n;if(200!==s.status){console.log('Looks like there was a problem. Status Code: '+s.status);return}" - "s.json().then(function(s){e=s.TTL;" + "loopDeLoop(1000);" + "const SENSOR_URL='/json?view=sensorupdate';" + "function loopDeLoop(e=1000){setTimeout(function(){fetch(SENSOR_URL)" + ".then(e=>{if(!e.ok)throw new Error(e.status);return e.json()})" + ".then(o=>{e=o.TTL||e;" #if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES) - "var r=void 0===s.ShowUoM||s.ShowUoM&&'false'!==s.ShowUoM;" + "const n=void 0===o.ShowUoM||o.ShowUoM&&'false'!==o.ShowUoM;" #endif // if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES) - "for(o=0;o{loopDeLoop(5000)})},e)}" }; #endif // WEBSERVER_INCLUDE_JS #ifdef WEBSERVER_INCLUDE_JS static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { - "function elId(e){return document.getElementById(e)}" - "function getBrowser(){var e,o=navigator.userAgent,r=o.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];return/trident/i.test(r[1])?{name:'IE',version:(e=/\brv[ :]+(\\d+)/g.exec(o)||[])[1]||''}:'Chrome'===r[1]&&null!=(e=o.match(/\bOPR|Edge\\/(\\d+)/))?{name:'Opera',version:e[1]}:(r=r[2]?[r[1],r[2]]:[navigator.appName,navigator.appVersion,'-?'],null!=(e=o.match(/version\\/(\\d+)/i))&&r.splice(1,1,e[1]),{name:r[0],version:r[1]})}" - "var browser=getBrowser(),currentBrowser=browser.name+browser.version;textToDisplay=(browser.name=browser.version<12)?'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':'Fetching log entries...',elId('copyText_1').innerHTML=textToDisplay,loopDeLoop(1e3,0);var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];" - "function loopDeLoop(e,o){let r='copyText_1';isNaN(o)&&(o=1),null==e&&(e=1e3),scrolling_type=e<=500?'auto':'smooth';var n,t,i='',l=0," - "s=setInterval(function(){if(l>0){clearInterval(s);return}++o>1?l=1:fetch('/logjson').then(function(o){if(200!==o.status){console.log('Looks like there was a problem. Status Code: '+o.status);return}o.json()" - ".then(function(o){var l;for(null==t&&(t=''),n=0;n'+o.Log.Entries[n].timestamp+': '+o.Log.Entries[n].text+'')}e=o.Log.TTL,''!==t&&('Fetching log entries...'==elId(r).innerHTML&&(elId(r).innerHTML=''),elId(r).innerHTML+=t),t='',!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type}),elId('current_loglevel').innerHTML='Logging: '+logLevel[o.Log.SettingsWebLogLevel]+' ('+o.Log.SettingsWebLogLevel+')',clearInterval(s)," - "loopDeLoop(e,0)})}).catch(function(o){elId(r).innerHTML+='
>> '+o.message+' <<
',autoscroll_on=elId('autoscroll').checked,elId(r).scrollTop=elId(r).scrollHeight,e=5e3,clearInterval(s)," - "loopDeLoop(e,0)}),l=1},e)}" +"function elId(e){return document.getElementById(e)}" +"const ct1 = elId('copyText_1');" +"window.fetch?ct1.textContent='Fetching log entries...':" +"ct1.textContent='Error: This browser is not supported. Please use a modern browser.';" +"const logLevel={0:'Unused',1:'Error',2:'Info',3:'Debug',4:'Debug More',9:'Debug Dev'};" +"function loopDeLoop(e=1000){setTimeout(function(){fetch('/logjson')" +".then(e=>{if(!e.ok)throw Error(e.status);return e.json()})" +".then(t=>{let o='';" +"for(let l=0;l" +"${i.timestamp}: ${i.text}`)}" +"e=t.Log.TTL||e;" +"o&&(ct1.textContent==='Fetching log entries...'&&(ct1.innerHTML=''),ct1.innerHTML+=o);" +"applyLogFilter();" +"elId('autoscroll')?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?'auto':'smooth'});" +"let n=t.Log.SettingsWebLogLevel,r=logLevel[n]?" "?'Undefined';" +"elId('current_loglevel').textContent=`Logging: ${r} (${n})`;" +"loopDeLoop(e)})" +".catch(e=>{ct1.innerHTML+=`
>> ${e.message} <<
`;" +"ct1.scrollTop=ct1.scrollHeight;loopDeLoop(5000)})},e)}" +"function applyLogFilter(){" +"let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean)," +"t=document.querySelectorAll('#copyText_1 > div')," +"o=e.some(e=>!e.startsWith('!'));" +"for(let l of t){" +"l.dataset.originalHtml||(l.dataset.originalHtml=l.innerHTML);" +"let i=l.dataset.originalHtml,n=l.textContent.toLowerCase(),r=[],s=!1,a=!o;" +"for(let c of e){" +"if(c.startsWith('!')){let g=c.slice(1);" +"if(!g.includes('&')||g.startsWith('&')||g.endsWith('&')){" +"if(g&&n.includes(g.toLowerCase())){s=!0;break}}" +"else{let f=g.split('&').map(e=>e.trim()).filter(Boolean),u=-1,d=!0;" +"for(let p of f){let h=n.indexOf(p.toLowerCase(),u+1);if(h===-1){d=!1;break}u=h}" +"if(d){s=!0;break}}continue}" +"if(c.includes('&')&&!c.startsWith('&')&&!c.endsWith('&')){" +"let L=c.split('&').map(e=>e.trim()).filter(Boolean);" +"if(L.length>=2){let _=-1,$=!0;" +"for(let b of L){let m=n.indexOf(b.toLowerCase(),_+1);if(m===-1){$=!1;break}_=m}" +"if($){r.push(...L),a=!0;break}}continue}" +"if(n.includes(c.toLowerCase())){r.push(c),a=!0;break}}" +"if(s||!a){l.style.display='none';continue}" +"l.style.display='';l.innerHTML=highlightOutsideTags(i,r)}}" +"function highlightOutsideTags(e,t){if(!t.length)return e;" +"let o='',l=!1,i=0;" +"for(;i'===n){l=!1;o+=n;i++;continue}" +"if(!l){let r=!1;" +"for(let s of t){let a=e.slice(i,i+s.length);" +"if(a.toLowerCase()===s.toLowerCase()){" +"o+=`${a}`;" +"i+=s.length;r=!0;break}}" +"if(r)continue}" +"o+=n;i++}return o}" +"loopDeLoop(1000);" +"const input=document.getElementById('logfilter');" +"input&&(input.placeholder='\"!\"=exclude \";\"=or \"&\"=and (e.g. !act)');" }; #endif // WEBSERVER_INCLUDE_JS diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 5f1f5283c..2c441b4e3 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -31,6 +31,7 @@ # include "../Helpers/_Plugin_Helper_serial.h" # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" +# include "../Helpers/SPI_Helper.h" # include "../Helpers/StringConverter.h" # include "../Helpers/StringGenerator_GPIO.h" @@ -155,15 +156,17 @@ void handle_devices() { { // change of device: cleanup old device and reset default settings setTaskDevice_to_TaskIndex(taskdevicenumber, taskIndex); +#if FEATURE_I2C const deviceIndex_t DeviceIndex = getDeviceIndex(taskdevicenumber); if (validDeviceIndex(DeviceIndex)) { const DeviceStruct& device = Device[DeviceIndex]; if ((device.Type == DEVICE_TYPE_I2C) && device.I2CMax100kHz) { // 100 kHz-only I2C device? - bitWrite(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default + bitWrite(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default } } +#endif } else if (taskdevicenumber != INVALID_PLUGIN_ID) // save settings { @@ -281,6 +284,14 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + #ifdef ESP32 + #if FEATURE_SPI + if (device.isSPI()) { + Settings.setSPIBusForTask(taskIndex, getFormItemInt(F("pspibus"), 0)); + } + #endif + #endif // ifdef ESP32 +#if FEATURE_I2C if (device.Type == DEVICE_TYPE_I2C) { uint8_t flags = 0; bitWrite(flags, I2C_FLAGS_SLOW_SPEED, isFormItemChecked(F("taskdeviceflags0"))); @@ -322,8 +333,9 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # endif // if FEATURE_I2CMULTIPLEXER - Settings.I2C_Flags[taskIndex] = flags; + Settings.I2C_SPI_bus_Flags[taskIndex] = flags; } +#endif // Must load from file system to make sure all caches and checksums match. ExtraTaskSettings.clear(); @@ -617,7 +629,9 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (pluginID_set) { // LoadTaskSettings(x); +#if FEATURE_SPI int8_t spi_gpios[3] { -1, -1, -1 }; +#endif struct EventStruct TempEvent(x); addEnabled(Settings.TaskDeviceEnabled[x] && validDeviceIndex(DeviceIndex)); @@ -642,12 +656,17 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) addHtml(portDescr); } else { const DeviceStruct& device = Device[DeviceIndex]; - +#if FEATURE_I2C if (device.Type == DEVICE_TYPE_I2C) { format_I2C_port_description(x); - } else if (device.isSPI()) { - format_SPI_port_description(spi_gpios); - } else if (device.isSerial()) { + } else +#endif +#if FEATURE_SPI + if (device.isSPI()) { + format_SPI_port_description(spi_gpios, Settings.getSPIBusForTask(x)); + } else +#endif + if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); # else // ifdef PLUGIN_USES_SERIAL @@ -716,12 +735,15 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) switch (device.Type) { +#if FEATURE_I2C case DEVICE_TYPE_I2C: { format_I2C_pin_description(x); html_BR(); break; } +#endif +#if FEATURE_SPI case DEVICE_TYPE_SPI3: showpin3 = !pluginHasGPIODescription; @@ -733,6 +755,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) case DEVICE_TYPE_SPI: format_SPI_pin_description(spi_gpios, x, !pluginHasGPIODescription); break; +#endif case DEVICE_TYPE_ANALOG: { # ifdef ESP8266 @@ -971,6 +994,7 @@ void format_originating_node(uint8_t remoteUnit) { # endif // if FEATURE_ESPEASY_P2P +#if FEATURE_I2C void format_I2C_port_description(taskIndex_t x) { addHtml(F("I2C")); @@ -999,7 +1023,7 @@ void format_I2C_port_description(taskIndex_t x) if (isI2CMultiplexerEnabled(i2cBus) && I2CMultiplexerPortSelectedForTask(x)) { String mux; - if (bitRead(Settings.I2C_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel + if (bitRead(Settings.I2C_SPI_bus_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel mux = F("
Multiplexer channel(s)"); uint8_t b = 0; // For adding lineBreaks @@ -1017,21 +1041,25 @@ void format_I2C_port_description(taskIndex_t x) } # endif // if FEATURE_I2CMULTIPLEXER } +#endif -void format_SPI_port_description(int8_t spi_gpios[3]) +#if FEATURE_SPI +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus) { - if (!Settings.getSPI_pins(spi_gpios)) { + if (!Settings.getSPI_pins(spi_gpios, spi_bus)) { addHtml(F("SPI (Not enabled)")); return; } # ifdef ESP32 - addHtml(getSPI_optionToShortString(static_cast(Settings.InitSPI))); + addHtml(getSPI_optionToShortString(static_cast(0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1), spi_bus)); # endif // ifdef ESP32 # ifdef ESP8266 addHtml(F("SPI")); # endif // ifdef ESP8266 } +#endif +#if FEATURE_I2C void format_I2C_pin_description(taskIndex_t x) { # if FEATURE_I2C_MULTIPLE @@ -1046,10 +1074,13 @@ void format_I2C_pin_description(taskIndex_t x) Label_Gpio_toHtml(F("SCL"), formatGpioLabel(Settings.getI2CSclPin(i2cBus), false)); } } +#endif +#if FEATURE_SPI void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin) { - if (Settings.InitSPI > static_cast(SPI_Options_e::None)) { + const uint8_t spi_bus = Settings.getSPIBusForTask(x); + if ((0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1) > static_cast(SPI_Options_e::None)) { const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI") }; for (size_t i = 0; i < NR_ELEMENTS(labels); ++i) { @@ -1066,6 +1097,7 @@ void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSp } } } +#endif // ******************************************************************************** // Show the task settings page @@ -1195,7 +1227,11 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addPinConfig = true; } - if (addPinConfig || (device.Type == DEVICE_TYPE_I2C)) { + if (addPinConfig +#if FEATURE_I2C + || (device.Type == DEVICE_TYPE_I2C) +#endif + ) { if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL devicePage_show_serial_config(taskIndex); @@ -1207,15 +1243,18 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addPinConfig = false; html_add_script(F("document.getElementById('serPort').onchange();"), false); - } else if (device.Type == DEVICE_TYPE_I2C) { + } else +#if FEATURE_I2C + if (device.Type == DEVICE_TYPE_I2C) { devicePage_show_pin_config(taskIndex, DeviceIndex); addPinConfig = false; - +#if FEATURE_I2C if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { devicePage_show_I2C_config(taskIndex, DeviceIndex); } +#endif } - +#endif if (addPinConfig) { devicePage_show_pin_config(taskIndex, DeviceIndex); } @@ -1321,10 +1360,11 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormNote(F("Will go into effect on next input change.")); } - if (device.isSPI() - && (Settings.InitSPI == static_cast(SPI_Options_e::None))) { - addFormNote(F("SPI Interface is not configured yet (Hardware page).")); +#if FEATURE_SPI + if (device.isSPI()) { + devicePage_show_SPI_config(taskIndex, DeviceIndex); } +#endif if (device.connectedToGPIOpins()) { // get descriptive GPIO-names from plugin @@ -1343,11 +1383,14 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex { // Pin1 = GPIO <--- TX purpose = PinSelectPurpose::Serial_input; - } else if (device.isSPI()) + } +#if FEATURE_SPI + else if (device.isSPI()) { // All selectable SPI pins are output only purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String1, F("taskdevicepin1"), Settings.TaskDevicePin1[taskIndex]); } @@ -1360,23 +1403,25 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Serial Pin2 = GPIO ---> RX purpose = PinSelectPurpose::Serial_output; } - +#if FEATURE_SPI if (device.isSPI()) { // SPI only needs output pins purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String2, F("taskdevicepin2"), Settings.TaskDevicePin2[taskIndex]); } if (device.usesTaskDevicePin(3)) { PinSelectPurpose purpose = device.getPinSelectPurpose(3); // PinSelectPurpose::Generic; - +#if FEATURE_SPI if (device.isSPI()) { // SPI only needs output pins purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String3, F("taskdevicepin3"), Settings.TaskDevicePin3[taskIndex]); } } @@ -1399,27 +1444,39 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) # endif // ifdef PLUGIN_USES_SERIAL +#if FEATURE_SPI +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) +{ + if (Device[DeviceIndex].isSPI() + && Settings.getNrConfiguredSPI_buses() == 0) { + addFormNote(F("SPI Bus not configured yet (Hardware page).")); + } + #ifdef ESP32 + if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) { + uint8_t spiBus = Settings.getSPIBusForTask(taskIndex); + SPIInterfaceSelector(F("SPI Bus"), + F("pspibus"), + spiBus); + } + #endif // ifdef ESP32 +} +#endif + +#if FEATURE_I2C void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); addFormSubHeader(F("I2C options")); - if (!Settings.isI2CEnabled(0) - # if FEATURE_I2C_MULTIPLE - && (getI2CBusCount() > 1 && !Settings.isI2CEnabled(1)) - # if FEATURE_I2C_INTERFACE_3 - && (getI2CBusCount() > 2 && !Settings.isI2CEnabled(2)) - # endif // if FEATURE_I2C_INTERFACE_3 - # endif // if FEATURE_I2C_MULTIPLE - ) { + if (Settings.getNrConfiguredI2C_buses() == 0) { addFormNote(F("I2C Bus is not configured yet (Hardware page).")); } String dummy; PluginCall(PLUGIN_WEBFORM_SHOW_I2C_PARAMS, &TempEvent, dummy); - addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); + addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); if (Device[DeviceIndex].I2CMax100kHz) { addFormNote(F("This device is specified for max. 100 kHz operation!")); @@ -1442,7 +1499,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Show selector for an I2C multiplexer port if a multiplexer is configured if (isI2CMultiplexerEnabled(i2cBus)) { - bool multipleMuxPorts = bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); + bool multipleMuxPorts = bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), @@ -1509,6 +1566,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } # endif // if FEATURE_I2CMULTIPLEXER } +#endif void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 8b7bece77..17ec4f775 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -35,13 +35,20 @@ void handle_devicess_ShowAllTasksTable(uint8_t page); #if FEATURE_ESPEASY_P2P void format_originating_node(uint8_t remoteUnit); #endif +#if FEATURE_I2C void format_I2C_port_description(taskIndex_t x); +#endif -void format_SPI_port_description(int8_t spi_gpios[3]); - +#if FEATURE_SPI +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus); +#endif +#if FEATURE_I2C void format_I2C_pin_description(taskIndex_t x); +#endif +#if FEATURE_SPI void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin = true); +#endif @@ -56,7 +63,13 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex void devicePage_show_serial_config(taskIndex_t taskIndex); #endif +#if FEATURE_SPI +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); +#endif + +#if FEATURE_I2C void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); +#endif void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 12e3efad3..d8fc4aa8e 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -16,6 +16,7 @@ #include "../WebServer/FileList.h" #include "../WebServer/HTML_wrappers.h" #include "../WebServer/HardwarePage.h" +#include "../WebServer/InterfacesPage.h" #include "../WebServer/I2C_Scanner.h" #include "../WebServer/JSON.h" #include "../WebServer/LoadFromFS.h" @@ -272,6 +273,27 @@ void WebServerInit() #ifdef WEBSERVER_HARDWARE web_server.on(F("/hardware"), handle_hardware); #endif // ifdef WEBSERVER_HARDWARE + #ifdef WEBSERVER_INTERFACES + web_server.on(F("/interfaces"), handle_interfaces); +#if FEATURE_I2C + web_server.on(F("/interfaces_i2c"), handle_interfaces_i2c); +#endif // if FEATURE_I2C +#if FEATURE_SPI + web_server.on(F("/interfaces_spi"), handle_interfaces_spi); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + web_server.on(F("/interfaces_modbus"), handle_interfaces_modbus); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + web_server.on(F("/interfaces_can"), handle_interfaces_can); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + web_server.on(F("/interfaces_wrmbus"), handle_interfaces_wrmbus); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + web_server.on(F("/interfaces_wimbus"), handle_interfaces_wimbus); +#endif // if FEATURE_WIMBUS + #endif // ifdef WEBSERVER_INTERFACES #ifdef WEBSERVER_I2C_SCANNER web_server.on(F("/i2cscanner"), handle_i2cscanner); #endif // ifdef WEBSERVER_I2C_SCANNER @@ -281,8 +303,10 @@ void WebServerInit() #ifdef WEBSERVER_CSVVAL web_server.on(F("/csv"), handle_csvval); #endif + #ifdef WEBSERVER_LOG web_server.on(F("/log"), handle_log); web_server.on(F("/logjson"), handle_log_JSON); // Also part of WEBSERVER_NEW_UI + #endif #if FEATURE_NOTIFIER web_server.on(F("/notifications"), handle_notifications); #endif // if FEATURE_NOTIFIER @@ -492,7 +516,7 @@ void getWebPageTemplateDefault(const String& tmplName, WebTemplateParser& parser getWebPageTemplateDefaultHead(parser, addMeta, addJS); if (!parser.isTail()) { - parser.process(F("

ESP Easy Mega: ")); + parser.process(strformat(F("

ESP Easy Mega: "), isGpMenuSecondLevel(navMenuIndex) ? '2' : ' ')); parser.process(title); # if BUILD_IN_WEBHEADER parser.process(F( diff --git a/src/src/WebServer/HTML_wrappers.cpp b/src/src/WebServer/HTML_wrappers.cpp index 70525ed24..6db943674 100644 --- a/src/src/WebServer/HTML_wrappers.cpp +++ b/src/src/WebServer/HTML_wrappers.cpp @@ -598,10 +598,18 @@ void addGpioHtml(int8_t pin) { if (pin == -1) { return; } addHtml(formatGpioLabel(pin, false)); - if (Settings.isSPI_pin(pin) || + if ( +#if FEATURE_SPI + Settings.isSPI_pin(pin) || +#endif +#if FEATURE_I2C Settings.isI2C_pin(pin) || +#endif +#if FEATURE_ETHERNET Settings.isEthernetPin(pin) || - Settings.isEthernetPinOptional(pin)) { + Settings.isEthernetPinOptional(pin)|| +#endif + false) { addHtml(' '); addHtml(F(HTML_SYMBOL_WARNING)); } diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 7370cf812..da114daaa 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -16,14 +16,11 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" -#include "../Helpers/Hardware_I2C.h" +#include "../Helpers/I2C_access.h" +#include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" -#if FEATURE_I2C_MULTIPLE -#include "../Helpers/I2C_access.h" -#include "../Helpers/Hardware_device_info.h" -#endif // if FEATURE_I2C_MULTIPLE // ******************************************************************************** // Web Interface hardware page @@ -43,83 +40,14 @@ void handle_hardware() { Settings.Pin_status_led = getFormItemInt(F("pled")); Settings.Pin_status_led_Inversed = isFormItemChecked(F("pledi")); Settings.Pin_Reset = getFormItemInt(F("pres")); - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(0)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda); - update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl); + #if FEATURE_I2C_MULTIPLE + if (isFormItem(F("pi2cbuspcf"))) { + set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); } - Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch")); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0")); - if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0")); - } else { - Settings.I2C_Multiplexer_Addr = -1; - } - Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0")); - #endif // if FEATURE_I2CMULTIPLEXER - #if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code - if (getI2CBusCount() > 1) { - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(1)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda); - update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl); - } - Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1")); - if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1")); - } else { - Settings.I2C2_Multiplexer_Addr = -1; - } - Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1")); - #endif // if FEATURE_I2CMULTIPLEXER - } - #if FEATURE_I2C_INTERFACE_3 - if (getI2CBusCount() > 2) { - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(2)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda); - update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl); - } - Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2")); - if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2")); - } else { - Settings.I2C3_Multiplexer_Addr = -1; - } - Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2")); - #endif // if FEATURE_I2CMULTIPLEXER - } - #endif // if FEATURE_I2C_INTERFACE_3 - set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); #endif // if FEATURE_I2C_MULTIPLE - #ifdef ESP32 - Settings.InitSPI = getFormItemInt(F("initspi"), static_cast(SPI_Options_e::None)); - if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-define SPI GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi"), -1); - if (!Settings.isSPI_valid()) { // Checks - error += F("User-defined SPI pins not configured correctly!\n"); - } - } - #else //for ESP8266 we keep the old UI - Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init - #endif + #if defined(ESP32) && FEATURE_SD + Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); + #endif // if defined(ESP32) && FEATURE_SD Settings.Pin_sd_cs = getFormItemInt(F("sd")); int gpio = 0; @@ -137,10 +65,6 @@ void handle_hardware() { } error += SaveSettings(); addHtmlError(error); - if (error.isEmpty()) { - // Apply I2C settings. - initI2C(); - } } addHtml(F("
")); @@ -150,171 +74,40 @@ void handle_hardware() { addFormSubHeader(F("Wifi Status LED")); addFormPinSelect(PinSelectPurpose::Status_led, formatGpioName_output(F("LED")), F("pled"), Settings.Pin_status_led); addFormCheckBox(F("Inversed LED"), F("pledi"), Settings.Pin_status_led_Inversed); - addFormNote(F("Use ’GPIO-2 (D4)’ with ’Inversed’ checked for onboard LED")); + addFormNote(F("Use ’GPIO-2" +#ifdef ESP8266 + " (D4)" +#endif + "’ with ’Inversed’ checked for onboard LED")); addFormSubHeader(F("Reset Pin")); addFormPinSelect(PinSelectPurpose::Reset_pin, formatGpioName_input(F("Switch")), F("pres"), Settings.Pin_Reset); addFormNote(F("Press about 10s for factory reset")); - #if FEATURE_I2CMULTIPLEXER - const __FlashStringHelper *i2c_muxtype_options[] = { - F("- None -"), - F("TCA9548a - 8 channel"), - F("TCA9546a - 4 channel"), - F("TCA9543a - 2 channel"), - F("PCA9540 - 2 channel (experimental)") - }; - const int i2c_muxtype_choices[] = { - I2C_MULTIPLEXER_NONE, - I2C_MULTIPLEXER_TCA9548A, - I2C_MULTIPLEXER_TCA9546A, - I2C_MULTIPLEXER_TCA9543A, - I2C_MULTIPLEXER_PCA9540 - }; - const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices), - i2c_muxtype_options, i2c_muxtype_choices); +# if FEATURE_I2C_MULTIPLE + const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); -// Select the I2C address for a port multiplexer - - String i2c_mux_options[9]; - int i2c_mux_choices[9]; - uint8_t mux_opt = 0; - i2c_mux_options[mux_opt] = F("- None -"); - i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE; - mux_opt++; - for (int8_t x = 0; x < 8; x++) { - i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x); - if (x == 0) { // PCA9540 has a fixed address 0f 0x70 - i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]"); - } else if (x < 4) { - i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]"); - } else { - i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]"); - } - i2c_mux_choices[mux_opt] = 0x70 + x; - mux_opt++; - } - const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices); - #endif // if FEATURE_I2CMULTIPLEXER - - uint8_t i2cBus = 0; - #if FEATURE_I2C_MULTIPLE - for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) - #endif // if FEATURE_I2C_MULTIPLE - { - #if !FEATURE_I2C_MULTIPLE - addFormSubHeader(F("I2C Bus")); - #else // if !FEATURE_I2C_MULTIPLE - addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus)); - #endif // if !FEATURE_I2C_MULTIPLE - #if FEATURE_PLUGIN_PRIORITY - if (isI2CPriorityTaskActive(i2cBus)) { - I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus); - } else - #endif // if FEATURE_PLUGIN_PRIORITY - { - addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus)); - addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus)); - } - addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000); - addUnit(F("Hz")); - addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most.")); - addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100, 3400000); - addUnit(F("Hz")); - - if (0 == i2cBus) { // Only support Clock-stretching on Bus 1 - addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0); - #ifdef ESP8266 - addUnit(F("usec")); - #endif - #ifdef ESP32 - addUnit(F("1/80 usec")); - #endif - } - - #if FEATURE_I2CMULTIPLEXER - #if !FEATURE_I2C_MULTIPLE - addFormSubHeader(F("I2C Multiplexer")); - #else // if !FEATURE_I2C_MULTIPLE - addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus)); - #endif // if FEATURE_I2C_MULTIPLE - // Select the type of multiplexer to use - { - muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus)); - addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus), Settings.getI2CMultiplexerAddr(i2cBus)); - // addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"), i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus)); - const String id = strformat(F("pi2cmuxreset%u"), i2cBus); - addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id); - addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus)); - addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540.")); - } - #endif // if FEATURE_I2CMULTIPLEXER - } - #if FEATURE_I2C_MULTIPLE - const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1 - ? ((Settings.isI2CEnabled(1) ? 1 : 0) - # if FEATURE_I2C_INTERFACE_3 - + (Settings.isI2CEnabled(2) ? 1 : 0) - # endif // if FEATURE_I2C_INTERFACE_3 - ) - : 0) + (Settings.isI2CEnabled(0) ? 1 : 0); if (i2cMaxBusCount > 1) { addFormSubHeader(F("PCF & MCP Direct I/O")); const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP(); I2CInterfaceSelector(F("I2C Bus"), - F("pi2cbuspcf"), - i2cBus, - false); + F("pi2cbuspcf"), + i2cBus, + false); } - #endif // if FEATURE_I2C_MULTIPLE +# endif // if FEATURE_I2C_MULTIPLE - // SPI Init - addFormSubHeader(F("SPI Interface")); - #ifdef ESP32 - { - // Script to show GPIO pins for User-defined SPI GPIOs - // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), - // Minified: - html_add_script(F("function spiOptionChanged(e){var i=9==e.value?'':'none';" - "document.getElementById('tr_spipinsclk').style.display=i," - "document.getElementById('tr_spipinmiso').style.display=i," - "document.getElementById('tr_spipinmosi').style.display=i}"), - false); - const __FlashStringHelper * spi_options[] = { - getSPI_optionToString(SPI_Options_e::None), - getSPI_optionToString(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - getSPI_optionToString(SPI_Options_e::Hspi), - #endif - getSPI_optionToString(SPI_Options_e::UserDefined)}; - const int spi_index[] = { - static_cast(SPI_Options_e::None), - static_cast(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - static_cast(SPI_Options_e::Hspi), - #endif - static_cast(SPI_Options_e::UserDefined) - }; - constexpr size_t nrOptions = NR_ELEMENTS(spi_index); - FormSelectorOptions selector(nrOptions, spi_options, spi_index); - selector.onChangeCall = F("spiOptionChanged(this)"); - selector.addFormSelector(F("Init SPI"), F("initspi"), Settings.InitSPI); - // User-defined pins - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), F("spipinsclk"), Settings.SPI_SCLK_pin); - addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), F("spipinmiso"), Settings.SPI_MISO_pin); - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), F("spipinmosi"), Settings.SPI_MOSI_pin); - html_add_script(F("document.getElementById('initspi').onchange();"), false); // Initial trigger onchange script - addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); - } - #else //for ESP8266 we keep the existing UI - addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); - addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); - #endif - addFormNote(F("Chip Select (CS) config must be done in the plugin")); - #if FEATURE_SD addFormSubHeader(F("SD Card")); + #ifdef ESP32 + if (getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) { + uint8_t spiBus = Settings.getSPIBusForSDCard(); + SPIInterfaceSelector(F("SPI Bus"), + F("sdspibus"), + spiBus); + } + #endif // ifdef ESP32 addFormPinSelect(PinSelectPurpose::SD_Card, formatGpioName_output(F("SD Card CS")), F("sd"), Settings.Pin_sd_cs); #endif // if FEATURE_SD @@ -336,26 +129,4 @@ void handle_hardware() { TXBuffer.endStream(); } -#if FEATURE_PLUGIN_PRIORITY -bool isI2CPriorityTaskActive(uint8_t i2cBus) { - bool hasI2CPriorityTask = false; - for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) { - hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus); - } - return hasI2CPriorityTask; -} - -void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) { - int pinnr = -1; - bool input, output, warning = false; - addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus)); - addRowLabel(formatGpioName_bidirectional(F("SDA"))); - getGpioInfo(i2c_sda, pinnr, input, output, warning); - addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false)); - addRowLabel(formatGpioName_output(F("SCL"))); - getGpioInfo(i2c_scl, pinnr, input, output, warning); - addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false)); -} -#endif // if FEATURE_PLUGIN_PRIORITY - #endif // ifdef WEBSERVER_HARDWARE diff --git a/src/src/WebServer/HardwarePage.h b/src/src/WebServer/HardwarePage.h index fa42a9554..324654b90 100644 --- a/src/src/WebServer/HardwarePage.h +++ b/src/src/WebServer/HardwarePage.h @@ -10,12 +10,6 @@ // ******************************************************************************** void handle_hardware(); -# if FEATURE_PLUGIN_PRIORITY -bool isI2CPriorityTaskActive(uint8_t i2cBus); -void I2CShowSdaSclReadonly(int8_t i2c_sda, - int8_t i2c_scl, - uint8_t i2cBus); -# endif // if FEATURE_PLUGIN_PRIORITY #endif // ifdef WEBSERVER_HARDWARE #endif // ifndef WEBSERVER_WEBSERVER_HARDWAREPAGE_H diff --git a/src/src/WebServer/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp new file mode 100644 index 000000000..11fafa1cb --- /dev/null +++ b/src/src/WebServer/InterfacesPage.cpp @@ -0,0 +1,596 @@ +#include "../WebServer/InterfacesPage.h" + +#ifdef WEBSERVER_INTERFACES + +# include "../WebServer/ESPEasy_WebServer.h" +# include "../WebServer/HTML_wrappers.h" +# include "../WebServer/Markup.h" +# include "../WebServer/Markup_Buttons.h" +# include "../WebServer/Markup_Forms.h" + +# include "../CustomBuild/ESPEasyLimits.h" + +# include "../DataStructs/DeviceStruct.h" + +# include "../Globals/Settings.h" + +# include "../Helpers/ESPEasy_Storage.h" +# include "../Helpers/Hardware_GPIO.h" +# include "../Helpers/Hardware_I2C.h" +# include "../Helpers/Hardware_SPI.h" +# include "../Helpers/SPI_Helper.h" +# include "../Helpers/StringConverter.h" +# include "../Helpers/StringGenerator_GPIO.h" + +# if FEATURE_I2C_MULTIPLE +# include "../Helpers/I2C_access.h" +# include "../Helpers/Hardware_device_info.h" +# endif // if FEATURE_I2C_MULTIPLE + +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +#if FEATURE_I2C +void handle_interfaces_i2c() { + navMenuIndex = MENU_INDEX_INTERFACES_I2C; + handle_interfaces(); +} +#endif // if FEATURE_I2C + +#if FEATURE_SPI +void handle_interfaces_spi() { + navMenuIndex = MENU_INDEX_INTERFACES_SPI; + handle_interfaces(); +} +#endif // if FEATURE_SPI + +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB +void handle_interfaces_modbus() { + navMenuIndex = MENU_INDEX_INTERFACES_MODBUS; + handle_interfaces(); +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +void handle_interfaces_can() { + navMenuIndex = MENU_INDEX_INTERFACES_CAN; + handle_interfaces(); +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +void handle_interfaces_wrmbus() { + navMenuIndex = MENU_INDEX_INTERFACES_WRMBUS; + handle_interfaces(); +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +void handle_interfaces_wimbus() { + navMenuIndex = MENU_INDEX_INTERFACES_WIMBUS; + handle_interfaces(); +} +#endif // if FEATURE_WIMBUS + +void handle_interfaces() { +# ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("handle_interfaces")); +# endif + + if (!isLoggedIn()) { return; } + if ((navMenuIndex != MENU_INDEX_INTERFACES) && (navMenuIndex <= MENU_MAX_INDEX_SHOWN)) { +#if FEATURE_I2C + navMenuIndex = MENU_INDEX_INTERFACES_I2C; +#elif FEATURE_SPI + navMenuIndex = MENU_INDEX_INTERFACES_SPI; +#elif FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + navMenuIndex = MENU_INDEX_INTERFACES_MODBUS; +#elif FEATURE_CAN + navMenuIndex = MENU_INDEX_INTERFACES_CAN; +#elif FEATURE_WRMBUS + navMenuIndex = MENU_INDEX_INTERFACES_WRMBUS; +#elif FEATURE_WIMBUS + navMenuIndex = MENU_INDEX_INTERFACES_WIMBUS; +#else + navMenuIndex = MENU_INDEX_INTERFACES; +#endif + } + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + save_interfaces(); + + + addHtml(F("")); + html_table_class_normal(); + addFormFixedFirstColumn(); // This must be added as the first element in a table definition + addFormHeader(strformat(F("%s Interfaces Settings"), FsP(getGpMenuIcon(navMenuIndex))), F(""), F("Interfaces/Interfaces.html")); + + #if FEATURE_I2C + if (navMenuIndex == MENU_INDEX_INTERFACES_I2C) { + interfaces_show_I2C(); + } + #endif + + #if FEATURE_SPI + if (navMenuIndex == MENU_INDEX_INTERFACES_SPI) { + interfaces_show_SPI(); + } + #endif + + #if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + if (navMenuIndex == MENU_INDEX_INTERFACES_MODBUS) { + interfaces_show_MODBUS(); + } + #endif // if FEATURE_MODBUS + + #if FEATURE_CAN + if (navMenuIndex == MENU_INDEX_INTERFACES_CAN) { + interfaces_show_CAN(); + } + #endif // if FEATURE_CAN + + #if FEATURE_WRMBUS + if (navMenuIndex == MENU_INDEX_INTERFACES_WRMBUS) { + interfaces_show_WRMBUS(); + } + #endif // if FEATURE_WRMBUS + + #if FEATURE_WIMBUS + if (navMenuIndex == MENU_INDEX_INTERFACES_WIMBUS) { + interfaces_show_WIMBUS(); + } + #endif // if FEATURE_WIMBUS + + addFormSeparator(2); + + html_TR_TD(); + html_TD(); + addSubmitButton(); + html_TR_TD(); + html_end_table(); + html_end_form(); + + sendHeadandTail_stdtemplate(_TAIL); + TXBuffer.endStream(); +} + +void save_interfaces() { + + String error; + bool updated{}; + + #if FEATURE_I2C + + if ((navMenuIndex == MENU_INDEX_INTERFACES_I2C) && save_I2C(error)) { updated = true; } + #endif + + #if FEATURE_SPI + if ((navMenuIndex == MENU_INDEX_INTERFACES_SPI) && save_SPI(error)) { updated = true; } + #endif + + #if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + if ((navMenuIndex == MENU_INDEX_INTERFACES_MODBUS) && save_MODBUS(error)) { updated = true; } + #endif // if FEATURE_MODBUS + + #if FEATURE_CAN + if ((navMenuIndex == MENU_INDEX_INTERFACES_CAN) && save_CAN(error)) { updated = true; } + #endif // if FEATURE_CAN + + #if FEATURE_WRMBUS + if ((navMenuIndex == MENU_INDEX_INTERFACES_WRMBUS) && save_WRMBUS(error)) { updated = true; } + #endif // if FEATURE_WRMBUS + + #if FEATURE_WIMBUS + if ((navMenuIndex == MENU_INDEX_INTERFACES_WIMBUS) && save_WIMBUS(error)) { updated = true; } + #endif // if FEATURE_WIMBUS + + if (updated) { + error += SaveSettings(); + addHtmlError(error); + + if (error.isEmpty()) { +#if FEATURE_I2C + // Apply I2C settings. + initI2C(); +#endif + +#if FEATURE_SPI + // Apply SPI settings + initializeSPIBuses(); +#endif + } + } +} + +#if FEATURE_I2C + +bool save_I2C(String& error) { + bool updated{}; + + + if (isFormItem(F("pi2csp0"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(0)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda); + update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl); + } + Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW); + Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch")); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0")); + + if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0")); + } else { + Settings.I2C_Multiplexer_Addr = -1; + } + Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0")); +# endif // if FEATURE_I2CMULTIPLEXER + } +# if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code + + if ((getI2CBusCount() > 1) && isFormItem(F("pi2csp1"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(1)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda); + update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl); + } + Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1")); + + if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1")); + } else { + Settings.I2C2_Multiplexer_Addr = -1; + } + Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1")); +# endif // if FEATURE_I2CMULTIPLEXER + } + + +# if FEATURE_I2C_INTERFACE_3 + + if ((getI2CBusCount() > 2) && isFormItem(F("pi2csp2"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(2)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda); + update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl); + } + Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2")); + + if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2")); + } else { + Settings.I2C3_Multiplexer_Addr = -1; + } + Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2")); +# endif // if FEATURE_I2CMULTIPLEXER + } +# endif // if FEATURE_I2C_INTERFACE_3 + +# endif // if FEATURE_I2C_MULTIPLE + + return updated; +} + +#endif + + +#if FEATURE_SPI +bool save_SPI(String& error) { +# ifdef ESP32 + bool SPI_updated{}; + + if (update_whenset_FormItemInt(F("initspi0"), Settings.InitSPI)) { + SPI_updated = true; + + // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + } + + if (update_whenset_FormItemInt(F("initspi1"), Settings.InitSPI1)) { + SPI_updated = true; + + // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + } + + if (SPI_updated) { + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks + error += strformat(F("SPI bus %u pins not configured correctly!
"), spi_bus); + } + } + } + return SPI_updated; +# else // for ESP8266 we keep the old UI + const auto old_initSPI = Settings.InitSPI; + Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init + return old_initSPI != Settings.InitSPI; +# endif // ifdef ESP32 +} + +#endif + +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB +bool save_MODBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +bool save_CAN(String& error) { + return false; // TODO +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +bool save_WRMBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +bool save_WIMBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_WIMBUS + +#if FEATURE_I2C +void interfaces_show_I2C() { +# if FEATURE_I2CMULTIPLEXER + const __FlashStringHelper *i2c_muxtype_options[] = { + F("- None -"), + F("TCA9548a - 8 channel"), + F("TCA9546a - 4 channel"), + F("TCA9543a - 2 channel"), + F("PCA9540 - 2 channel (experimental)") + }; + const int i2c_muxtype_choices[] = { + I2C_MULTIPLEXER_NONE, + I2C_MULTIPLEXER_TCA9548A, + I2C_MULTIPLEXER_TCA9546A, + I2C_MULTIPLEXER_TCA9543A, + I2C_MULTIPLEXER_PCA9540 + }; + const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices), + i2c_muxtype_options, i2c_muxtype_choices); + + // Select the I2C address for a port multiplexer + + String i2c_mux_options[9]; + int i2c_mux_choices[9]; + uint8_t mux_opt = 0; + i2c_mux_options[mux_opt] = F("- None -"); + i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE; + mux_opt++; + + for (int8_t x = 0; x < 8; x++) { + i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x); + + if (x == 0) { // PCA9540 has a fixed address 0f 0x70 + i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]"); + } else if (x < 4) { + i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]"); + } else { + i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]"); + } + i2c_mux_choices[mux_opt] = 0x70 + x; + mux_opt++; + } + const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices); +# endif // if FEATURE_I2CMULTIPLEXER + + uint8_t i2cBus = 0; +# if FEATURE_I2C_MULTIPLE + + for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) +# endif // if FEATURE_I2C_MULTIPLE + { +# if !FEATURE_I2C_MULTIPLE + addFormSubHeader(F("I2C Bus")); +# else + addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus)); +# endif // if !FEATURE_I2C_MULTIPLE + addFormDetailsStart((0 == i2cBus) || Settings.isI2CEnabled(i2cBus)); +# if FEATURE_PLUGIN_PRIORITY + + if (isI2CPriorityTaskActive(i2cBus)) { + I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus); + } else +# endif // if FEATURE_PLUGIN_PRIORITY + { + addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus)); + addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus)); + } + addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000); + addUnit(F("Hz")); + addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most.")); + addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100, + 3400000); + addUnit(F("Hz")); + + if (0 == i2cBus) { // Only support Clock-stretching on Bus 1 + addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0); +# ifdef ESP8266 + addUnit(F("usec")); +# endif +# ifdef ESP32 + addUnit(F("1/80 usec")); +# endif + } + +# if FEATURE_I2CMULTIPLEXER +# if !FEATURE_I2C_MULTIPLE + addFormSubHeader(F("I2C Multiplexer")); +# else // if !FEATURE_I2C_MULTIPLE + addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus)); +# endif // if !FEATURE_I2C_MULTIPLE + + addFormDetailsStart(Settings.getI2CMultiplexerType(i2cBus) != I2C_MULTIPLEXER_NONE); + // Select the type of multiplexer to use + { + muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus)); + addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus), + Settings.getI2CMultiplexerAddr(i2cBus)); + + // addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"), + // i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus)); + const String id = strformat(F("pi2cmuxreset%u"), i2cBus); + addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id); + addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus)); + addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540.")); + } + addFormDetailsEnd(); +# endif // if FEATURE_I2CMULTIPLEXER + addFormDetailsEnd(); + } +} +#endif + +#if FEATURE_SPI +void interfaces_show_SPI() { + +# ifdef ESP32 + + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + // SPI Init + addFormSubHeader(concat(F("SPI Bus "), spi_bus)); + { + addFormDetailsStart(0 == spi_bus || ((0 != spi_bus) && (static_cast(Settings.InitSPI1) != SPI_Options_e::None))); + // Script to show GPIO pins for User-defined SPI GPIOs + // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : + // 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display + // = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), + // Minified: + html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9<=e.value?'':'none';" + "document.getElementById('tr_spipinsclk%u').style.display=i," + "document.getElementById('tr_spipinmiso%u').style.display=i," + "document.getElementById('tr_spipinmosi%u').style.display=i" + "}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus), + false); + const __FlashStringHelper *spi_options[] = { + getSPI_optionToString(SPI_Options_e::None), + getSPI_optionToString(SPI_Options_e::Vspi_Fspi), +# ifdef ESP32_CLASSIC + getSPI_optionToString(SPI_Options_e::Hspi), +# endif + getSPI_optionToString(SPI_Options_e::UserDefined_VSPI) +# if SOC_SPI_PERIPH_NUM > 2 + , getSPI_optionToString(SPI_Options_e::UserDefined_HSPI) +# endif + }; + const int spi_index[] = { + static_cast(SPI_Options_e::None), + static_cast(SPI_Options_e::Vspi_Fspi), +# ifdef ESP32_CLASSIC + static_cast(SPI_Options_e::Hspi), +# endif + static_cast(SPI_Options_e::UserDefined_VSPI) +# if SOC_SPI_PERIPH_NUM > 2 + , static_cast(SPI_Options_e::UserDefined_HSPI) +# endif + }; + constexpr size_t nrOptions = NR_ELEMENTS(spi_index); + FormSelectorOptions selector(nrOptions, spi_options, spi_index); + selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus); + selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), + concat(F("initspi"), spi_bus), + 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1); + int8_t spi_gpio[3]; + Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured + // User-defined pins + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]); + addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); + html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script + // addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); + addFormDetailsEnd(); + } + } +# else // for ESP8266 we keep the existing UI + addFormSubHeader(F("SPI Bus 0")); + addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); + addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); +# endif // ifdef ESP32 +} +#endif + +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB +void interfaces_show_MODBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +void interfaces_show_CAN() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +void interfaces_show_WRMBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +void interfaces_show_WIMBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_WIMBUS + +#if FEATURE_I2C +# if FEATURE_PLUGIN_PRIORITY + +bool isI2CPriorityTaskActive(uint8_t i2cBus) { + bool hasI2CPriorityTask = false; + + for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) { + hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus); + } + return hasI2CPriorityTask; +} + +void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) { + int pinnr = -1; + bool input, output, warning = false; + + addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus)); + addRowLabel(formatGpioName_bidirectional(F("SDA"))); + getGpioInfo(i2c_sda, pinnr, input, output, warning); + addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false)); + addRowLabel(formatGpioName_output(F("SCL"))); + getGpioInfo(i2c_scl, pinnr, input, output, warning); + addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false)); +} + +# endif // if FEATURE_PLUGIN_PRIORITY +#endif + +#endif // ifdef WEBSERVER_INTERFACES diff --git a/src/src/WebServer/InterfacesPage.h b/src/src/WebServer/InterfacesPage.h new file mode 100644 index 000000000..a96601c7a --- /dev/null +++ b/src/src/WebServer/InterfacesPage.h @@ -0,0 +1,57 @@ +#pragma once + +#include "../WebServer/common.h" + +#ifdef WEBSERVER_INTERFACES + +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +void handle_interfaces(); + +void save_interfaces(); +#if FEATURE_I2C +void handle_interfaces_i2c(); +bool save_I2C(String& error); +void interfaces_show_I2C(); +#endif +#if FEATURE_SPI +void handle_interfaces_spi(); +bool save_SPI(String& error); +void interfaces_show_SPI(); +#endif + +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB +void handle_interfaces_modbus(); +bool save_MODBUS(String& error); +void interfaces_show_MODBUS(); +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +void handle_interfaces_can(); +bool save_CAN(String& error); +void interfaces_show_CAN(); +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +void handle_interfaces_wrmbus(); +bool save_WRMBUS(String& error); +void interfaces_show_WRMBUS(); +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +void handle_interfaces_wimbus(); +bool save_WIMBUS(String& error); +void interfaces_show_WIMBUS(); +#endif // if FEATURE_WIMBUS + + +#if FEATURE_I2C +# if FEATURE_PLUGIN_PRIORITY +bool isI2CPriorityTaskActive(uint8_t i2cBus); +void I2CShowSdaSclReadonly(int8_t i2c_sda, + int8_t i2c_scl, + uint8_t i2cBus); +# endif +#endif +#endif diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 3e1e217fd..366fe656b 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -247,6 +247,7 @@ void handle_json() LabelType::FLASH_CHIP_SPEED, LabelType::FLASH_IDE_MODE, LabelType::FS_SIZE, + LabelType::FS_FREE, LabelType::SUNRISE, LabelType::SUNSET, @@ -632,7 +633,7 @@ void handle_json() # endif int8_t channel = Settings.I2C_Multiplexer_Channel[TaskIndex]; - if (bitRead(Settings.I2C_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { KeyValueStruct kv(F("I2CBus")); diff --git a/src/src/WebServer/Log.cpp b/src/src/WebServer/Log.cpp index 75d7fe265..b645036fe 100644 --- a/src/src/WebServer/Log.cpp +++ b/src/src/WebServer/Log.cpp @@ -1,11 +1,14 @@ #include "../WebServer/Log.h" +#ifdef WEBSERVER_LOG + #include "../WebServer/ESPEasy_WebServer.h" #include "../WebServer/404.h" #include "../WebServer/HTML_wrappers.h" #include "../WebServer/JSON.h" #include "../WebServer/Markup.h" #include "../WebServer/Markup_Buttons.h" +#include "../WebServer/Markup_Forms.h" #include "../DataStructs/LogBuffer.h" #include "../DataStructs/TimingStats.h" @@ -35,6 +38,7 @@ void handle_log() { "
Logging:
")); addHtml(F("Autoscroll: ")); addCheckBox(F("autoscroll"), true); + addFormTextBox(F("Filter"), F("logfilter"), "", 30); addHtml(F("
")); serve_JS(JSfiles_e::FetchAndParseLog); @@ -148,3 +152,4 @@ void handle_log_JSON() { handleNotFound(); #endif // ifdef WEBSERVER_LOG } +#endif \ No newline at end of file diff --git a/src/src/WebServer/Log.h b/src/src/WebServer/Log.h index 2c3198789..74cad153e 100644 --- a/src/src/WebServer/Log.h +++ b/src/src/WebServer/Log.h @@ -3,7 +3,7 @@ #include "../WebServer/common.h" - +#ifdef WEBSERVER_LOG // ******************************************************************************** // Web Interface log page // ******************************************************************************** @@ -13,7 +13,7 @@ void handle_log(); // Web Interface JSON log page // ******************************************************************************** void handle_log_JSON(); - +#endif #endif \ No newline at end of file diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 7153a1add..a85593e58 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -12,6 +12,7 @@ #include "../Helpers/Convert.h" #include "../Helpers/ESPEasy_UnitOfMeasure.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/StringConverter_Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -114,8 +115,12 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int bool input, output, warning; if (getGpioInfo(gpio, pinnr, input, output, warning)) { +#if FEATURE_I2C bool includeI2C = true; +#endif +#if FEATURE_SPI bool includeSPI = true; +#endif bool includeSerial = true; #if FEATURE_ETHERNET bool includeEthernet = true; @@ -127,6 +132,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int // bool includeResetPin = true; switch (purpose) { +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: includeSPI = false; @@ -134,6 +140,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int return; } break; +#endif #if FEATURE_ETHERNET case PinSelectPurpose::Ethernet: includeEthernet = false; @@ -160,7 +167,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int return; } break; - +#if FEATURE_I2C case PinSelectPurpose::I2C: #if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -169,6 +176,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int #endif #endif includeI2C = false; +#endif // fallthrough case PinSelectPurpose::Generic_bidir: @@ -217,19 +225,20 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int break; } - +#if FEATURE_I2C if (includeI2C && Settings.isI2C_pin(gpio)) { disabled = true; } +#endif if (includeSerial && isSerialConsolePin(gpio)) { disabled = true; } - +#if FEATURE_SPI if (includeSPI && Settings.isSPI_pin(gpio)) { disabled = true; } - +#endif // Not blocking these GPIO pins, as they may already be in dual-purpose use, just a place-holder // if (includeStatusLed && (Settings.Pin_status_led == gpio)) { // disabled = true; @@ -530,6 +539,15 @@ void addFormHeader(const __FlashStringHelper *header, html_table_header(F("")); } +void addFormHeader(const String& header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 300); + html_table_header(F("")); +} + /* void addFormHeader(const String& header, const String& helpButton) { addFormHeader(header, helpButton, EMPTY_STRING); @@ -543,6 +561,41 @@ void addFormHeader(const String& header, const String& helpButton, const String& } */ +// ******************************************************************************** +// Add a detail wrapper start & end, terminates the page-table, and starts a new page table +// ******************************************************************************** +#ifndef BUILD_MINIMAL_OTA +void addFormDetailsStart(const bool initialOpen) { + addFormDetailsStart(F("Details..."), initialOpen); +} + +void addFormDetailsStart(const __FlashStringHelper *caption, const bool initialOpen) +{ + html_end_table(); + addHtml(strformat(F("
"), FsP(initialOpen ? F("open") : F("")))); + addHtml(F("")); + addHtml(caption); + addHtml(F("")); + html_table_class_normal(); + addFormFixedFirstColumn(); +} + +void addFormDetailsEnd() +{ + html_end_table(); + addHtml(F("
")); + html_table_class_normal(); + addFormFixedFirstColumn(); +} + +// Fix first table column at 25vw (view width %) via css class 'tc1', as we work with multiple tables that should be vertically aligned +// This must be added as the first element in a table definition +void addFormFixedFirstColumn() +{ + addHtml(F("")); +} +#endif // ifndef BUILD_MINIMAL_OTA + // ******************************************************************************** // Add a sub header // ******************************************************************************** diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index cf2720a1b..df8f4cf51 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -170,6 +170,9 @@ void addFormHeader(const __FlashStringHelper *header, void addFormHeader(const __FlashStringHelper *header, const __FlashStringHelper *helpButton, const __FlashStringHelper *rtdHelpButton); +void addFormHeader(const String& header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton); /* void addFormHeader(const String& header, @@ -179,6 +182,21 @@ void addFormHeader(const __FlashStringHelper *header, const String& rtdHelpButton); */ +// ******************************************************************************** +// Add a detail wrapper start & end, terminates the page-table, and starts a new page table +// Feature disabled in MIMNIMAL_OTA builds for reduced bin size +// ******************************************************************************** +#ifndef BUILD_MINIMAL_OTA +void addFormDetailsStart(const bool initialOpen); +void addFormDetailsStart(const __FlashStringHelper *caption, const bool initialOpen); +void addFormDetailsEnd(); +void addFormFixedFirstColumn(); +#else // ifndef BUILD_MINIMAL_OTA +#define addFormDetailsStart(...) +#define addFormDetailsEnd() +#define addFormFixedFirstColumn() +#endif // ifndef BUILD_MINIMAL_OTA + // ******************************************************************************** // Add a sub header // ******************************************************************************** diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 8580a8de0..524085b3d 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -8,6 +8,7 @@ #include "../Globals/Settings.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -464,6 +465,11 @@ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __Fla addPinSelect(purpose, id, choice); } +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice) { + addRowLabel_tr_id(label, id); + addPinSelect(purpose, id, choice); +} + void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice) { addRowLabel_tr_id(label, id); @@ -491,7 +497,7 @@ void addFormPinSelect(const String& label, const String & id, int choice) addPinSelect(PinSelectPurpose::Generic, id, choice); } */ - +#if FEATURE_I2C void addFormPinSelectI2C(const String& label, const String& id, uint8_t i2cBus, int choice) { addRowLabel_tr_id(label, id); @@ -529,7 +535,7 @@ void addFormSelectorI2C(const String& id, } addSelector_Foot(); } - +#endif void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, int selectedIndex, @@ -563,10 +569,12 @@ void addFormPinStateSelect(int gpio, int choice) // do not add the pin state select for these pins. enabled = false; } +#if FEATURE_ETHERNET if (Settings.isEthernetPin(gpio)) { // do not add the pin state select for non-optional Ethernet pins enabled = false; } +#endif int pinnr = -1; bool input, output, warning; @@ -654,6 +662,16 @@ bool getCheckWebserverArg_int(const String& key, int& value) { return res; } +bool getCheckWebserverArg_int(const String& key, + uint32_t & value) { + const String valueStr = webArg(key); + if (valueStr.isEmpty()) return false; + uint32_t tmp{}; + const bool res = validUIntFromString(valueStr, tmp); + value = tmp; + return res; +} + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int & value) { @@ -670,6 +688,25 @@ bool update_whenset_FormItemInt(const String& key, int& value) { return false; } +bool update_whenset_FormItemInt(const __FlashStringHelper * key, + uint32_t & value) +{ + return update_whenset_FormItemInt(String(key), value); +} + +bool update_whenset_FormItemInt(const String& key, + uint32_t & value) +{ + uint32_t tmpVal; + + if (getCheckWebserverArg_int(key, tmpVal)) { + value = tmpVal; + return true; + } + return false; +} + + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int8_t& value) { diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 2251e76dd..29d6e2021 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -271,9 +271,10 @@ void addFormPinSelect(const __FlashStringHelper * label, int choice); */ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __FlashStringHelper * id, int choice); +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice); void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice); - +#if FEATURE_I2C void addFormPinSelectI2C(const String& label, const String& id, uint8_t i2cBus, @@ -289,6 +290,7 @@ void addFormSelectorI2C(const String& id, const String& tooltip = EMPTY_STRING #endif ); +#endif void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, @@ -317,12 +319,21 @@ int getFormItemInt(const String& key, int defaultValue); bool getCheckWebserverArg_int(const String& key, int & value); +bool getCheckWebserverArg_int(const String& key, + uint32_t & value); + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int & value); bool update_whenset_FormItemInt(const String& key, int & value); +bool update_whenset_FormItemInt(const __FlashStringHelper * key, + uint32_t & value); + +bool update_whenset_FormItemInt(const String& key, + uint32_t & value); + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int8_t & value); diff --git a/src/src/WebServer/NetworkPage.cpp b/src/src/WebServer/NetworkPage.cpp index 4a7ec01c6..69964d2b0 100644 --- a/src/src/WebServer/NetworkPage.cpp +++ b/src/src/WebServer/NetworkPage.cpp @@ -160,12 +160,20 @@ void handle_networks_CopySubmittedSettings_NWPluginCall(ESPEasy::net::networkInd # endif # ifdef ESP32 Settings.setRoutePrio_for_network(networkindex, getFormItemInt(F("routeprio"), 0)); + Settings.setNetworkInterface_isFallback(networkindex, isFormItemChecked(F("fallback"))); Settings.setNetworkInterfaceSubnetBlockClientIP(networkindex, isFormItemChecked(F("block_web_access"))); # endif // ifdef ESP32 +# ifdef ESP8266 + if (networkindex == 1) { + // Only add fallback checkbox to AP on ESP8266 + Settings.setNetworkInterface_isFallback(networkindex, isFormItemChecked(F("fallback"))); + } +# endif // ifdef ESP32 + # if FEATURE_USE_IPV6 Settings.setNetworkEnabled_IPv6(networkindex, isFormItemChecked(F("en_ipv6"))); # endif - Settings.setNetworkInterfaceStartupDelayAtBoot(networkindex, getFormItemInt(F("delay_start"))); + Settings.setNetworkInterfaceStartupDelay(networkindex, getFormItemInt(F("delay_start"))); String dummy; ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBFORM_SAVE, &TempEvent, dummy); } @@ -258,6 +266,9 @@ void handle_networks_ShowAllNetworksTable() if (TempEvent.Par2) { addHtml(F("(*)")); } +// if (TempEvent.Par3 > 0) { +// addHtml(strformat(F("
%u ms"), TempEvent.Par3)); +// } } break; # endif // ifdef ESP32 @@ -375,11 +386,21 @@ void handle_networks_NetworkSettingsPage(ESPEasy::net::networkIndex_t networkind Settings.getRoutePrio_for_network(networkindex), 0, 255); addFormNote(F("The active interface with highest priority will be used for default route (gateway).")); + addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); +# endif +# ifdef ESP8266 + if (networkindex == 1) { + // Only add fallback checkbox to AP on ESP8266 + addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); + } # endif // ifdef ESP32 - addFormCheckBox(F("Block Web Access"), F("block_web_access"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkindex)); - addFormNote(F("When checked, any host from a subnet on this network interface will be blocked")); - addFormNumericBox(F("Delay Startup At Boot"), F("delay_start"), Settings.getNetworkInterfaceStartupDelayAtBoot(networkindex), 0, 60000); + addFormNumericBox(F("Delay Startup"), F("delay_start"), Settings.getNetworkInterfaceStartupDelay(networkindex), 0, 60000); addUnit(F("ms")); +#ifdef ESP32 + addFormNote(F("For fallback interface, it is the delay after connection of primary interface has failed. For non-fallback it is the delay from boot")); +#endif + addFormCheckBox(F("Block Web Access"), F("block_web_access"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkindex)); + addFormNote(F("When checked, any host from a subnet on this network interface will be blocked to access the ESPEasy web interface")); # if FEATURE_USE_IPV6 addFormCheckBox(F("Enable IPv6"), F("en_ipv6"), Settings.getNetworkEnabled_IPv6(networkindex)); diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 7eb9929a5..0762cf97d 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -640,6 +640,7 @@ void handle_sysinfo_Storage() { LabelType::OTA_2STEP, # endif // ifdef ESP8266 LabelType::FS_SIZE, + LabelType::FS_FREE, LabelType::MAX_LABEL }; diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index 49ead277e..7f88138ec 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -55,6 +55,14 @@ #endif #endif // ifndef MENU_INDEX_HARDWARE_VISIBLE +#ifndef MENU_INDEX_INTERFACES_VISIBLE +#ifdef WEBSERVER_INTERFACES + # define MENU_INDEX_INTERFACES_VISIBLE true +#else + # define MENU_INDEX_INTERFACES_VISIBLE false +#endif +#endif + #ifndef MENU_INDEX_DEVICES_VISIBLE #ifdef WEBSERVER_DEVICES # define MENU_INDEX_DEVICES_VISIBLE true @@ -107,6 +115,25 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { case MENU_INDEX_NETWORK: return ICON("🌐"); // Alternative 🖧 (not working on Apple) case MENU_INDEX_CONTROLLERS: return ICON("✆"); case MENU_INDEX_HARDWARE: return ICON("☷"); + case MENU_INDEX_INTERFACES: return ICON("❉"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: return ICON("I²C"); +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: return ICON("SPI"); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + case MENU_INDEX_INTERFACES_MODBUS: return ICON("Modbus"); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: return ICON("CAN bus"); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return ICON("mBus"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return ICON("w-mBus"); +#endif // if FEATURE_WIMBUS case MENU_INDEX_DEVICES: return ICON("❖"); case MENU_INDEX_RULES: return ICON("⧴"); case MENU_INDEX_NOTIFICATIONS: return ICON("✉"); @@ -117,15 +144,37 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { const __FlashStringHelper* getGpMenuLabel(uint8_t index) { switch (index) { - case MENU_INDEX_MAIN: return F("Main"); - case MENU_INDEX_CONFIG: return F("Config"); - case MENU_INDEX_NETWORK: return F("Network"); - case MENU_INDEX_CONTROLLERS: return F("Controllers"); - case MENU_INDEX_HARDWARE: return F("Hardware"); - case MENU_INDEX_DEVICES: return F("Devices"); - case MENU_INDEX_RULES: return F("Rules"); - case MENU_INDEX_NOTIFICATIONS: return F("Notifications"); - case MENU_INDEX_TOOLS: return F("Tools"); + case MENU_INDEX_MAIN: return F("Main"); + case MENU_INDEX_CONFIG: return F("Config"); + case MENU_INDEX_NETWORK: return F("Network"); + case MENU_INDEX_CONTROLLERS: return F("Controllers"); + case MENU_INDEX_HARDWARE: return F("Hardware"); + case MENU_INDEX_INTERFACES: return F("Interfaces"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: +#endif // if FEATURE_SPI +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + case MENU_INDEX_INTERFACES_MODBUS: +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: +#endif // if FEATURE_CAN +#if FEATURE_I2C || FEATURE_SPI || (FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB) || FEATURE_CAN + break; // No label, only an 'icon', for the second-level menu +#endif // if FEATURE_I2C || FEATURE_SPI || FEATURE_MODBUS || FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return F("(wired)"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return F("(wireless)"); +#endif // if FEATURE_WIMBUS + case MENU_INDEX_DEVICES: return F("Devices"); + case MENU_INDEX_RULES: return F("Rules"); + case MENU_INDEX_NOTIFICATIONS: return F("Notifications"); + case MENU_INDEX_TOOLS: return F("Tools"); } return F(""); } @@ -137,6 +186,25 @@ const __FlashStringHelper* getGpMenuURL(uint8_t index) { case MENU_INDEX_NETWORK: return F("/network"); case MENU_INDEX_CONTROLLERS: return F("/controllers"); case MENU_INDEX_HARDWARE: return F("/hardware"); + case MENU_INDEX_INTERFACES: return F("/interfaces"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: return F("/interfaces_i2c"); +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: return F("/interfaces_spi"); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS && FEATURE_MODBUS_INTERFACES_TAB + case MENU_INDEX_INTERFACES_MODBUS: return F("/interfaces_modbus"); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: return F("/interfaces_can"); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return F("/interfaces_wrmbus"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return F("/interfaces_wimbus"); +#endif // if FEATURE_WIMBUS case MENU_INDEX_DEVICES: return F("/devices"); case MENU_INDEX_RULES: return F("/rules"); case MENU_INDEX_NOTIFICATIONS: return F("/notifications"); @@ -152,6 +220,19 @@ bool GpMenuVisible(uint8_t index) { case MENU_INDEX_NETWORK: return MENU_INDEX_NETWORK_VISIBLE; case MENU_INDEX_CONTROLLERS: return MENU_INDEX_CONTROLLERS_VISIBLE; case MENU_INDEX_HARDWARE: return MENU_INDEX_HARDWARE_VISIBLE; + case MENU_INDEX_INTERFACES: return MENU_INDEX_INTERFACES_VISIBLE; + case MENU_INDEX_INTERFACES_I2C: return (1 == FEATURE_I2C); + case MENU_INDEX_INTERFACES_SPI: return (1 == FEATURE_SPI); + case MENU_INDEX_INTERFACES_MODBUS: return (1 == FEATURE_MODBUS && 1 == FEATURE_MODBUS_INTERFACES_TAB); + #if defined(FEATURE_CAN) + case MENU_INDEX_INTERFACES_CAN: return (1 == FEATURE_CAN); + #endif // if defined(FEATURE_CAN) + #if defined(FEATURE_WRMBUS) + case MENU_INDEX_INTERFACES_WRMBUS: return (1 == FEATURE_WRMBUS); + #endif // if defined(FEATURE_WRMBUS) + #if defined(FEATURE_WIMBUS) + case MENU_INDEX_INTERFACES_WIMBUS: return (1 == FEATURE_WIMBUS); + #endif // if defined(FEATURE_WIMBUS) case MENU_INDEX_DEVICES: return MENU_INDEX_DEVICES_VISIBLE; case MENU_INDEX_RULES: return MENU_INDEX_RULES_VISIBLE; case MENU_INDEX_NOTIFICATIONS: return MENU_INDEX_NOTIFICATIONS_VISIBLE; @@ -160,6 +241,28 @@ bool GpMenuVisible(uint8_t index) { return false; } +bool isGpMenuSecondLevel(uint8_t index) { + switch (index) { + case MENU_INDEX_MAIN: + case MENU_INDEX_CONFIG: + case MENU_INDEX_NETWORK: + case MENU_INDEX_CONTROLLERS: + case MENU_INDEX_HARDWARE: + case MENU_INDEX_INTERFACES: + case MENU_INDEX_DEVICES: + case MENU_INDEX_RULES: + case MENU_INDEX_NOTIFICATIONS: + case MENU_INDEX_TOOLS: return false; + case MENU_INDEX_INTERFACES_WIMBUS: + case MENU_INDEX_INTERFACES_WRMBUS: + case MENU_INDEX_INTERFACES_CAN: + case MENU_INDEX_INTERFACES_MODBUS: + case MENU_INDEX_INTERFACES_SPI: + case MENU_INDEX_INTERFACES_I2C: return true; + } + return false; +} + bool WebTemplateParser::process(const char c) { switch (c) { case '{': @@ -404,9 +507,20 @@ void WebTemplateParser::getWebPageTemplateVar(const String& varName) if (equals(varName, F("menu"))) { - addHtml(F("