diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..3bcf7571 --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +# .clang-format +Language: Proto +BasedOnStyle: Google + +ColumnLimit: 0 +AlignConsecutiveAssignments: Consecutive +AlignTrailingComments: + Kind: Never +SpacesBeforeTrailingComments: 1 +IndentWidth: 2 +UseTab: Never diff --git a/.github/workflows/buf-checks.yml b/.github/workflows/buf-checks.yml deleted file mode 100644 index c9d1f4f9..00000000 --- a/.github/workflows/buf-checks.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Linting and Backwards-Compatibility Checks -on: [pull_request] -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: checkout - if: success() - uses: actions/checkout@v1 - with: - ref: master - - name: checkout-master - if: success() - run: git checkout master - - name: checkout - if: success() - uses: actions/checkout@v1 - - name: make local - if: success() - run: make local diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..f8cfad9d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,75 @@ +name: Build +on: + pull_request: + push: + branches: [master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Tool Dependencies + uses: jdx/mise-action/@v2 + + - name: Lib Dependencies + run: | + pip install protobuf + pip install setuptools==75.3.4 + go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@v1.5.1 + npm ci + + - name: Env Debug Info + continue-on-error: true # this is a purely informational step + run: | + protoc --version # provided by: mise + protolint -v # provided by: mise + go version # provided by: mise + # this line doesn't work, not sure why + # which protoc-gen-doc # provided by: go install ... + python --version # provided by: mise + pip --version # provided by: mise + pip list # provided by: pip install ... + node --version # provided by: mise + npm --version # provided by: mise + + - name: Build JS + run: bin/build-js + + - name: Build Arduino + run: bin/build-arduino + + - name: Build Python + run: bin/build-python + + - name: Build Documentation + run: bin/build-docs + + - uses: actions/upload-artifact@v4 + with: + name: javascript-wrapper + path: ./js_out + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + with: + name: arduino-wrapper + path: ./arduino_out + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + with: + name: python-wrapper + path: ./python_out + if-no-files-found: error + + - uses: actions/upload-artifact@v4 + with: + name: documentation + path: ./docs_out + if-no-files-found: error + + - name: Lint + run: bin/lint diff --git a/.github/workflows/protoc-wrapper-generation.yml b/.github/workflows/protoc-wrapper-generation.yml deleted file mode 100644 index 27987a7b..00000000 --- a/.github/workflows/protoc-wrapper-generation.yml +++ /dev/null @@ -1,134 +0,0 @@ -name: Generate .proto Wrapper Files -on: - push: - branches: master - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: write - pages: write - id-token: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - # Setup - - uses: actions/setup-python@v5 - with: - python-version: '3.x' - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - version: '3.13.0' - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install Python dependencies - run : | - python -m pip install --upgrade pip - pip install protobuf setuptools - - name: Self Checkout - uses: actions/checkout@v4 - with: - path: protobuf-checkout - submodules: true - - name: Configure Git - run: | - git config --global user.name adafruitio - git config --global user.email adafruitio@adafruit.com - - # Arduino wrappers for the microcontrollers - - name: Generate Arduino files from .proto Files - env: - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python - run: | - mkdir ./arduino_out - mkdir ./doc - echo "Installing protobuf-gen-doc" - go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest - cp ~/go/bin/protoc-gen-doc . - echo "Generating docs..." - protoc --plugin=protobuf-checkout/nanopb/generator/protoc-gen-nanopb -Iprotobuf-checkout/nanopb/generator/proto --proto_path=./protobuf-checkout/proto ./protobuf-checkout/proto/wippersnapper/*/*/*.proto --nanopb_out=./arduino_out --nanopb_opt=-I./protobuf-checkout/proto --nanopb_opt=-t --plugin=protoc-gen-doc=./protoc-gen-doc --doc_out=./doc --doc_opt=html,index.html - # Local (non-CI) build command: - # protoc --plugin=nanopb/generator/protoc-gen-nanopb -Inanopb/generator/proto --proto_path=./proto ./proto/wippersnapper/*/*/*.proto --nanopb_out=./arduino_out --nanopb_opt=-I./proto --nanopb_opt=-t --plugin=protoc-gen-doc=./protoc-gen-doc --doc_out=./doc --doc_opt=html,index.html - - - name: Checkout Arduino Repo - uses: actions/checkout@v4 - with: - repository: adafruit/Adafruit_Wippersnapper_Arduino - token: ${{ secrets.IO_BOT_PAT }} - path: arduino-checkout - - name: Copy Generated Arduino Files to Arduino Repo - run: | - rm -rf ./arduino-checkout/src/wippersnapper/ - mkdir ./arduino-checkout/src/wippersnapper/ - cp -a ./arduino_out/. ./arduino-checkout/src/ - - name: Open Arduino Pull Request - env: - GITHUB_USER: adafruitio - GITHUB_TOKEN: ${{ secrets.IO_BOT_PAT }} - PROTOBUF_BRANCH_NAME: protobuf-update - run: | - cd arduino-checkout - git checkout -b $PROTOBUF_BRANCH_NAME-$GITHUB_SHA - git status - git add src - git commit -m "Updating protobuf wrappers" || exit 0 # quit cleanly if nothing to commit - hub push origin $PROTOBUF_BRANCH_NAME-$GITHUB_SHA - hub pull-request --base main --message ".proto file wrappers updated" --message "Auto-generated by [GitHub Actions on Protobuf Repo][1]" --message "[1]: https://github.com/adafruit/Wippersnapper_Protobuf/blob/master/.github/workflows/protoc-wrapper-generation.yml" - - # NodeJS wrappers for the mqtt broker - - name: Generate JS Wrapper Files from .proto Files - run: | - mkdir ./js_out - protoc --proto_path=./protobuf-checkout/proto ./protobuf-checkout/proto/wippersnapper/*/*/*.proto --js_out=import_style=commonjs,binary:js_out - protoc ./protobuf-checkout/nanopb/generator/proto/nanopb.proto --js_out=import_style=commonjs,binary:js_out - - name: Checkout io-node Repo - uses: actions/checkout@v4 - with: - repository: AdafruitInternalDev/io-node - token: ${{ secrets.IO_BOT_PAT }} - path: js-checkout - ref: main - - name: Copy JS Files - run: | - rm -rf ./js-checkout/lib/wprsnpr/protobufs/wippersnapper/ - cp -r ./js_out/wippersnapper/ ./js-checkout/lib/wprsnpr/protobufs/wippersnapper/ - rm -rf ./js-checkout/lib/wprsnpr/protobufs/nanopb/ - mkdir ./js-checkout/lib/wprsnpr/protobufs/nanopb/ - mv ./js_out/protobuf-checkout/nanopb/generator/proto/nanopb_pb.js ./js-checkout/lib/wprsnpr/protobufs/nanopb/nanopb_pb.js - - name: Open Node Pull Request - env: - GITHUB_USER: adafruitio - GITHUB_TOKEN: ${{ secrets.IO_BOT_PAT }} - PROTOBUF_BRANCH_NAME: protobuf-update - run: | - cd js-checkout - git checkout -b $PROTOBUF_BRANCH_NAME-$GITHUB_SHA - git add lib/wprsnpr/protobufs/ - git commit -m "Updating protobuf wrappers" || exit 0 # quit cleanly if nothing to commit - hub push origin $PROTOBUF_BRANCH_NAME-$GITHUB_SHA - hub pull-request --base main --message ".proto file wrappers updated" --message "Auto-generated by [GitHub Actions on Protobuf Repo][1]" --message "[1]: https://github.com/adafruit/Wippersnapper_Protobuf/blob/master/.github/workflows/protoc-wrapper-generation.yml" - - # Docs - - name: Checkout doc branch - uses: actions/checkout@v4 - with: - ref: gh-pages - path: protobuf-docs-checkout - - name: Commit and push generated HTML Docs file - run: | - cd protobuf-docs-checkout - cp -r ../doc ./doc - git add doc/ - git commit -m "Add generated HTML documentation for $GITHUB_SHA" || exit 0 # quit cleanly if nothing to commit - git push - - name: Setup Pages - uses: actions/configure-pages@v5 - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - path: 'protobuf-docs-checkout/doc' - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/test-wrapper-arduino.yml b/.github/workflows/test-wrapper-arduino.yml deleted file mode 100644 index d658488a..00000000 --- a/.github/workflows/test-wrapper-arduino.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Generate new proto wrapper files and upload artifacts -on: [ pull_request ] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Install Protoc - uses: arduino/setup-protoc@v1 - with: - version: '3.13.0' - - name: Install Python dependencies - run : | - python -m pip install --upgrade pip - pip3 install scons protobuf grpcio-tools - - name: Self Checkout - uses: actions/checkout@v3 - with: - path: protobuf-checkout - submodules: true - - name: Configure Git - run: | - git config --global user.name adafruitio - git config --global user.email adafruitio@adafruit.com - - name: Generate Arduino files from .proto Files - env: - PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python - run: | - mkdir ./arduino_out - mkdir ./doc - echo "Installing protobuf-gen-doc" - go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@latest - cp ~/go/bin/protoc-gen-doc . - echo "Generating docs..." - protoc --plugin=protobuf-checkout/nanopb/generator/protoc-gen-nanopb -Iprotobuf-checkout/nanopb/generator/proto --proto_path=./protobuf-checkout/proto ./protobuf-checkout/proto/wippersnapper/*/*/*.proto --nanopb_out=./arduino_out --nanopb_opt=-I./protobuf-checkout/proto --nanopb_opt=-t --plugin=protoc-gen-doc=./protoc-gen-doc --doc_out=./doc --doc_opt=html,index.html - # Local (non-CI) build command: - # protoc --plugin=nanopb/generator/protoc-gen-nanopb -Inanopb/generator/proto --proto_path=./proto ./proto/wippersnapper/*/*/*.proto --nanopb_out=./arduino_out --nanopb_opt=-I./proto --nanopb_opt=-t --plugin=protoc-gen-doc=./protoc-gen-doc --doc_out=./doc --doc_opt=html,index.html - cp ./doc/index.html ./arduino_out - - uses: actions/upload-artifact@v4 - with: - name: build-artifact-arduino-wrapper - path: ./arduino_out \ No newline at end of file diff --git a/.gitignore b/.gitignore index 920fdfa2..9ff6ebae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,26 @@ +## WipperSnapper_Protobuf stuff + +# submodule +nanopb/ + +# npm libs for buf-es +node_modules/ + +# built wrapper artifacts +python_out +js_out +arduino_out/ + + +## Generic stuff + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class +.DS_Store + +.vscode/ # C extensions *.so @@ -25,8 +44,6 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST -python_out -js_out # PyInstaller # Usually these files are written by a python script from a template @@ -139,4 +156,5 @@ dmypy.json # Cython debug symbols cython_debug/ -.DS_Store \ No newline at end of file + +/.vs diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..f83ee316 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,5 @@ +nodejs 22 +python 3.13 +protoc 33.0 +protolint 0.56.4 +go 1.25 diff --git a/README.md b/README.md index aed9575c..7f20f6a0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Protocol buffer messages for use with [Adafruit.io WipperSnapper](http://io.adaf [Documentation](https://adafruit.github.io/Wippersnapper_Protobuf/) -# This code is for research and educational purposes only +## This code is for research and educational purposes only We use this repository internally for [Adafruit.io WipperSnapper](http://io.adafruit.com/wippersnapper) and published it here. It is not supported, documented, or guaranteed to work outside of WipperSnapper whatsoever! @@ -16,7 +16,8 @@ If you would like to request a component or feature to be added into WipperSnapp This repository includes an automated script for generating the required protocol buffer wrapper messages. -Nanopb header files for Arduino may also be generated by installing `protoc`, cloning this repository, and invoking: +Nanopb header files for Arduino may also be generated by installing `protoc`, cloning this repository, and invoking + ``` git submodule init git submodule update diff --git a/bin/build-arduino b/bin/build-arduino new file mode 100755 index 00000000..5d25892b --- /dev/null +++ b/bin/build-arduino @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# vars +: ${OUT_PATH=arduino_out} + +# move to the directory above this file +cd "${0%/*}/.." + +# include helpful functions +source "./bin/functions.sh" + +clean +activate_mise +ensure protoc +ensure_nanopb_submodule + +# invoke protoc for arduino output +protoc \ + --plugin=nanopb/generator/protoc-gen-nanopb \ + --proto_path=./proto/wippersnapper \ + --proto_path=./nanopb/generator/proto \ + --nanopb_opt=-I./proto/wippersnapper --nanopb_opt=-t \ + --nanopb_out=$OUT_PATH \ + ./proto/wippersnapper/*.proto + +completed_message "Arduino" diff --git a/bin/build-docs b/bin/build-docs new file mode 100755 index 00000000..ae1c89ad --- /dev/null +++ b/bin/build-docs @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# vars +: ${OUT_PATH=docs_out} + +# move to the directory above this file +cd "${0%/*}/.." + +# include helpful functions +source "./bin/functions.sh" + +clean +activate_mise +ensure go + +go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@v1.5.1 + +# invoke protoc for docs output +protoc \ + --proto_path=./proto/wippersnapper \ + ./proto/wippersnapper/*.proto \ + --plugin=`which protoc-gen-doc` \ + --doc_out=./$OUT_PATH \ + --doc_opt=html,index.html + +completed_message "Documentation" + diff --git a/bin/build-js b/bin/build-js new file mode 100755 index 00000000..d733cf42 --- /dev/null +++ b/bin/build-js @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# vars +: ${OUT_PATH=js_out} + +# move to the directory above this file +cd "${0%/*}/.." + +# include helpful functions +source "./bin/functions.sh" + +clean +activate_mise +ensure protoc + +# put buf on the path and invoke protoc +PATH=$PATH:$(pwd)/node_modules/.bin \ + protoc \ + --proto_path=./proto/wippersnapper \ + --es_out $OUT_PATH \ + --es_opt target=js+dts \ + --es_opt js_import_style=legacy_commonjs \ + ./proto/wippersnapper/*.proto + +completed_message "JavaScript" diff --git a/bin/build-python b/bin/build-python new file mode 100755 index 00000000..2552402b --- /dev/null +++ b/bin/build-python @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# vars +: ${OUT_PATH=python_out} + +# move to the directory above this file +cd "${0%/*}/.." + +# include helpful functions +source "./bin/functions.sh" + +clean +activate_mise +ensure protoc +ensure python +ensure_nanopb_submodule + +# invoke protoc for python output +protoc \ + --proto_path=./proto/wippersnapper \ + --proto_path=nanopb/generator/proto \ + --python_out=./$OUT_PATH \ + ./proto/wippersnapper/*.proto + +completed_message "Python" diff --git a/bin/functions.sh b/bin/functions.sh new file mode 100755 index 00000000..8caffd11 --- /dev/null +++ b/bin/functions.sh @@ -0,0 +1,35 @@ +# functions to reuse in the build scripts + +# delete and recreate the out directory +clean() { + rm -rf $OUT_PATH + mkdir $OUT_PATH +} + +# activate mise for managing tool and library versions +activate_mise() { + eval "$(mise activate bash)" +} + +# checks to make sure a tool is on the path +# hints about mise install +ensure() { + if ! which $1 &> /dev/null; then + echo "Could not find $1 on the path! Did you run 'mise install'?" + exit 1 + fi +} + +# checks to ensure the nanopb submodule is populate +# hints how to git submodule +ensure_nanopb_submodule() { + if ! ls nanopb/generator &> /dev/null; then + printf "Did not find nanopb files in the repo! Did you init submodules?\ngit submodule init\ngit submodule update\n" + exit 1 + fi +} + +# message to signify completion +completed_message() { + echo "Built $1 wrappers to $OUT_PATH/" +} diff --git a/bin/lint b/bin/lint new file mode 100755 index 00000000..57eb3689 --- /dev/null +++ b/bin/lint @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# move to the directory above this file +cd "${0%/*}/.." + +# activate mise +eval "$(mise activate bash)" + +# make sure necessary tools are present +if ! which protolint &> /dev/null; then + echo "Could not find protolint on the path! Did you run 'mise install'?" + exit 1 +fi + +# run the linter against our protobuf directory +protolint proto/wippersnapper + diff --git a/bin/test-all b/bin/test-all new file mode 100755 index 00000000..d88b7634 --- /dev/null +++ b/bin/test-all @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +report() { + TOTAL_FILE_SIZE=`du -sh $1 | awk '{print $1}'` + echo "Total file size: $TOTAL_FILE_SIZE" + FILE_COUNT=`ls -1 $1 | wc -l` + echo "File count: $FILE_COUNT" + # stat -c "%n %s" -- $1/* | column -t +} + +echo " +Building all wrappers..." + +time bin/build-python +echo +time bin/build-js +echo +time bin/build-arduino + +echo " +Builds succeeded" + +echo " +Python +======" +report python_out + +echo " +JavaScript +==========" +report js_out + +echo " +Arduino +=======" +report arduino_out diff --git a/buf.yaml b/buf.yaml deleted file mode 100644 index 10acfeed..00000000 --- a/buf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -version: v1beta1 -build: - roots: - - proto -lint: - use: - - DEFAULT - ignore: - - nanopb/nanopb.proto -# TODO: Note that we've turned off breaking change detection -# while we develop the protobuffs, re-enable this in the repo -# for accepting changes to finalized protobufs. -#breaking: -# use: -# - FILE diff --git a/nanopb b/nanopb index 049485ff..6cfe48d6 160000 --- a/nanopb +++ b/nanopb @@ -1 +1 @@ -Subproject commit 049485ff557178f646d573eca3bd647f543b760b +Subproject commit 6cfe48d6f1593f8fa5c0f90437f5e6522587745e diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ad57923c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,257 @@ +{ + "name": "wippersnapper_protobuf", + "version": "2.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wippersnapper_protobuf", + "version": "2.0.0", + "license": "ISC", + "devDependencies": { + "@bufbuild/buf": "^1.65.0", + "@bufbuild/protoc-gen-es": "^2.11.0" + } + }, + "node_modules/@bufbuild/buf": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.65.0.tgz", + "integrity": "sha512-IQmIBB2CGbJAwx1NkuAWMuj4QGPnZ8mujbf4ckx9t6KI9EzfUzql1OyKi9qPrxlLAciI+kBIyPDQ2MIvXTxWUg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "buf": "bin/buf", + "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking", + "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@bufbuild/buf-darwin-arm64": "1.65.0", + "@bufbuild/buf-darwin-x64": "1.65.0", + "@bufbuild/buf-linux-aarch64": "1.65.0", + "@bufbuild/buf-linux-armv7": "1.65.0", + "@bufbuild/buf-linux-x64": "1.65.0", + "@bufbuild/buf-win32-arm64": "1.65.0", + "@bufbuild/buf-win32-x64": "1.65.0" + } + }, + "node_modules/@bufbuild/buf-darwin-arm64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.65.0.tgz", + "integrity": "sha512-2U8CHjW1ysINYKwIPcc4WAiQPxe91RIjNtjpg+RC9rP0aZ7TpGm5MTMY5l3sN4drtmKdb9rBs3bMQsMNhSc90A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-darwin-x64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.65.0.tgz", + "integrity": "sha512-aMqfc6pQC4L9dZpSD61XCEpPWKEtb1rXDPkK0/tzrfTWodnbaJ/elNoxsCGzbZVSMFeAdomUpXmSMrk8ALfWWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-aarch64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.65.0.tgz", + "integrity": "sha512-gzqvY4PLRQ7g0+RlE9g+OL/6yPd5szG7e3Wd5bgjJzfKaQerNiQWaGyPLdcRsIM/WxJhT5e5lG8OrrWHwgQ9Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-armv7": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.65.0.tgz", + "integrity": "sha512-RpYFuPr9MKniD+WNfDgCclyvMu+/w9kK41OWr9sNnbS2BorujskwPiY0iTf5j+8+n/MeAnLIGlyC36+vUB/wIw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-linux-x64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.65.0.tgz", + "integrity": "sha512-0j06h1uKCXlOtrlNcTBkURazT+AwMNvuVxgJsYeUDnSliN05QS7LnBzPOwKg76ariSqlLo+QXk9eNtdhgVjYOg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-arm64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.65.0.tgz", + "integrity": "sha512-KBFsQ3iEityUuLTUCoXAO6ZTGUXWljSjK4upqofsYCb4OJeSeVguD7b09efkQt9ymKsXBt5wQicsRdkMJy/VEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/buf-win32-x64": { + "version": "1.65.0", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.65.0.tgz", + "integrity": "sha512-vJYzHjncSLdy4sPDW8kLqUldHh6Vucg6KabAflm7CDj29lU/HydV8T+nOVsXkoRMUf4+H/qy8WjnSMEtRkaogA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz", + "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@bufbuild/protoc-gen-es": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-2.11.0.tgz", + "integrity": "sha512-VzQuwEQDXipbZ1soWUuAWm1Z0C3B/IDWGeysnbX6ogJ6As91C2mdvAND/ekQ4YIWgen4d5nqLfIBOWLqCCjYUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.11.0", + "@bufbuild/protoplugin": "2.11.0" + }, + "bin": { + "protoc-gen-es": "bin/protoc-gen-es" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@bufbuild/protobuf": "2.11.0" + }, + "peerDependenciesMeta": { + "@bufbuild/protobuf": { + "optional": true + } + } + }, + "node_modules/@bufbuild/protoplugin": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-2.11.0.tgz", + "integrity": "sha512-lyZVNFUHArIOt4W0+dwYBe5GBwbKzbOy8ObaloEqsw9Mmiwv2O48TwddDoHN4itylC+BaEGqFdI1W8WQt2vWJQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@bufbuild/protobuf": "2.11.0", + "@typescript/vfs": "^1.6.2", + "typescript": "5.4.5" + } + }, + "node_modules/@typescript/vfs": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.6.4.tgz", + "integrity": "sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..f3a0c5d2 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "wippersnapper_protobuf", + "version": "2.0.0", + "description": "javascript wrappers for wippersnapper protobufs", + "main": "bin/build-js", + "repository": { + "type": "git", + "url": "git+https://github.com/adafruit/Wippersnapper_Protobuf.git" + }, + "author": "Adafruit IO Team", + "license": "ISC", + "homepage": "https://github.com/adafruit/Wippersnapper_Protobuf#readme", + "devDependencies": { + "@bufbuild/buf": "^1.65.0", + "@bufbuild/protoc-gen-es": "^2.11.0" + } +} diff --git a/proto/nanopb/nanopb.proto b/proto/nanopb/nanopb.proto deleted file mode 100644 index c8067e3a..00000000 --- a/proto/nanopb/nanopb.proto +++ /dev/null @@ -1,179 +0,0 @@ -// Custom options for defining: -// - Maximum size of string/bytes -// - Maximum number of elements in array -// -// These are used by nanopb to generate statically allocable structures -// for memory-limited environments. - -syntax = "proto2"; -import "google/protobuf/descriptor.proto"; - -option java_package = "fi.kapsi.koti.jpa.nanopb"; - -enum FieldType { - FT_DEFAULT = 0; // Automatically decide field type, generate static field if possible. - FT_CALLBACK = 1; // Always generate a callback field. - FT_POINTER = 4; // Always generate a dynamically allocated field. - FT_STATIC = 2; // Generate a static field or raise an exception if not possible. - FT_IGNORE = 3; // Ignore the field completely. - FT_INLINE = 5; // Legacy option, use the separate 'fixed_length' option instead -} - -enum IntSize { - IS_DEFAULT = 0; // Default, 32/64bit based on type in .proto - IS_8 = 8; - IS_16 = 16; - IS_32 = 32; - IS_64 = 64; -} - -enum TypenameMangling { - M_NONE = 0; // Default, no typename mangling - M_STRIP_PACKAGE = 1; // Strip current package name - M_FLATTEN = 2; // Only use last path component - M_PACKAGE_INITIALS = 3; // Replace the package name by the initials -} - -enum DescriptorSize { - DS_AUTO = 0; // Select minimal size based on field type - DS_1 = 1; // 1 word; up to 15 byte fields, no arrays - DS_2 = 2; // 2 words; up to 4095 byte fields, 4095 entry arrays - DS_4 = 4; // 4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays - DS_8 = 8; // 8 words; up to 2^32-1 entry arrays -} - -// This is the inner options message, which basically defines options for -// a field. When it is used in message or file scope, it applies to all -// fields. -message NanoPBOptions { - // Allocated size for 'bytes' and 'string' fields. - // For string fields, this should include the space for null terminator. - optional int32 max_size = 1; - - // Maximum length for 'string' fields. Setting this is equivalent - // to setting max_size to a value of length+1. - optional int32 max_length = 14; - - // Allocated number of entries in arrays ('repeated' fields) - optional int32 max_count = 2; - - // Size of integer fields. Can save some memory if you don't need - // full 32 bits for the value. - optional IntSize int_size = 7 [default = IS_DEFAULT]; - - // Force type of field (callback or static allocation) - optional FieldType type = 3 [default = FT_DEFAULT]; - - // Use long names for enums, i.e. EnumName_EnumValue. - optional bool long_names = 4 [default = true]; - - // Add 'packed' attribute to generated structs. - // Note: this cannot be used on CPUs that break on unaligned - // accesses to variables. - optional bool packed_struct = 5 [default = false]; - - // Add 'packed' attribute to generated enums. - optional bool packed_enum = 10 [default = false]; - - // Skip this message - optional bool skip_message = 6 [default = false]; - - // Generate oneof fields as normal optional fields instead of union. - optional bool no_unions = 8 [default = false]; - - // integer type tag for a message - optional uint32 msgid = 9; - - // decode oneof as anonymous union - optional bool anonymous_oneof = 11 [default = false]; - - // Proto3 singular field does not generate a "has_" flag - optional bool proto3 = 12 [default = false]; - - // Force proto3 messages to have no "has_" flag. - // This was default behavior until nanopb-0.4.0. - optional bool proto3_singular_msgs = 21 [default = false]; - - // Generate an enum->string mapping function (can take up lots of space). - optional bool enum_to_string = 13 [default = false]; - - // Generate bytes arrays with fixed length - optional bool fixed_length = 15 [default = false]; - - // Generate repeated field with fixed count - optional bool fixed_count = 16 [default = false]; - - // Generate message-level callback that is called before decoding submessages. - // This can be used to set callback fields for submsgs inside oneofs. - optional bool submsg_callback = 22 [default = false]; - - // Shorten or remove package names from type names. - // This option applies only on the file level. - optional TypenameMangling mangle_names = 17 [default = M_NONE]; - - // Data type for storage associated with callback fields. - optional string callback_datatype = 18 [default = "pb_callback_t"]; - - // Callback function used for encoding and decoding. - // Prior to nanopb-0.4.0, the callback was specified in per-field pb_callback_t - // structure. This is still supported, but does not work inside e.g. oneof or pointer - // fields. Instead, a new method allows specifying a per-message callback that - // will be called for all callback fields in a message type. - optional string callback_function = 19 [default = "pb_default_field_callback"]; - - // Select the size of field descriptors. This option has to be defined - // for the whole message, not per-field. Usually automatic selection is - // ok, but if it results in compilation errors you can increase the field - // size here. - optional DescriptorSize descriptorsize = 20 [default = DS_AUTO]; - - // Set default value for has_ fields. - optional bool default_has = 23 [default = false]; - - // Extra files to include in generated `.pb.h` - repeated string include = 24; - - // Automatic includes to exlude from generated `.pb.h` - // Same as nanopb_generator.py command line flag -x. - repeated string exclude = 26; - - // Package name that applies only for nanopb. - optional string package = 25; - - // Override type of the field in generated C code. Only to be used with related field types - optional google.protobuf.FieldDescriptorProto.Type type_override = 27; - - // Due to historical reasons, nanopb orders fields in structs by their tag number - // instead of the order in .proto. Set this to false to keep the .proto order. - // The default value will probably change to false in nanopb-0.5.0. - optional bool sort_by_tag = 28 [default = true]; -} - -// Extensions to protoc 'Descriptor' type in order to define options -// inside a .proto file. -// -// Protocol Buffers extension number registry -// -------------------------------- -// Project: Nanopb -// Contact: Petteri Aimonen -// Web site: http://kapsi.fi/~jpa/nanopb -// Extensions: 1010 (all types) -// -------------------------------- - -extend google.protobuf.FileOptions { - optional NanoPBOptions nanopb_fileopt = 1010; -} - -extend google.protobuf.MessageOptions { - optional NanoPBOptions nanopb_msgopt = 1010; -} - -extend google.protobuf.EnumOptions { - optional NanoPBOptions nanopb_enumopt = 1010; -} - -extend google.protobuf.FieldOptions { - optional NanoPBOptions nanopb = 1010; -} - - diff --git a/proto/wippersnapper/analogio.options b/proto/wippersnapper/analogio.options new file mode 100644 index 00000000..255845bc --- /dev/null +++ b/proto/wippersnapper/analogio.options @@ -0,0 +1,4 @@ +# analogio.options +ws.analogio.Add.pin_name max_size: 64 +ws.analogio.Remove.pin_name max_size: 64 +ws.analogio.Event.pin_name max_size: 64 diff --git a/proto/wippersnapper/analogio.proto b/proto/wippersnapper/analogio.proto new file mode 100644 index 00000000..0b7eef17 --- /dev/null +++ b/proto/wippersnapper/analogio.proto @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023-2026 Brent Rubell, Loren Norman, Tyeth Gundry for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.analogio; +import "sensor.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Event event = 1; + } +} + +/** +* SampleMode specifies the pin's sample mode. +*/ +enum SampleMode { + SM_UNSPECIFIED = 0; /** Invalid Sample Mode from Broker. */ + SM_TIMER = 1; /** Periodically sample the pin's value. */ + SM_EVENT = 2; /** Sample the pin's value when an event occurs. */ +} + +/** +* Add adds an analog pin to the device. +*/ +message Add { + string pin_name = 1; /** Name of the pin. */ + float period = 2; /** Time between reads, in seconds. */ + ws.sensor.Type read_mode = 3; /** Desired read mode for the pin. */ + SampleMode sample_mode = 4; /** Desired sample mode for the pin. */ +} + +/** +* Remove removes an analog pin from the device. +*/ +message Remove { + string pin_name = 1; /** Name of the pin. */ +} + + +/** +* Event contains a value, sent when an analog pin is read. +*/ +message Event { + string pin_name = 1; /** Name of the pin. */ + ws.sensor.Event value = 2; /** Reading(s) from an analog pin. */ +} diff --git a/proto/wippersnapper/checkin.options b/proto/wippersnapper/checkin.options new file mode 100644 index 00000000..7ca23a5a --- /dev/null +++ b/proto/wippersnapper/checkin.options @@ -0,0 +1,4 @@ +# checkin.options +ws.checkin.Request.hardware_uid max_size: 64 +ws.checkin.Request.firmware_version max_size: 25 +ws.checkin.B2D submsg_callback:true \ No newline at end of file diff --git a/proto/wippersnapper/checkin.proto b/proto/wippersnapper/checkin.proto new file mode 100644 index 00000000..65660d14 --- /dev/null +++ b/proto/wippersnapper/checkin.proto @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: 2020-2024 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +// Messages for registering new and existing hardware with the Adafruit.io MQTT Broker +syntax = "proto3"; +package ws.checkin; +import "analogio.proto"; +import "digitalio.proto"; +import "ds18x20.proto"; +import "i2c.proto"; +import "pixels.proto"; +import "pwm.proto"; +import "servo.proto"; +import "sleep.proto"; +import "uart.proto"; +import "display.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Response response = 1; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Request request = 1; + Complete complete = 2; + } +} + +/** +* Request notifies the MQTT broker that a new/existing device is requesting to connect. +*/ +message Request { + string hardware_uid = 1; /** Identifies the client's physical hardware (board name + last 3 of NIC's MAC address). */ + string firmware_version = 2; /** Identifies the client's firmware version. */ +} + +/** +* Response sends a broker response to the client's Request. +*/ +message Response { + Response response = 1; /** Specifies if the hardware definition exists on the server. */ + int32 total_gpio_pins = 2; /** Specifies the number of GPIO pins on the device. */ + int32 total_analog_pins = 3; /** Specifies the number of analog pins on the device. */ + float reference_voltage = 4; /** Specifies the hardware's default reference voltage. */ + ComponentAdds component_adds = 5; /** A list of components to set up and initialize during checkin */ + bool sleep_enabled = 6; /** Whether sleep is enabled or not, ignore config if not */ + ws.sleep.SleepConfig sleep_config = 7; /** Sleep configuration for the device */ + + /** + * Response. Specifies if the hardware definiton is within the database. + */ + enum Response { + R_UNSPECIFIED = 0; /** Invalid response from server */ + R_OK = 1; /** Board found within definition index */ + R_BOARD_NOT_FOUND = 2; /** Board not found in definition index */ + } +} + +/** + * Generic component add message, could contain any *Add message from component protos + */ +message ComponentAdds { + repeated ws.digitalio.Add digitalio_adds = 1; + repeated ws.analogio.Add analogio_adds = 2; + repeated ws.servo.Add servo_adds = 3; + repeated ws.pwm.Add pwm_adds = 4; + repeated ws.pixels.Add pixels_adds = 5; + repeated ws.ds18x20.Add ds18x20_adds = 6; + repeated ws.uart.Add uart_adds = 7; + repeated ws.i2c.Add i2c_adds = 8; + repeated ws.display.Add display_adds = 9; +} + +/** + * Signal to the broker that the device has completed the checkin + * routine and is now operating normally + */ +message Complete { + +} diff --git a/proto/wippersnapper/description/v1/description.md b/proto/wippersnapper/description/v1/description.md deleted file mode 100644 index 5c60801f..00000000 --- a/proto/wippersnapper/description/v1/description.md +++ /dev/null @@ -1,60 +0,0 @@ - -# description.proto - -This file details how WipperSnapper firmware. registers a new or checks-in an existing board with the Adafruit IO MQTT broker. - - -## Sequence Diagrams - - -### Process: Check in - -WipperSnapper's check in process involves a board sending its hardware identifier, MAC address and library version to the MQTT Broker. The broker verifies if the hardware's "digital twin" definition exists within the [WipperSnapper_Boards](https://github.com/adafruit/Wippersnapper_Boards) repository. - -Then, the broker sends a message back to the board with `OK` if found within the repo. If the board was not found within the repo, the `NOT_FOUND` response is sent and the board should disconnect from the broker and halt. - -```mermaid - -sequenceDiagram - -autonumber - -Device-->>IO: CreateDescriptionRequest -Note over Device,IO: This message is sent over the general MQTT
topic: /:user/wprsnpr/status -IO->>Storage (DB): Check if machine_name
exists as a JSON definition
within the Boards repo -Storage (DB)->>IO: Return RESPONSE_OK
or RESPONSE_BOARD_NOT_FOUND -IO-->>Device: CreateDescriptionResponse -Note over IO,Device: Contains metadata about the physical hardware
from the JSON definition file. -``` - - -### Process: Hardware Configuration - -Where it can, the WipperSnapper firmware avoids dynamic allocation. If the `CreateDescriptionResponse` message contains a valid response( `RESPONSE_OK` ), fields from the `CreateDescriptionResponse` message are parsed and used to configure some hardware-specific classes: - -```mermaid -sequenceDiagram -autonumber -Device->>App: Parse CreateDescriptionResponse -App->>Digital IO Class: Configure total_gpio_pins -App->>Analog IO Class: Configure total_analog_pins and reference_voltage -App->>I2C Class: Configure total_i2c_ports -``` - -### Process: Hardware Sync - -The hardware sync process described by this API is outdated and will be depreciated in a future API version. It only exists for use with the Digital IO class. - -After the broker sends the `CreateDescriptionResponse` message, it sends the values and states for any digital IO pins configured on the device. Then, it waits for a `RegistrationComplete` response from the device. The `RegistrationComplete` message confirms that the hardware has completed configuring its state and values. - -```mermaid -sequenceDiagram -autonumber -IO-->>Device: CreateDescriptionResponse -IO-->>Device: ConfigurePinRequests -Note over IO,Device: This message contains a list of
ConfigurePinRequest messages, one per
digital GPIO pin. -Device-->>DigitalGPIO: Decode and configure
each ConfigurePinRequest message -Device-->>IO: RegistrationComplete -Note over Device,IO: After the ConfigurePinRequests have completed,
tell the broker that the device
is ready to accept MQTT commands. -``` - diff --git a/proto/wippersnapper/description/v1/description.proto b/proto/wippersnapper/description/v1/description.proto deleted file mode 100644 index 0bbd9751..00000000 --- a/proto/wippersnapper/description/v1/description.proto +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: 2020-2021 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -// Messages for describing hardware over the description topic -syntax = "proto3"; - -package wippersnapper.description.v1; -import "nanopb/nanopb.proto"; - -/** -* CreateDescriptionRequest identifies a device with Adafruit.io WipperSnapper. -*/ -message CreateDescriptionRequest { - string machine_name = 1 [(nanopb).max_size = 64]; /** Identifies client's physical hardware */ - int32 mac_addr = 2; /** Client's UID, last 3 bytes of MAC address */ - int32 usb_vid = 3; /** Optional, USB Vendor ID */ - int32 usb_pid = 4; /** Optional, USB Product ID */ - Version version = 5 [deprecated = true, (nanopb).type = FT_IGNORE]; /** Client's library version. */ - int32 ver_major = 10 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_minor = 11 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_patch = 12 [deprecated = true, (nanopb).type = FT_IGNORE]; - string ver_pre_release = 13 [deprecated = true, (nanopb).type = FT_IGNORE, (nanopb).max_size = 6]; - int32 ver_build = 14 [deprecated = true, (nanopb).type = FT_IGNORE]; - string str_version = 15 [(nanopb).max_size = 20]; /** Library version, as a string */ - - - message Version { - uint64 major = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; - uint64 minor = 2 [deprecated = true, (nanopb).type = FT_IGNORE]; - uint64 micro = 3 [deprecated = true, (nanopb).type = FT_IGNORE]; - string label = 4 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_major = 5 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_minor = 6 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_patch = 7 [deprecated = true, (nanopb).type = FT_IGNORE]; - string ver_pre_release = 8 [deprecated = true, (nanopb).type = FT_IGNORE]; - int32 ver_build = 9 [deprecated = true, (nanopb).type = FT_IGNORE]; - } -} - -/** -* CreateDescriptionResponse represents a device's specifications. -*/ -message CreateDescriptionResponse { - Response response = 1; /** Specifies if the hardware definition exists on the server. */ - int32 total_gpio_pins = 2; /** Specifies the number of GPIO pins on the client's physical hardware. */ - int32 total_analog_pins = 3; /** Specifies the number of analog pins on the client's physical hardware. */ - float reference_voltage = 4; /** Specifies the hardware's default reference voltage. */ - int32 total_i2c_ports = 5; /** Specifies the number of hardware's I2C ports (i2cPorts[]). */ - - /** - * Response. Specifies if the hardware definiton is within the database. - */ - enum Response { - RESPONSE_UNSPECIFIED = 0; /** Invalid response from server */ - RESPONSE_OK = 1; /** Board found within definition index */ - RESPONSE_BOARD_NOT_FOUND = 2; /** Board not found in definition index */ - } -} - -/** -* RegistrationComplete Specifies if the device finished configuring -* its components and is ready for configuration messages. -*/ -message RegistrationComplete { - bool is_complete = 1; /** True if device successfully configured its components, False otherwise. */ -} - -// Request the board definition JSON from a device -// MQTT Topic: `device/ID/description/get` -message GetDefinitionRequest { - string data = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; // Request may be any UTF-8 string value -} - -// Response from Adafruit IO with the JSON board definition as a string -// MQTT Topic: `device/ID/description/get` -message GetDefinitionResponse { - string board_definition = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; // Response is JSON data encoded as a string -} diff --git a/proto/wippersnapper/digitalio.options b/proto/wippersnapper/digitalio.options new file mode 100644 index 00000000..935b74f5 --- /dev/null +++ b/proto/wippersnapper/digitalio.options @@ -0,0 +1,5 @@ +# digitalio.options +ws.digitalio.Add.pin_name max_size: 64 +ws.digitalio.Remove.pin_name max_size: 64 +ws.digitalio.Event.pin_name max_size: 64 +ws.digitalio.Write.pin_name max_size: 64 diff --git a/proto/wippersnapper/digitalio.proto b/proto/wippersnapper/digitalio.proto new file mode 100644 index 00000000..af8de024 --- /dev/null +++ b/proto/wippersnapper/digitalio.proto @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 Brent Rubell, Loren Norman, Tyeth Gundry for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.digitalio; +import "sensor.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Event event = 1; + } +} + +/** +* SampleMode specifies the pin's sample mode. +*/ +enum SampleMode { + SM_UNSPECIFIED = 0; /** Invalid Sample Mode from Broker. */ + SM_TIMER = 1; /** Periodically sample the pin's value. */ + SM_EVENT = 2; /** Sample the pin's value when an event occurs. */ +} + +/** +* Direction specifies the pin's direction, INPUT/INPUT_PULL_UP/OUTPUT. +*/ +enum Direction { + D_UNSPECIFIED = 0; /** Invalid Direction from Broker. */ + D_INPUT = 1; /** Set the pin to behave as an input. */ + D_INPUT_PULL_UP = 2; /** Set the pin to behave as an input. */ + D_OUTPUT = 3; /** Set the pin to behave as an output. */ +} + +/** +* Add adds a digital pin to the device. +*/ +message Add { + string pin_name = 1; /** The pin's name. */ + Direction gpio_direction = 2; /** The pin's direction. */ + SampleMode sample_mode = 3; /** Specifies the pin's sample mode. */ + float period = 4; /** Time between measurements in seconds, if MODE_TIMER. */ + Write write = 5; /** Optional initial value to write to the pin, used during check-in. */ + bool is_inverted = 6; /** If true, inverts the pin's value (active low). */ +} + +/** +* Remove removes a digital pin from the device. +*/ +message Remove { + string pin_name = 1; /** The pin's name. */ +} + +/** +* Event is sent from the device to the broker when a digital pin's value changes. +*/ +message Event { + string pin_name = 1; /** The pin's name. */ + ws.sensor.Event value = 2; /** The pin's value. */ +} + +/** +* Write writes a boolean value to a digital pin. +*/ +message Write { + string pin_name = 1; /** The pin's name. */ + ws.sensor.Event value = 2; /** The pin's value. */ +} diff --git a/proto/wippersnapper/display.options b/proto/wippersnapper/display.options new file mode 100644 index 00000000..6cca305a --- /dev/null +++ b/proto/wippersnapper/display.options @@ -0,0 +1,55 @@ +# display.options +# Nanopb configuration for display.proto + +# DisplayProperties +ws.display.DisplayProperties.status_bar initializer:"true" + +# Add message +ws.display.Add.driver max_size:32 +ws.display.Add.panel max_size:32 +ws.display.Add.name max_size:64 + +# Display-specific SPI pin constraints (shared SPI pins are in spi.options) +ws.display.TftSpiConfig.pin_dc max_size:6 +ws.display.TftSpiConfig.pin_rst max_size:6 +ws.display.EpdSpiConfig.pin_dc max_size:6 +ws.display.EpdSpiConfig.pin_rst max_size:6 +ws.display.EpdSpiConfig.pin_sram_cs max_size:6 +ws.display.EpdSpiConfig.pin_busy max_size:6 + +ws.display.TtlRgb666PinConfig.pin_r0 max_size:6 +ws.display.TtlRgb666PinConfig.pin_r1 max_size:6 +ws.display.TtlRgb666PinConfig.pin_r2 max_size:6 +ws.display.TtlRgb666PinConfig.pin_g0 max_size:6 +ws.display.TtlRgb666PinConfig.pin_g1 max_size:6 +ws.display.TtlRgb666PinConfig.pin_g2 max_size:6 +ws.display.TtlRgb666PinConfig.pin_b0 max_size:6 +ws.display.TtlRgb666PinConfig.pin_b1 max_size:6 +ws.display.TtlRgb666PinConfig.pin_b2 max_size:6 + +ws.display.I8080PinConfig.pin_d0 max_size:6 +ws.display.I8080PinConfig.pin_d1 max_size:6 +ws.display.I8080PinConfig.pin_d2 max_size:6 +ws.display.I8080PinConfig.pin_d3 max_size:6 +ws.display.I8080PinConfig.pin_d4 max_size:6 +ws.display.I8080PinConfig.pin_d5 max_size:6 +ws.display.I8080PinConfig.pin_d6 max_size:6 +ws.display.I8080PinConfig.pin_d7 max_size:6 +ws.display.I8080PinConfig.pin_cs max_size:6 +ws.display.I8080PinConfig.pin_dc max_size:6 +ws.display.I8080PinConfig.pin_rst max_size:6 + +ws.display.DsiInterfaceConfig.pin_rst max_size:6 + +ws.display.BacklightConfig.pin max_size:6 + +# Remove message +ws.display.Remove.name max_size:64 + +# Write message +ws.display.Write.name max_size:64 +ws.display.Write.message max_size:1024 + +# Response messages +ws.display.AddedOrReplaced.name max_size:64 +ws.display.Removed.name max_size:64 diff --git a/proto/wippersnapper/display.proto b/proto/wippersnapper/display.proto new file mode 100644 index 00000000..c5fcb44c --- /dev/null +++ b/proto/wippersnapper/display.proto @@ -0,0 +1,269 @@ +// SPDX-FileCopyrightText: 2025 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +// Display API for Adafruit WipperSnapper + +syntax = "proto3"; +package ws.display; +import "i2c.proto"; +import "pwm.proto"; +import "digitalio.proto"; +import "spi.proto"; + +// ============================================================================ +// Message Envelopes +// ============================================================================ + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + bool did_succeed = 1; /** Indicates if the requested operation succeeded. */ + oneof payload { + AddedOrReplaced added_or_replaced = 2; + Removed removed = 3; + } +} + +// ============================================================================ +// Enums +// ============================================================================ + +/** + * DisplayClass determines the type of display + * and selects which driver should be used. + */ +enum DisplayClass { + DISPLAY_CLASS_UNSPECIFIED = 0; /** Unspecified display type. */ + DISPLAY_CLASS_EPD = 1; /** EPD (E-Paper Display) type */ + DISPLAY_CLASS_TFT = 2; /** TFT (Thin Film Transistor) type */ + DISPLAY_CLASS_OLED = 3; /** OLED (Organic LED) type */ + DISPLAY_CLASS_LED_BACKPACK = 4; /** LED Backpack (HT16K33-based 7-segment/14-segment displays) */ + DISPLAY_CLASS_CHAR_LCD = 5; /** Character LCD (text-based LCD displays) */ +} + +/** + * EPDMode defines the mode of the EPD display. + * This affects how the display renders images. + */ +enum EPDMode { + EPD_MODE_UNSPECIFIED = 0; /** Unspecified EPD mode */ + EPD_MODE_GRAYSCALE4 = 1; /** Grayscale 4 EPD mode */ + EPD_MODE_MONO = 2; /** Monochrome EPD mode */ +} + +/** + * LedBackpackAlignment represents text alignment options for LED backpack displays + */ +enum LedBackpackAlignment { + LBA_UNSPECIFIED = 0; /** Unspecified alignment option. */ + LBA_LEFT = 1; /** (Default) Left-aligned. */ + LBA_RIGHT = 2; /** Right-aligned. */ +} + +// /** +// * OledTextSize represents the desired OLED display text 'magnification' size. +// */ +// enum OledTextSize { +// OTS_UNSPECIFIED = 0; /** Unspecified text size. */ +// OTS_DEFAULT = 1; /** Default text size, 6x8px. */ +// OTS_LARGE = 2; /** Larger text size option, 12x16px. */ +// } + +// ============================================================================ +// Configuration Messages +// ============================================================================ + +message DisplayProperties { + int32 width = 1; /** Display width in pixels. */ + int32 height = 2; /** Display height in pixels. */ + int32 rotation = 3; /** Display rotation (Clockwise 90* increments 0-3). */ + int32 text_size = 4; /** Text size or magnification factor (if applicable) - 1 = 6x8px, 2 = 12x16px, ... */ + bool status_bar = 5; /** show status bar? **/ +} + +/** +* EpdSpiConfig contains the configuration for SPI-based EPD displays. +* Includes the SPI device config plus display-specific pins. +*/ +message EpdSpiConfig { + ws.spi.Config spi = 1; /** SPI bus, doesnt carry additional pin config. */ + string pin_dc = 2; /** Pin for data/command */ + string pin_rst = 3; /** Pin for reset */ + string pin_sram_cs = 4; /** Pin for SRAM chip select (if required by the EPD). */ + string pin_busy = 5; /** Pin for busy signal (if required by the EPD). */ +} + +/** + * EPDConfig contains the configuration for an EPD display. + * It includes the mode, dimensions, and panel type. + */ +message EPDConfig { + EPDMode mode = 1; /** The mode of the EPD display */ + DisplayProperties properties = 2; /** Common display properties (dimensions, rotation, text size). */ + /** Text Size is a Scale factor for text (1 = 6x8px, 2 = 12x16px,...) */ +} + +/** + * LedBackpackConfig represents the configuration for a LED backpack display. + */ +message LedBackpackConfig { + int32 brightness = 1; /** Desired brightness of the LED backpack, from 0 (off) to 15 (full brightness). */ + LedBackpackAlignment alignment = 2; /** Desired text alignment for the LED backpack. */ +} + +/** + * CharLcdConfig represents the configuration for a character LCD display. + */ +message CharLcdConfig { + uint32 rows = 1; /** Number of rows for the character LCD. */ + uint32 columns = 2; /** Number of columns for the character LCD. */ +} + +/** + * TftSpiConfig contains the configuration for SPI TFT displays. + * Includes the SPI device config plus display-specific pins. + */ +message TftSpiConfig { + ws.spi.Config spi = 1; /** SPI bus */ + string pin_dc = 2; /** Pin for data/command */ + string pin_rst = 3; /** Pin for reset */ +} + +/** TTL RGB666 pin configuration for parallel RGB displays. */ +message TtlRgb666PinConfig { + string pin_r0 = 1; /** Pin for R0 */ + string pin_r1 = 2; /** Pin for R1 */ + string pin_r2 = 3; /** Pin for R2 */ + string pin_g0 = 4; /** Pin for G0 */ + string pin_g1 = 5; /** Pin for G1 */ + string pin_g2 = 6; /** Pin for G2 */ + string pin_b0 = 7; /** Pin for B0 */ + string pin_b1 = 8; /** Pin for B1 */ + string pin_b2 = 9; /** Pin for B2 */ +} + +/** Intel 8080 8-bit parallel pin configuration for displays. */ +message I8080PinConfig { + string pin_d0 = 1; /** Pin for D0 */ + string pin_d1 = 2; /** Pin for D1 */ + string pin_d2 = 3; /** Pin for D2 */ + string pin_d3 = 4; /** Pin for D3 */ + string pin_d4 = 5; /** Pin for D4 */ + string pin_d5 = 6; /** Pin for D5 */ + string pin_d6 = 7; /** Pin for D6 */ + string pin_d7 = 8; /** Pin for D7 */ + string pin_cs = 9; /** Pin for chip select */ + string pin_dc = 10; /** Pin for data/command */ + string pin_rst = 11; /** Pin for reset */ +} + +/** + * DsiInterfaceConfig contains the minimal configuration for MIPI DSI displays. + * DSI clock and data lanes are dedicated differential pads on the SoC, not GPIOs. + */ +message DsiInterfaceConfig { + uint32 bus = 1; /** DSI bus index (0 if only one DSI peripheral). */ + string pin_rst = 2; /** GPIO pin for display panel reset. */ +} + +/** + * BacklightConfig configures the display backlight. + * Shared across any display type with a controllable backlight + * (TFT, DSI, Character LCD with RGB backlight, etc.). + */ +message BacklightConfig { + oneof backlight_add { + ws.digitalio.Add backlight_digital = 1; /** Optional digital pin configuration for backlight control, used during check-in. */ + ws.pwm.Add backlight_pwm = 2; /** Optional PWM configuration for backlight control, used during check-in. */ + } + // comes via DigitalIO.Add.Write +} + + +// ============================================================================ +// Request Messages +// ============================================================================ + +/** + * Add adds a new display or replaces an existing one. + * Used as ws.display.Add in i2c.proto for I2C-based displays. + * + * Known driver strings (migrated from DisplayDriver enum): + * EPD drivers: "SSD1680", "ILI0373", "UC8253", "UC8179", "UC8151", "SSD1683" + * TFT drivers: "ST7789" + * OLED drivers: "SSD1306", "SSD1305", "SH1106", "SH1107" + * LED Backpack drivers: "HT16K33" + * Character LCD drivers: "MCP23008", "PCF8574" + * + * Panel naming conventions (for E-Paper displays with same driver, different panels): + * Format: "{size}-{resolution}-{color_mode}" or "adafruit-{product_id}" + * Examples: "5.83-648x480-mono", "4.2-300x400-gray", "adafruit-6397" + + To shorten the component names, Brent and I discussed using sub folders for class/driver/resolution, then maybe colour/panel type, but not clear cut. + displays/oled/ssd1306/ + displays/epd/ssd1680/250x122/mono/ [2.13" e-ink featherwing which has a panel variant with the same driver and no distinguishing features rev2024] + */ +message Add { + string name = 1; /** Component name for the display. */ + DisplayClass type = 2; /** The type of display */ + string driver = 3; /** Driver name (e.g., "UC8179", "SSD1306", "HT16K33") */ + string panel = 4; /** Panel identifier for same driver with different configs */ + oneof interface_type { + EpdSpiConfig spi_epd = 5; /** SPI configuration for EPD. */ + TftSpiConfig spi_tft = 6; /** SPI configuration for TFT. */ + ws.i2c.Descriptor i2c = 7; /** I2C configuration for OLED/LED/LCD displays. */ + TtlRgb666PinConfig ttl_rgb666 = 8; /** RGB666 configuration for Qualia. */ + I8080PinConfig i8080 = 9; /** i8080 8-bit parallel for T-DisplayS3/Memento. */ + DsiInterfaceConfig dsi = 10; /** DSI configuration for MIPI DSI displays (e.g., ESP32-P4). */ + } + oneof config { + EPDConfig config_epd = 11; /** Configuration for EPD. */ + LedBackpackConfig config_led = 14; /** Configuration for LED Backpack. */ + CharLcdConfig config_char_lcd = 15; /** Configuration for Character LCD. */ + DisplayProperties config_display = 18; /** Configuration for Generic display (w/h/r/txtSz). */ + } + Write write = 19; /** Optional initial write for a display, used during check-in. */ + BacklightConfig backlight = 20; /** Optional backlight configuration (shared across display types). */ +} + +/** + * Remove removes an existing display. + */ +message Remove { + string name = 1; /** Component name for the display. */ +} + +/** + * Write writes content to a display. + */ +message Write { + string name = 1; /** Component name for the display. */ + string message = 2; /** Monospace text message */ +} + +// ============================================================================ +// Response Messages +// ============================================================================ + +/** + * AddedOrReplaced confirms a display was added or replaced. + */ +message AddedOrReplaced { +} + +/** + * Removed confirms a display was removed. + */ +message Removed { +} diff --git a/proto/wippersnapper/display/v1/display.md b/proto/wippersnapper/display/v1/display.md deleted file mode 100644 index e44f5b32..00000000 --- a/proto/wippersnapper/display/v1/display.md +++ /dev/null @@ -1,83 +0,0 @@ - -# display.proto - -This file details the WipperSnapper messaging API for interfacing with a display. - -## WipperSnapper Components - -The following WipperSnapper Components may utilize `display.proto`: - -* E-Ink/E-Paper Displays -* TFT Displays -* OLED Displays -* 7-Segment Displays -* Alphanumeric Displays -* LCD Character Displays - -## Sequence Diagrams - -### Attaching a Display Component to a device running WipperSnapper - -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: DisplayAddOrReplace -Note over IO, Device: DisplayType field dictates which
display we are using (LED, E-Ink, etc) - -Device->>ws_display controller: DisplayAddOrReplace - -ws_display controller->>ws_display hardware: DisplayAddOrReplace - -ws_display hardware->>ws_display driver: Driver Configure Request - -ws_display driver->>ws_display hardware: Driver Configure Response - -ws_display hardware->>ws_display controller: Hardware Response - -ws_display controller-->>Device: DisplayAddedorReplaced - -Device-->>IO: DisplayAddedorReplaced -``` - -### Removing a Display Component from a device running WipperSnapper - -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: DisplayRemove -Note over IO, Device: name field dictates which
display we are removing - -Device->>ws_display controller: DisplayRemove - -ws_display controller->>ws_display hardware: Delete hardware instance - -ws_display hardware->>ws_display driver: Delete driver instance - -ws_display driver->>ws_display hardware: Deletion Result - -ws_display hardware->>ws_display controller: Deletion Result - -ws_display controller-->>Device: DisplayRemoved - -Device-->>IO: DisplayRemoved -``` - -### Writing to a Display from IO - -The display message is set by the component's feed value, which is a string. The message is sent to the display driver. - -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: DisplayWrite -Note over IO, Device: name field dictates which
display we are writing to - -Device->>ws_display controller: handleDisplayWrite() - -ws_display controller->>ws_display hardware: Get display hardware - -ws_display hardware->>ws_display driver: Execute writeX() -``` \ No newline at end of file diff --git a/proto/wippersnapper/display/v1/display.proto b/proto/wippersnapper/display/v1/display.proto deleted file mode 100644 index cbe09152..00000000 --- a/proto/wippersnapper/display/v1/display.proto +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -// Display API for Adafruit WipperSnapper - -syntax = "proto3"; - -package wippersnapper.display.v1; -import "nanopb/nanopb.proto"; - -// ============================================================================ -// Enums -// ============================================================================ - -/** - * DisplayType determines the type of display - * and selects which driver should be used. - */ -enum DisplayType { - DISPLAY_TYPE_UNSPECIFIED = 0; /** Unspecified display type. */ - DISPLAY_TYPE_EPD = 1; /** EPD display type */ - DISPLAY_TYPE_TFT = 2; /** TFT display type */ -} - -/** - * EPDMode defines the mode of the EPD display. - * This affects how the display renders images. - */ -enum EPDMode { - EPD_MODE_UNSPECIFIED = 0; /** Unspecified EPD mode */ - EPD_MODE_GRAYSCALE4 = 1; /** Grayscale 4 EPD mode */ - EPD_MODE_MONO = 2; /** Monochrome EPD mode */ -} - -/** - * DisplayDriver determines the specific driver to use for the display. - */ -enum DisplayDriver { - DISPLAY_DRIVER_UNSPECIFIED = 0; /** Unspecified display driver */ - DISPLAY_DRIVER_EPD_SSD1680 = 1; /** EPD SSD1680 driver */ - DISPLAY_DRIVER_EPD_ILI0373 = 2; /** EPD SSD167 driver */ - DISPLAY_DRIVER_TFT_ST7789 = 3; /** TFT ST7789 driver */ -} - -// ============================================================================ -// Configuration Messages -// ============================================================================ - -/** - * EpdSpiConfig contains the configuration for SPI-based EPD displays. - * Includes the SPI bus number and pin assignments. - */ -message EpdSpiConfig { - int32 bus = 1; /** The SPI bus to use */ - string pin_dc = 2[(nanopb).max_size = 6]; /** Pin for data/command */ - string pin_rst = 3[(nanopb).max_size = 6]; /** Pin for reset */ - string pin_cs = 4[(nanopb).max_size = 6]; /** Pin for chip select */ - string pin_sram_cs = 5[(nanopb).max_size = 6]; /** Pin for SRAM chip select */ - string pin_busy = 6[(nanopb).max_size = 6]; /** Pin for busy signal */ -} - -/** - * EPDConfig contains the configuration for an EPD display. - * It includes the mode, dimensions, and panel type. - */ -message EPDConfig { - EPDMode mode = 1; /** The mode of the EPD display */ - int32 width = 2; /** Width of the EPD display, in pixels */ - int32 height = 3; /** Height of the EPD display, in pixels */ - int32 text_size = 4; /** Scale factor for text (1 = 6x8px, 2 = 12x16px,...) */ -} - -/** - * TftSpiConfig contains the configuration for SPI TFT displays. - * Includes the SPI bus number and pin assignments. - */ -message TftSpiConfig { - int32 bus = 1; /** The SPI bus to use */ - string pin_cs = 2[(nanopb).max_size = 6]; /** Pin for chip select */ - string pin_dc = 3[(nanopb).max_size = 6]; /** Pin for data/command */ - string pin_mosi = 4[(nanopb).max_size = 6]; /** Pin for MOSI */ - string pin_sck = 5[(nanopb).max_size = 6]; /** Pin for SCK */ - string pin_rst = 6[(nanopb).max_size = 6]; /** Pin for reset */ - string pin_miso = 7[(nanopb).max_size = 6]; /** Pin for MISO */ -} - -message TftConfig { - int32 width = 1; /** Width of the TFT display in pixels */ - int32 height = 2; /** Height of the TFT display in pixels */ - int32 rotation = 3; /** Rotation of the display (0-3) */ - int32 text_size = 4; /** Scale factor for text (1 = 6x8px, 2 = 12x16px,...) */ -} - -// ============================================================================ -// Request Messages -// ============================================================================ - -/** - * DisplayAddOrReplace adds a new display or replaces an existing one. - */ -message DisplayAddOrReplace { - DisplayType type = 1; /** The type of display */ - DisplayDriver driver = 2; /** The specific driver for the display */ - string name = 3[(nanopb).max_size = 64]; /** Identifies the display. */ - oneof interface_type { - EpdSpiConfig spi_epd = 4; /** SPI configuration for EPD. */ - TftSpiConfig spi_tft = 5; /** SPI configuration for TFT. */ - } - oneof config { - EPDConfig config_epd = 6; /** Configuration for EPD. */ - TftConfig config_tft = 7; /** Configuration for TFT. */ - } -} - -/** - * DisplayRemove removes an existing display. - */ -message DisplayRemove { - string name = 1[(nanopb).max_size = 64]; /** Identifier for the display. */ -} - -/** - * DisplayWrite writes content to a display. - */ -message DisplayWrite { - string name = 1[(nanopb).max_size = 64]; /** Identifies the display. */ - string message = 2[(nanopb).max_size = 1024]; /** Message to write. */ -} - -// ============================================================================ -// Response Messages -// ============================================================================ - -/** - * DisplayAddedOrReplaced confirms a display was added or replaced. - */ -message DisplayAddedOrReplaced { - string name = 1[(nanopb).max_size = 64]; /** Identifier for the display. */ - bool did_add = 2; /** Indicates if a display was added */ -} - -/** - * DisplayRemoved confirms a display was removed. - */ -message DisplayRemoved { - string name = 1[(nanopb).max_size = 64]; /** Identifies the display. */ - bool did_remove = 2; /** Indicates if a display was removed */ -} - diff --git a/proto/wippersnapper/docs/analogio.md b/proto/wippersnapper/docs/analogio.md new file mode 100644 index 00000000..33eff471 --- /dev/null +++ b/proto/wippersnapper/docs/analogio.md @@ -0,0 +1,287 @@ +# analogio.proto + +This file details the WipperSnapper messaging API for reading analog input pins (ADC - Analog to Digital Converter). + +## WipperSnapper Components + +The following WipperSnapper components utilize `analogio.proto`: +* [analogio](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/analogio) - Analog input pins + +## Architecture Overview + +The v2 Analog IO API uses message envelopes for cleaner organization: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Add/configure an analog input pin + - `remove` - Remove an analog input pin + +- **D2B (DeviceToBroker)** - Responses and data from device to Adafruit IO + - `event` - Analog reading from pin + +**Note:** Analog IO is read-only. There is no `write` command. For analog output (DAC), use PWM instead. + +## Read Modes + +Analog pins can be read in different modes using the `read_mode` field (of type `ws.sensor.Type`): + +| Read Mode | Description | Use Case | +|-----------|-------------|----------| +| `SENSOR_TYPE_VOLTAGE` | Read as voltage (V) | Battery monitoring, voltage dividers | +| `SENSOR_TYPE_RAW` | Read as raw ADC value | Custom sensor calibration | +| `SENSOR_TYPE_UNITLESS_PERCENT` | Read as percentage (0-100%) | Potentiometers, analog sensors | + +## Sequence Diagrams + +### Add an Analog Pin + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant ADC as ADC Controller + +IO->>Device: ws.analogio.B2D { add } +Note over IO,Device: pin_name: "A0"
period: 2.0 (seconds)
read_mode: SENSOR_TYPE_VOLTAGE + +Device->>ADC: Configure analog pin +ADC->>Device: Pin configured + +loop Every period seconds + ADC->>ADC: Read analog value + ADC->>Device: Convert to requested mode + Device->>IO: ws.analogio.D2B { event } + Note over Device,IO: pin_name: "A0"
value: {type: VOLTAGE, value: 3.28} +end +``` + +### Remove an Analog Pin + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant ADC as ADC Controller + +IO->>Device: ws.analogio.B2D { remove } +Note over IO,Device: pin_name: "A0" + +Device->>ADC: Stop reading pin +ADC->>ADC: Release pin resources +ADC->>Device: Pin removed +``` + +## Use Cases & Examples + +### Example 1: Battery Voltage Monitoring + +Monitor a battery voltage with a voltage divider: + +``` +ws.analogio.B2D { add: { + pin_name: "A1", + period: 10.0, + read_mode: SENSOR_TYPE_VOLTAGE +}} +``` + +The device sends voltage readings: +``` +ws.analogio.D2B { event: { + pin_name: "A1", + value: {type: VOLTAGE, value: 3.7} +}} +``` + +**Note:** Voltage reading is based on board's reference voltage (typically 3.3V). Use external voltage dividers for higher voltages. + +### Example 2: Potentiometer Position + +Read a potentiometer as a percentage: + +``` +ws.analogio.B2D { add: { + pin_name: "A2", + period: 0.5, + read_mode: SENSOR_TYPE_UNITLESS_PERCENT +}} +``` + +Returns: +``` +ws.analogio.D2B { event: { + pin_name: "A2", + value: {type: UNITLESS_PERCENT, value: 75.3} +}} +``` + +### Example 3: Raw ADC Value + +Get raw ADC reading for custom calibration: + +``` +ws.analogio.B2D { add: { + pin_name: "A3", + period: 1.0, + read_mode: SENSOR_TYPE_RAW +}} +``` + +Returns: +``` +ws.analogio.D2B { event: { + pin_name: "A3", + value: {type: RAW, value: 2048} +}} +``` + +### Example 4: Light Sensor (Photoresistor) + +Monitor light levels with a photoresistor voltage divider: + +``` +ws.analogio.B2D { add: { + pin_name: "A4", + period: 5.0, + read_mode: SENSOR_TYPE_VOLTAGE +}} +``` + +Higher voltage = more light (depending on circuit configuration). + +## Pin Naming Conventions + +Analog pin names should match the board's pinout definition in the [WipperSnapper_Boards](https://github.com/adafruit/Wippersnapper_Boards) repository: + +- Arduino-style: `"A0"`, `"A1"`, `"A2"`, etc. +- ESP32: `"GPIO32"`, `"GPIO33"` (GPIO with ADC capability) +- RP2040: `"GP26"`, `"GP27"`, `"GP28"` (ADC-capable pins) + +**Important:** Not all pins support analog input. Check your board's documentation for ADC-capable pins. + +## ADC Resolution + +Different boards have different ADC resolutions: + +| Platform | Resolution | Range | Voltage Range | +|----------|-----------|-------|---------------| +| Arduino Uno/Nano | 10-bit | 0-1023 | 0-5V | +| ESP32 | 12-bit | 0-4095 | 0-3.3V (with attenuation up to ~3.6V) | +| RP2040 (Pico) | 12-bit | 0-4095 | 0-3.3V | +| SAMD21/SAMD51 | 12-bit | 0-4095 | 0-3.3V | + +When using `SENSOR_TYPE_RAW`, the value will be in the range supported by your board's ADC. + +## Voltage Reference + +The voltage reading depends on the board's reference voltage: + +- **3.3V boards** (ESP32, RP2040, SAMD): ADC max = 3.3V +- **5V boards** (Arduino Uno): ADC max = 5V + +Some boards allow configuring the voltage reference (AREF). Check your board's documentation. + +## Sampling Period Guidelines + +Choose an appropriate `period` based on your use case: + +| Use Case | Recommended Period | Reasoning | +|----------|-------------------|-----------| +| Battery monitoring | 30-60 seconds | Slow-changing, conserve bandwidth | +| Temperature sensing | 5-10 seconds | Moderately slow | +| Light sensing | 1-5 seconds | Moderate responsiveness | +| Potentiometer | 0.1-0.5 seconds | Fast user interaction | +| Fast sensors | 0.05-0.1 seconds | Quick response needed | + +**Note:** Faster sampling = more MQTT messages = more bandwidth/power consumption. + +## Voltage Dividers for High Voltages + +To measure voltages higher than the board's ADC range, use a voltage divider: + +``` +Vin ---[R1]---+---[R2]--- GND + | + ADC Pin +``` + +Formula: `Vout = Vin * (R2 / (R1 + R2))` + +**Example:** Measure 12V battery with 3.3V ADC +- Use R1 = 10kΩ, R2 = 3.3kΩ +- 12V → 2.98V at ADC pin +- In code: `actualVoltage = readVoltage * ((R1 + R2) / R2)` + +## Best Practices + +### Hardware +1. **Input protection:** Add series resistor (1-10kΩ) to protect ADC from overvoltage +2. **Filtering:** Add 0.1µF capacitor near ADC pin to reduce noise +3. **Voltage dividers:** Use 1% tolerance resistors for accurate voltage measurement +4. **Reference voltage:** Keep reference stable - avoid noisy power supplies + +### Software +1. **Sampling rate:** Don't sample faster than necessary (wastes bandwidth and power) +2. **Averaging:** For noisy signals, increase period and rely on device-side filtering +3. **Calibration:** Use `SENSOR_TYPE_RAW` and calibrate in your application if needed +4. **Units:** Choose appropriate `read_mode` for your application (voltage vs. raw vs. percent) + +### Power Management +1. **Low-power devices:** Use longer periods (≥10 seconds) to conserve battery +2. **Always-on sensors:** Shorter periods acceptable for mains-powered devices +3. **Deep sleep:** Consider if analog reading frequency allows device to sleep between readings + +## Troubleshooting + +### Reading is always 0 or maximum +- **Check wiring:** Verify sensor is connected correctly +- **Check voltage range:** Ensure input is within ADC range (0-3.3V or 0-5V) +- **Input protection:** Series resistor might be too high +- **Pin capability:** Verify pin supports analog input + +### Noisy/unstable readings +- **Add capacitor:** 0.1µF between ADC pin and GND +- **Increase period:** Longer period allows more internal averaging +- **Check power supply:** Noisy power can affect ADC reference +- **Shielded cables:** For long cable runs, use shielded cables + +### Reading doesn't match expected voltage +- **Check reference voltage:** Verify board's ADC reference voltage +- **Voltage divider:** Recalculate divider ratio +- **Pin damage:** Overvoltage may have damaged ADC input +- **Calibration:** Use multimeter to verify actual voltage + +### Values changing without input change +- **Floating input:** Ensure sensor has stable connection +- **EMI:** Keep analog wires away from power lines and motors +- **Pull-up/pull-down:** Some sensors need external resistors + +## Analog vs. Digital + +| Feature | Analog Input | Digital Input | +|---------|--------------|---------------| +| Values | Continuous (0-Vref) | Binary (HIGH/LOW) | +| Resolution | 10-12 bits typical | 1 bit | +| Use Cases | Voltage, potentiometers, analog sensors | Buttons, switches, digital sensors | +| Noise Sensitivity | Higher | Lower | +| Speed | Slower (ADC conversion time) | Faster | + +## Relationship with Other Components + +Analog input is useful for: + +- **Voltage monitoring:** Battery level, solar panel output +- **Sensor reading:** Temperature (thermistor), light (LDR), sound (microphone) +- **User input:** Potentiometers, joysticks, faders +- **Signal monitoring:** Audio level, RF signal strength + +For digital control outputs, see: +- [digitalio.proto](digitalio.md) - Digital GPIO +- [pwm.proto](pwm.md) - PWM for analog-like output + +## Related Documentation + +- [sensor.proto](sensor.md) - Sensor types and event structure +- [WipperSnapper Components](https://github.com/adafruit/Wippersnapper_Components) - Analog input component definitions +- [WipperSnapper Boards](https://github.com/adafruit/Wippersnapper_Boards) - Board-specific ADC capabilities \ No newline at end of file diff --git a/proto/wippersnapper/docs/checkin.md b/proto/wippersnapper/docs/checkin.md new file mode 100644 index 00000000..e236bf5e --- /dev/null +++ b/proto/wippersnapper/docs/checkin.md @@ -0,0 +1,150 @@ +# checkin.proto + +This file details how WipperSnapper firmware registers a new or checks-in an existing board with the Adafruit IO MQTT broker. + +## Architecture Overview + +The v2 Check-In API uses message envelopes: + +- **B2D (BrokerToDevice)** - Response from Adafruit IO + - `response` - Board verification result with hardware capabilities and component configuration + +- **D2B (DeviceToBroker)** - Requests from device + - `request` - Device identification (hardware UID and firmware version) + - `complete` - Signal that device has finished initializing all components + +## Message Details + +### Request (D2B) + +- **hardware_uid** - Board name + last 3 of NIC's MAC address +- **firmware_version** - Client firmware version string + +### Response (B2D) + +- **response** - Status enum: `R_OK` (found) or `R_BOARD_NOT_FOUND` +- **total_gpio_pins** - Number of GPIO pins on the device +- **total_analog_pins** - Number of analog pins on the device +- **reference_voltage** - Hardware's default ADC reference voltage +- **component_adds** - `ComponentAdds` message containing all pre-configured components +- **sleep_enabled** - Whether sleep mode is enabled +- **sleep_config** - Sleep configuration (if enabled) + +### ComponentAdds + +Contains separate repeated fields for each component type to initialize during check-in: + +```protobuf +message ComponentAdds { + repeated ws.digitalio.Add digitalio_adds = 1; + repeated ws.analogio.Add analogio_adds = 2; + repeated ws.servo.Add servo_adds = 3; + repeated ws.pwm.Add pwm_adds = 4; + repeated ws.pixels.Add pixels_adds = 5; + repeated ws.ds18x20.Add ds18x20_adds = 6; + repeated ws.uart.Add uart_adds = 7; + repeated ws.i2c.DeviceAddOrReplace i2c_adds = 8; + repeated ws.display.Add display_adds = 9; +} +``` + +Each component's `Add` message may include an optional initial `write` field for setting the component's initial state. + +### Complete (D2B) + +Empty message signalling the device has finished processing all components and is ready for normal operation. + +## Sequence Diagrams + +### Process: Check-In + +WipperSnapper's check-in process involves a board sending its hardware identifier and firmware version to the MQTT Broker. The broker verifies if the hardware's "digital twin" definition exists within the [WipperSnapper_Boards](https://github.com/adafruit/Wippersnapper_Boards) repository. + +```mermaid +sequenceDiagram +autonumber + +Device->>IO: ws.checkin.D2B { request } +Note over Device,IO: hardware_uid: "ESP32-C3-A1B2C3"
firmware_version: "2.0.0" + +IO->>Storage: Check if hardware definition
exists in Boards repo +Storage->>IO: Return R_OK
or R_BOARD_NOT_FOUND + +IO->>Device: ws.checkin.B2D { response } +Note over IO,Device: response: R_OK
total_gpio_pins: 20
total_analog_pins: 6
reference_voltage: 3.3
component_adds: { ... } +``` + +### Process: Hardware Configuration + +If the response is `R_OK`, the device configures hardware classes from the response: + +```mermaid +sequenceDiagram +autonumber +Device->>Device: Parse Response +Device->>Digital IO: Configure total_gpio_pins +Device->>Analog IO: Configure total_analog_pins and reference_voltage +``` + +### Process: Component Initialization + +The device processes each component list in `ComponentAdds`: + +```mermaid +sequenceDiagram +autonumber +participant Device +participant IO as Adafruit IO + +Device->>Device: Process digitalio_adds[] +Device->>Device: Process analogio_adds[] +Device->>Device: Process i2c_adds[] +Device->>Device: Process display_adds[] +Note over Device: ... process remaining component types ... + +Device->>IO: ws.checkin.D2B { complete } +Note over Device,IO: Device is ready for normal operation +``` + +## Example: Full Check-In with Components + +``` +// Device sends request +ws.checkin.D2B { + request: { + hardware_uid: "ESP32S3-A1B2C3", + firmware_version: "2.0.0" + } +} + +// Broker responds with board info and components +ws.checkin.B2D { + response: { + response: R_OK, + total_gpio_pins: 40, + total_analog_pins: 10, + reference_voltage: 3.3, + component_adds: { + digitalio_adds: [ + { pin_name: "D13", gpio_direction: D_OUTPUT, write: { pin_name: "D13", value: ... } } + ], + i2c_adds: [ + { device_description: { device_address: 0x77 }, device_name: "bme280", device_period: 60.0 } + ], + display_adds: [ + { type: DISPLAY_CLASS_TFT, driver: "ST7789", spi_tft: { ... }, config_display: { ... } } + ] + } + } +} + +// Device finishes initialization +ws.checkin.D2B { + complete: {} +} +``` + +## Related Documentation + +- [wippersnapper_device_overview.md](wippersnapper_device_overview.md) - Complete device flow with check-in +- Individual component docs for `Add` message details diff --git a/proto/wippersnapper/docs/digitalio.md b/proto/wippersnapper/docs/digitalio.md new file mode 100644 index 00000000..1ebd84e2 --- /dev/null +++ b/proto/wippersnapper/docs/digitalio.md @@ -0,0 +1,295 @@ + +# digitalio.proto + +This file details the WipperSnapper messaging API for interfacing with digital I/O pins (GPIO). + +## WipperSnapper Components + +The following WipperSnapper components utilize `digitalio.proto`: +* [pin](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/pin) + +## Architecture Overview + +The v2 Digital IO API uses message envelopes for cleaner organization: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Add/configure a digital pin + - `remove` - Remove a digital pin + - `write` - Write a value to an output pin + +- **D2B (DeviceToBroker)** - Responses and data from device to Adafruit IO + - `event` - Pin value changed (from input pins) + +## Pin Configuration + +### Direction + +Pins can be configured in three directions: + +| Direction | Description | Use Case | +|-----------|-------------|----------| +| `D_INPUT` | Standard input mode | Reading button states, sensor signals | +| `D_INPUT_PULL_UP` | Input with internal pull-up resistor | Reading switches/buttons without external resistor | +| `D_OUTPUT` | Output mode | Controlling LEDs, relays, logic signals | + +### Sample Mode + +For input pins, the sample mode determines how often values are read: + +| Mode | Description | Best For | +|------|-------------|----------| +| `SM_TIMER` | Periodically sample at fixed interval | Monitoring stable inputs, polling sensors | +| `SM_EVENT` | Sample when pin state changes (interrupt-driven) | Buttons, switches, motion sensors | + +### Additional Add Fields + +- **write** - Optional initial `Write` message for setting pin state at check-in +- **is_inverted** - If true, inverts the pin's value (active low) + +### Value Type + +Both `Event` and `Write` messages use `ws.sensor.Event` for the value field, which carries the pin's boolean state along with sensor metadata. + +## Sequence Diagrams + +### Add a Digital Pin (Input) + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant GPIO as GPIO Controller + +IO->>Device: ws.digitalio.B2D { add } +Note over IO,Device: pin_name: "D13"
gpio_direction: D_INPUT
sample_mode: SM_TIMER
period: 1.0 (seconds) + +Device->>GPIO: Configure pin as input +GPIO->>Device: Pin configured + +alt Sample Mode: SM_TIMER + loop Every period seconds + GPIO->>Device: Read pin value + Device->>IO: ws.digitalio.D2B { event } + Note over Device,IO: pin_name: "D13"
value: {type: RAW, value: 1} + end +else Sample Mode: SM_EVENT + GPIO->>GPIO: Attach interrupt + Note over GPIO: Trigger on pin change + GPIO->>Device: Pin value changed + Device->>IO: ws.digitalio.D2B { event } + Note over Device,IO: pin_name: "D13"
value: {type: RAW, value: 0} +end +``` + +### Add a Digital Pin (Output) + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant GPIO as GPIO Controller + +IO->>Device: ws.digitalio.B2D { add } +Note over IO,Device: pin_name: "D12"
gpio_direction: D_OUTPUT
write: { pin_name: "D12", value: ... } + +Device->>GPIO: Configure pin as output +GPIO->>GPIO: Set initial value +GPIO->>Device: Pin configured +``` + +### Write to an Output Pin + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant GPIO as GPIO Controller +participant LED as Physical Output + +IO->>Device: ws.digitalio.B2D { write } +Note over IO,Device: pin_name: "D12"
value: {type: RAW, value: 1} + +Device->>GPIO: Set pin HIGH +GPIO->>LED: Output voltage HIGH +``` + +### Remove a Digital Pin + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant GPIO as GPIO Controller + +IO->>Device: ws.digitalio.B2D { remove } +Note over IO,Device: pin_name: "D13" + +Device->>GPIO: Detach interrupt (if SM_EVENT) +GPIO->>GPIO: Release pin resources +GPIO->>Device: Pin removed +``` + +## Use Cases & Examples + +### Example 1: Button Input (Event-Driven) + +Monitor a button that triggers on press/release: + +``` +ws.digitalio.B2D { add: { + pin_name: "D2", + gpio_direction: D_INPUT_PULL_UP, + sample_mode: SM_EVENT +}} +``` + +When the button is pressed or released, the device sends: +``` +ws.digitalio.D2B { event: { + pin_name: "D2", + value: {type: RAW, value: 0} +}} +``` + +**Best Practices:** +- Use `D_INPUT_PULL_UP` for buttons without external pull-up resistors +- `SM_EVENT` mode is most efficient - only sends updates on state change +- Button logic is typically inverted (LOW = pressed, HIGH = released) + +### Example 2: LED Control + +Control an LED from Adafruit IO: + +``` +ws.digitalio.B2D { add: { + pin_name: "D13", + gpio_direction: D_OUTPUT, + write: { pin_name: "D13", value: {type: RAW, value: 0} } +}} +``` + +To turn the LED on: +``` +ws.digitalio.B2D { write: { + pin_name: "D13", + value: {type: RAW, value: 1} +}} +``` + +### Example 3: Sensor Polling + +Poll a digital sensor every 2 seconds: + +``` +ws.digitalio.B2D { add: { + pin_name: "D7", + gpio_direction: D_INPUT, + sample_mode: SM_TIMER, + period: 2.0 +}} +``` + +The device will send readings every 2 seconds: +``` +ws.digitalio.D2B { event: { + pin_name: "D7", + value: {type: RAW, value: 1} +}} +``` + +### Example 4: Motion Sensor (PIR) + +Detect motion with a PIR sensor: + +``` +ws.digitalio.B2D { add: { + pin_name: "D5", + gpio_direction: D_INPUT, + sample_mode: SM_EVENT +}} +``` + +Motion detected: +``` +ws.digitalio.D2B { event: { + pin_name: "D5", + value: {type: RAW, value: 1} +}} +``` + +## Pin Naming Conventions + +Pin names should match the board's pinout definition in the [WipperSnapper_Boards](https://github.com/adafruit/Wippersnapper_Boards) repository: + +- Arduino-style: `"D0"`, `"D1"`, `"D13"`, etc. +- ESP32: `"GPIO2"`, `"GPIO15"`, etc. +- RP2040: `"GP0"`, `"GP1"`, etc. + +## Sample Mode Guidelines + +### When to use SM_TIMER +- ✓ Monitoring stable signals that don't change frequently +- ✓ Polling sensors that provide digital output +- ✓ Cases where you need regular updates regardless of state +- ✓ When pin changes are too frequent for event mode + +### When to use SM_EVENT +- ✓ Buttons and switches +- ✓ Motion sensors (PIR) +- ✓ Door/window sensors +- ✓ Any input that changes infrequently +- ✓ Battery-powered applications (more efficient) + +**Note:** Not all pins support interrupt (SM_EVENT) mode. Check your board's documentation. + +## Best Practices + +### Input Pins +1. **Pull-up resistors:** Use `D_INPUT_PULL_UP` for buttons/switches without external resistors +2. **Debouncing:** Consider signal stability - some boards have hardware debouncing +3. **Sample period:** For `SM_TIMER`, choose appropriate period based on expected change frequency +4. **Power consumption:** `SM_EVENT` uses less power than `SM_TIMER` for infrequent events + +### Output Pins +1. **Initial state:** Always set initial `value` when adding output pins +2. **Current limits:** Don't drive high-current loads directly - use transistors/MOSFETs +3. **Logic levels:** Ensure 3.3V/5V compatibility with connected devices +4. **Pin capabilities:** Not all pins can be used as outputs - check board documentation + +### General +1. **Pin conflicts:** Don't configure the same pin multiple times simultaneously +2. **Reserved pins:** Avoid pins used for built-in functions (LED, I2C, SPI, etc.) +3. **Cleanup:** Always remove pins when no longer needed to free resources + +## Relationship with Other Components + +Digital IO pins are fundamental and can interact with: + +- **PWM** - Some digital pins support PWM output for dimming LEDs or motor control +- **Servo** - Servos use digital pins with PWM +- **I2C/SPI** - Specific pins are reserved for these buses +- **UART** - TX/RX pins for serial communication + +Always check pin availability before configuring digital IO on pins that might be needed for other protocols. + +## Troubleshooting + +### Input pin always reads the same value +- Check wiring and connections +- Verify pull-up/pull-down resistor configuration +- Test with multimeter to confirm expected voltage levels + +### Event mode not triggering +- Verify pin supports interrupt mode +- Check if pin is configured correctly in board definition +- Consider switching to SM_TIMER for testing + +### Output pin not controlling device +- Verify pin is configured as D_OUTPUT +- Check current requirements of connected device +- Use transistor/MOSFET for high-current loads +- Verify voltage levels match (3.3V vs 5V) diff --git a/proto/wippersnapper/docs/display.md b/proto/wippersnapper/docs/display.md new file mode 100644 index 00000000..d5e3645f --- /dev/null +++ b/proto/wippersnapper/docs/display.md @@ -0,0 +1,505 @@ +# display.proto + +This file details the WipperSnapper messaging API for interfacing with displays connected via SPI, I2C, parallel buses, or DSI. + +## Architecture Overview + +The v2 Display API supports a wide variety of display technologies and connection types through a unified message structure. + +### Display Classes +- **EPD (E-Paper/E-Ink)** - Low-power bistable displays +- **TFT** - Color LCD displays +- **OLED** - Organic LED displays (monochrome or color) +- **LED Backpack** - HT16K33-based 7-segment/14-segment LED displays +- **Character LCD** - Text-based LCD displays + +### Interface Types +- **SPI** - Serial Peripheral Interface (EPD and TFT) +- **I2C** - Inter-Integrated Circuit (OLED, LED backpack, character LCD) +- **TTL RGB666** - 18-bit parallel RGB for Qualia boards +- **i8080** - 8-bit parallel Intel bus for T-DisplayS3/Memento +- **DSI** - MIPI Display Serial Interface for high-resolution displays (e.g., ESP32-P4) + +## Supported Display Drivers + +Drivers are specified as strings (no proto recompilation needed to add new ones). + +### E-Paper/E-Ink Displays (EPD) + +| Driver | Description | +|--------|-------------| +| **SSD1680** | EPD controller for monochrome e-paper | +| **ILI0373** | EPD controller (also known as SSD167) | +| **UC8253** | EPD controller for 3.7" displays | +| **UC8179** | EPD controller for 5.83" displays | +| **UC8151** | EPD controller for flexible e-ink (also ILI0343) | +| **SSD1683** | EPD controller for 4.2" grayscale displays | + +### TFT Displays + +| Driver | Description | +|--------|-------------| +| **ST7789** | TFT LCD controller | + +### OLED Displays + +| Driver | Description | +|--------|-------------| +| **SSD1306** | Monochrome OLED (128x64, 128x32) | +| **SSD1305** | Monochrome OLED | +| **SH1106** | Monochrome OLED (128x64) | +| **SH1107** | Monochrome OLED | + +### LED Backpack Displays + +| Driver | Description | +|--------|-------------| +| **HT16K33** | Adafruit LED backpacks (7-segment, alphanumeric) | + +### Character LCD Displays + +| Driver | Description | +|--------|-------------| +| **MCP23008** | I2C GPIO expander for LCD | +| **PCF8574** | I2C to parallel expander for LCD | + +## Message Envelopes (B2D / D2B) + +All display messages are wrapped in envelope messages that include the display instance name. + +### BrokerToDevice (B2D) + +```protobuf +message B2D { + string name = 1; // Unique instance identifier + oneof payload { + Add add = 2; // Add or replace a display + Remove remove = 3; // Remove a display + Write write = 4; // Write content to a display + } +} +``` + +### DeviceToBroker (D2B) + +```protobuf +message D2B { + string name = 1; // Unique instance identifier + bool did_succeed = 2; // Whether the operation succeeded + oneof payload { + AddedOrReplaced added_or_replaced = 3; + Removed removed = 4; + } +} +``` + +## Display Properties + +Common display properties shared across graphical display types: + +```protobuf +message DisplayProperties { + int32 width = 1; // Display width in pixels + int32 height = 2; // Display height in pixels + int32 rotation = 3; // Clockwise 90° increments (0-3) + int32 text_size = 4; // Text scale factor (1 = 6x8px, 2 = 12x16px, ...) +} +``` + +## Interface Configurations + +### Shared SPI Device Config + +All SPI displays build on `ws.spi.DeviceConfig` from [spi.proto](spi.md), which provides the standard SPI bus and pin assignments (bus, MOSI, SCK, MISO, CS). Display-specific pins (Data/Command, Reset) are added by each display SPI config. + +### TFT SPI Configuration + +Extends the shared SPI config with display-specific pins: + +* **spi** - `ws.spi.DeviceConfig` (bus number, MOSI, SCK, MISO, CS) +* **pin_dc** - Data/Command pin +* **pin_rst** - Reset pin + +### EPD SPI Configuration + +Extends the shared SPI config with display-specific and EPD-specific pins: + +* **spi** - `ws.spi.DeviceConfig` (bus number, MOSI, SCK, MISO, CS) +* **pin_dc** - Data/Command pin +* **pin_rst** - Reset pin +* **pin_sram_cs** - SRAM Chip Select pin (**optional**, for buffering) +* **pin_busy** - Busy signal pin (**optional**, indicates when display is ready) + +### I2C Configuration + +I2C displays use `ws.i2c.DeviceDescriptor` for bus configuration (address, SDA/SCL pins, etc.). See [i2c.md](i2c.md) for details. + +### TTL RGB666 Configuration (Qualia Boards) + +18-bit parallel RGB pin configuration: + +* **pin_r0, pin_r1, pin_r2** - Red channel pins +* **pin_g0, pin_g1, pin_g2** - Green channel pins +* **pin_b0, pin_b1, pin_b2** - Blue channel pins + +### i8080 Configuration (T-DisplayS3, Memento) + +8-bit parallel Intel 8080 bus pin configuration: + +* **pin_d0 through pin_d7** - 8-bit data bus pins +* **pin_cs** - Chip Select pin +* **pin_dc** - Data/Command pin +* **pin_rst** - Reset pin + +### DSI Configuration + +MIPI DSI displays use dedicated differential pads (not GPIOs) for clock and data lanes: + +* **bus** - DSI bus index (0 if only one DSI peripheral) +* **pin_rst** - GPIO pin for display panel reset + +## Display-Specific Configs + +### EPDConfig + +* **mode** - `EPD_MODE_GRAYSCALE4` or `EPD_MODE_MONO` +* **properties** - `DisplayProperties` (width, height, rotation, text_size) + +### LedBackpackConfig + +* **brightness** - 0 (off) to 15 (full brightness) +* **alignment** - `LBA_LEFT` or `LBA_RIGHT` + +### CharLcdConfig + +* **rows** - Number of rows (2, 4, etc.) +* **columns** - Number of columns (16, 20, etc.) + +### DisplayProperties (generic) + +Used for TFT, OLED, TTL RGB666, i8080, and DSI displays: + +* **width, height** - Display dimensions in pixels +* **rotation** - Display rotation (0-3) +* **text_size** - Text scale factor + +## Backlight Configuration + +Backlight control is configured in the `Add` message via a `BacklightConfig` field. It supports two modes: + +```protobuf +message BacklightConfig { + oneof backlight_add { + ws.digitalio.Add backlight_digital = 1; // On/off backlight via digital pin + ws.pwm.Add backlight_pwm = 2; // Dimmable backlight via PWM pin + } +} +``` + +- **Digital backlight**: Simple on/off control using a DigitalIO pin. Control is sent via standard `DigitalIO.Write` messages. +- **PWM backlight**: Variable brightness control using a PWM pin. Control is sent via standard `PWM.Write` messages. + +The backlight pin is configured at display add time (check-in) and then controlled independently through the respective DigitalIO or PWM write paths. + +## Sequence Diagrams + +### Add or Replace a Display + +The same message is used for both adding new displays and updating existing configurations. + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant Display as Display Controller +participant Driver as Display Driver + +IO->>Device: B2D { name, Add } +Note over IO,Device: type: DISPLAY_CLASS_EPD
driver: "UC8179"
panel: "5.83-648x480-mono"
spi_epd: {spi_pins, pin_busy}
config_epd: {mode, properties}
backlight: {backlight_pwm: {...}} + +Device->>Display: Initialize Display Controller +Display->>Driver: Load driver and configure +Driver->>Display: Driver ready + +alt Configuration successful + Display->>Device: Display configured + Device->>IO: D2B { name, did_succeed: true, AddedOrReplaced } +else Configuration failed + Display->>Device: Error details + Device->>IO: D2B { name, did_succeed: false } +end +``` + +### Write Content to Display + +Displays receive text content via the `Write` message. + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant Display as Display Controller +participant Driver as Display Driver + +IO->>Device: B2D { name, Write } +Note over IO,Device: message: "Hello World" + +Device->>Display: Forward text content +Display->>Driver: Render text with text_size +Driver->>Display: Text rendered + +Driver->>Display: Update complete +Note over Display: For EPD: Wait for busy pin
For TFT: Immediate +``` + +### Remove a Display + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant Display as Display Controller +participant Driver as Display Driver + +IO->>Device: B2D { name, Remove } + +Device->>Display: Deinitialize display +Display->>Driver: Cleanup and release resources +Driver->>Display: Resources freed + +Display->>Device: Display removed +Device->>IO: D2B { name, did_succeed: true, Removed } +``` + +## Content Types for Write + +The `Write` message supports a single content type: + +### Text Message + +Monospace text rendered directly on the display: +``` +message: "Temperature: 23.5C\nHumidity: 45%" +``` +- Uses the display's default monospace font +- Sized according to `text_size` in `DisplayProperties` +- Line breaks with `\n` + +For LED backpacks and character LCDs, the text is rendered according to the display type (e.g., numeric segments, character cells). + +## E-Paper Display Considerations + +### Update Speed + +E-Paper displays have slower refresh rates than TFT displays: +- **Monochrome mode** - ~1-2 seconds per full refresh +- **Grayscale mode** - ~5-10 seconds per full refresh + +### Busy Pin + +The **pin_busy** signal indicates when the display is ready: +- HIGH - Display is busy updating +- LOW - Display is ready for next command + +Always check busy pin before sending new content to EPD displays. + +### Image Persistence + +E-Paper displays retain their image without power, making them ideal for: +- Low-power applications +- Status displays that update infrequently +- Outdoor readability in direct sunlight + +## Example Configurations + +### Example 1: 5.83" E-Ink Display (UC8179) + +``` +B2D { + name: "weather-display", + add: { + type: DISPLAY_CLASS_EPD, + driver: "UC8179", + panel: "5.83-648x480-mono", + spi_epd: { + spi: { + bus: 0, + pin_cs: "D8" + }, + pin_dc: "D10", + pin_rst: "D9", + pin_busy: "D11" + }, + config_epd: { + mode: EPD_MODE_MONO, + properties: { + width: 648, + height: 480, + text_size: 3 + } + } + } +} +``` + +### Example 2: TFT Display (ST7789) with PWM Backlight + +``` +B2D { + name: "status-screen", + add: { + type: DISPLAY_CLASS_TFT, + driver: "ST7789", + spi_tft: { + spi: { + bus: 0, + pin_mosi: "D11", + pin_sck: "D13", + pin_cs: "D5" + }, + pin_dc: "D6", + pin_rst: "D9" + }, + config_display: { + width: 240, + height: 135, + rotation: 1, + text_size: 2 + }, + backlight: { + backlight_pwm: { + pin: "D45", + frequency: 1000, + duty_cycle: 65535 + } + } + } +} +``` + +### Example 3: SSD1306 OLED over I2C + +``` +B2D { + name: "status-display", + add: { + type: DISPLAY_CLASS_OLED, + driver: "SSD1306", + panel: "128x64", + i2c: { + device_address: 0x3C + }, + config_display: { + width: 128, + height: 64, + text_size: 1 + } + } +} +``` + +### Example 4: 7-Segment LED Backpack over I2C + +``` +B2D { + name: "clock-display", + add: { + type: DISPLAY_CLASS_LED_BACKPACK, + driver: "HT16K33", + panel: "4digit-7seg", + i2c: { + device_address: 0x70 + }, + config_led: { + brightness: 8, + alignment: LBA_RIGHT + } + } +} +``` + +### Example 5: Character LCD over I2C + +``` +B2D { + name: "info-display", + add: { + type: DISPLAY_CLASS_CHAR_LCD, + driver: "PCF8574", + panel: "16x2", + i2c: { + device_address: 0x27 + }, + config_char_lcd: { + rows: 2, + columns: 16 + } + } +} +``` + +### Example 6: Writing to a Display + +``` +B2D { + name: "clock-display", + write: { + message: "12:34" + } +} +``` + +### Example 7: Add with Initial Content + +The `Add` message supports an optional `write` field for initial display content at check-in: + +``` +B2D { + name: "status-screen", + add: { + type: DISPLAY_CLASS_TFT, + driver: "ST7789", + spi_tft: { ... }, + config_display: { width: 240, height: 135 }, + write: { + message: "Initializing..." + } + } +} +``` + +## Best Practices + +### Display Naming + +Use descriptive names that indicate the display's purpose: +- Good: `"weather-display"`, `"status-screen"`, `"sensor-dashboard"` +- Bad: `"display1"`, `"disp"`, `"d"` + +### Panel Identifiers + +For E-Paper displays especially, the same driver can support multiple panels: +- Format: `"{size}-{resolution}-{color_mode}"` or `"adafruit-{product_id}"` +- Examples: `"5.83-648x480-mono"`, `"4.2-300x400-gray"`, `"adafruit-6397"` + +### Text Sizing + +Choose text_size based on display resolution: +- **Small displays (<128px)**: text_size = 1 +- **Medium displays (128-320px)**: text_size = 2 +- **Large displays (>320px)**: text_size = 3+ + +### Update Frequency + +- **E-Paper**: Limit updates to once per minute or less +- **TFT**: Can update as frequently as needed + +## Related Documentation + +- [spi.proto](spi.md) - Shared SPI bus and device pin configuration +- [i2c.proto](i2c.md) - For I2C-connected display device descriptors +- [pwm.proto](pwm.md) - For PWM backlight control +- [digitalio.proto](digitalio.md) - For digital backlight control diff --git a/proto/wippersnapper/docs/ds18x20.md b/proto/wippersnapper/docs/ds18x20.md new file mode 100644 index 00000000..6a687cf5 --- /dev/null +++ b/proto/wippersnapper/docs/ds18x20.md @@ -0,0 +1,86 @@ +# ds18x20.proto + +This file details the API used by hardware running Adafruit WipperSnapper firmware for interfacing with a DS18B20 temperature sensor. + +**Note about design and implementation**: WipperSnapper defines a pin as a OneWire bus. Only one DS18x20 sensor is allowed per pin. To use multiple DS18x20 sensors, use multiple pins. + +## WipperSnapper Component Definitions + +The following JSON component definition type(s) reference `ds18x20.proto`: +* [ds18x20](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/ds18x20) + +## Architecture Overview + +The v2 DS18x20 API uses message envelopes: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Initialize a OneWire bus and DS18x20 sensor + - `remove` - Deinitialize the sensor and release the pin + +- **D2B (DeviceToBroker)** - Responses and data from device to Adafruit IO + - `added` - Confirmation of sensor initialization + - `event` - Temperature sensor reading + +## Message Details + +### Add + +- **onewire_pin** - Pin to use as a OneWire bus +- **sensor_resolution** - Sensor resolution (9, 10, 11, or 12 bits) +- **period** - Read period in seconds +- **sensor_types** - SI types used by the sensor (`repeated ws.sensor.Type`) + +### Added (response) + +- **is_initialized** - True if the OneWire bus initialized successfully +- **onewire_pin** - The pin being used + +### Event + +- **onewire_pin** - Pin identifying the sensor +- **sensor_events** - Sensor readings (`repeated ws.sensor.Event`) + +## Sequence Diagrams + +### Create: DS18x20 + +Request from a broker to: +1) Initialize a OneWire bus on a desired pin +2) Initialize and configure a DS18x20 Maxim temperature sensor on the OneWire bus + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.ds18x20.B2D { add } +Note over IO, Device: onewire_pin: "D4"
sensor_resolution: 12
period: 10.0
sensor_types: [TEMPERATURE] + +Device->>IO: ws.ds18x20.D2B { added } +Note over IO, Device: is_initialized: true
onewire_pin: "D4" +``` + +### Delete: DS18x20 + +Request from a broker to deinitialize the sensor and release the OneWire pin: + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.ds18x20.B2D { remove } +Note over IO, Device: onewire_pin: "D4" +``` + +### Sensor Event: DS18x20 + +Periodic temperature reading sent from device to broker: + +```mermaid +sequenceDiagram +autonumber + +loop Every period seconds + Device->>IO: ws.ds18x20.D2B { event } + Note over Device,IO: onewire_pin: "D4"
sensor_events: [
{type: TEMPERATURE, value: 23.5}
] +end +``` diff --git a/proto/wippersnapper/docs/i2c.md b/proto/wippersnapper/docs/i2c.md new file mode 100644 index 00000000..005e8141 --- /dev/null +++ b/proto/wippersnapper/docs/i2c.md @@ -0,0 +1,185 @@ + +# i2c.proto + +This file details the API used by hardware running Adafruit WipperSnapper firmware for interfacing with the I2C bus and I2C sensors. + +## WipperSnapper Component Definitions + +The following JSON component definition type(s) reference `i2c.proto`: +* [i2c](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/i2c) + +## Architecture Overview + +The v2 I2C API uses message envelopes for cleaner organization: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `bus_scan` - Scan the I2C bus for devices + - `device_add_replace` - Add or update an I2C device + - `device_remove` - Remove an I2C device + +- **D2B (DeviceToBroker)** - Responses and data from device to Adafruit IO + - `bus_scanned` - Results of I2C bus scan + - `device_added_replaced` - Confirmation of device add/update + - `device_removed` - Confirmation of device removal + - `device_event` - Sensor data from I2C device + +## Status Codes + +### Bus Status + +| Status Code | Description | +|------------|-------------| +| `BS_SUCCESS` | I2C bus successfully initialized | +| `BS_ERROR_HANG` | I2C Bus hang detected - user should reset board | +| `BS_ERROR_PULLUPS` | I2C bus failed - SDA or SCL needs a pull-up resistor | +| `BS_ERROR_WIRING` | I2C bus communication failed - check wiring | +| `BS_ERROR_INVALID_CHANNEL` | I2C MUX channel must be 0-7 | + +### Device Status + +| Status Code | Description | +|------------|-------------| +| `DS_SUCCESS` | I2C device successfully initialized | +| `DS_FAIL_INIT` | I2C device failed to initialize | +| `DS_FAIL_DEINIT` | I2C device failed to deinitialize | +| `DS_FAIL_UNSUPPORTED_SENSOR` | WipperSnapper firmware outdated - update required | +| `DS_NOT_FOUND` | I2C device not found at specified address | + +## Device Descriptor + +All I2C operations use a `DeviceDescriptor` to identify devices: + +- **pin_scl** (`uint32`) - Pin number for the I2C SCL line +- **pin_sda** (`uint32`) - Pin number for the I2C SDA line +- **device_address** (`uint32`) - 7-bit I2C address +- **mux_address** (`uint32`) - Optional I2C multiplexer address +- **mux_channel** (`uint32`) - Optional I2C multiplexer channel + +## Scan + +The `Scan` message requests a bus scan: + +- **pin_scl** (`uint32`) - SCL pin number +- **pin_sda** (`uint32`) - SDA pin number +- **mux_address** (`uint32`) - Optional multiplexer address to scan through + +## Sequence Diagrams + +### I2C Scan + +On Adafruit.io, an I2C scan can be initialized one of two ways: +1) User clicks "I2C Scan" button +2) User clicks an I2C component from the Component Picker + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant I2C as I2C Controller + +IO->>Device: ws.i2c.B2D { bus_scan } +Note over IO,Device: pin_scl: 22
pin_sda: 21 + +Device->>I2C: Initialize bus (if needed) +I2C->>I2C: Scan for devices on bus + +I2C->>Device: List of found addresses +Device->>IO: ws.i2c.D2B { bus_scanned } +Note over Device,IO: found_devices: [DeviceDescriptor...]
bus_status: BS_SUCCESS +``` + +### Add or Replace an I2C Device + +**Note:** I2C devices may contain _multiple_ sensors (e.g., a BME280 has temperature, humidity, and pressure sensors). The `device_sensor_types` array specifies all sensor types, and `device_period` sets the polling interval. + +The same `DeviceAddOrReplace` message is used for both creating new devices and updating existing ones. + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant I2C as I2C Controller +participant Sensor as I2C Sensor + +IO->>Device: ws.i2c.B2D { device_add_replace } +Note over IO,Device: device_description: {
pin_scl: 22, pin_sda: 21,
device_address: 0x77
}
device_name: "bme280"
device_period: 5.0
device_sensor_types: [TEMPERATURE, HUMIDITY, PRESSURE] + +Device->>I2C: Initialize bus (if needed) +I2C->>Sensor: Initialize device at address +Sensor->>I2C: Init success/fail + +I2C->>I2C: Configure polling timer + +I2C->>Device: Device ready +Device->>IO: ws.i2c.D2B { device_added_replaced } +Note over Device,IO: device_description: {device_address: 0x77}
bus_status: BS_SUCCESS
device_status: DS_SUCCESS +``` + +### Sending Sensor Data from an I2C Device + +Sensor devices periodically send data to Adafruit IO based on their configured `device_period`. Since an I2C device may have multiple sensors (e.g., BME280 has temperature, humidity, and pressure), the `device_events` array contains all sensor readings. + +```mermaid +sequenceDiagram +autonumber +participant Sensor as I2C Sensor +participant I2C as I2C Controller +participant Device as WipperSnapper Device +participant IO as Adafruit IO + +loop Every device_period seconds + I2C->>Sensor: Read all sensors + Sensor->>I2C: Sensor values + I2C->>Device: Package sensor data + Device->>IO: ws.i2c.D2B { device_event } + Note over Device,IO: device_description: {device_address: 0x77}
device_events: [
{type: TEMPERATURE, value: 23.5},
{type: HUMIDITY, value: 45.2},
{type: PRESSURE, value: 1013.25}
] +end +``` + +### Remove an I2C Device + +Removing an I2C device deinitializes it and frees resources. + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant I2C as I2C Controller +participant Sensor as I2C Sensor + +IO->>Device: ws.i2c.B2D { device_remove } +Note over IO,Device: device_description: {device_address: 0x77} + +Device->>I2C: Deinitialize device +I2C->>Sensor: Stop polling and cleanup +Sensor->>I2C: Deinitialized + +I2C->>Device: Device removed +Device->>IO: ws.i2c.D2B { device_removed } +Note over Device,IO: device_description: {device_address: 0x77}
did_remove: true +``` + +## I2C Display Devices + +I2C-connected displays (OLEDs, LED backpacks, character LCDs) are configured through the unified display API in `display.proto`. The display `Add` message uses `ws.i2c.DeviceDescriptor` as its I2C interface configuration. See [display.md](display.md) for details. + +## I2C Multiplexer Support + +For boards with multiple I2C devices sharing the same address, use an I2C multiplexer (TCA9548A): + +- **mux_address** - Address of the multiplexer (typically 0x70-0x77) +- **mux_channel** - Channel number (0-7) where the device is connected + +Example DeviceDescriptor with multiplexer: +``` +device_description: { + pin_scl: 22, + pin_sda: 21, + device_address: 0x29, + mux_address: 0x70, + mux_channel: 2 +} +``` diff --git a/proto/wippersnapper/docs/pixels.md b/proto/wippersnapper/docs/pixels.md new file mode 100644 index 00000000..96138a8b --- /dev/null +++ b/proto/wippersnapper/docs/pixels.md @@ -0,0 +1,185 @@ +# pixels.proto + +This file details the WipperSnapper messaging API for interfacing with a strand of addressable RGB(W) pixels (Adafruit NeoPixel/WS2812b, DotStar/APA102). + +## WipperSnapper Components + +The following component definitions reference `pixels.proto`: +* [Adafruit_DotStar](https://github.com/adafruit/Wippersnapper_Components/pull/44) +* [Adafruit_NeoPixels](https://github.com/adafruit/Wippersnapper_Components/pull/44) + +## Architecture Overview + +The v2 Pixels API uses message envelopes: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Add a strand of addressable pixels + - `remove` - Remove a strand and release resources + - `write` - Write a color to a strand + +- **D2B (DeviceToBroker)** - Responses from device to Adafruit IO + - `added` - Confirmation of strand initialization + +## Enums + +### Type +| Value | Description | +|-------|-------------| +| `T_UNSPECIFIED` | Unspecified (error) | +| `T_NEOPIXEL` | NeoPixel strand | +| `T_DOTSTAR` | DotStar strand | + +### Order (Color Ordering) +| Value | Description | +|-------|-------------| +| `O_GRB` | Green, Red, Blue (NeoPixel default) | +| `O_GRBW` | Green, Red, Blue, White | +| `O_RGB` | Red, Green, Blue | +| `O_RGBW` | Red, Green, Blue, White | +| `O_BRG` | Blue, Red, Green (DotStar default) | +| `O_RBG` | Red, Blue, Green | +| `O_GBR` | Green, Blue, Red | +| `O_BGR` | Blue, Green, Red | + +## Message Details + +### Add + +- **type** - Pixel type (`T_NEOPIXEL` or `T_DOTSTAR`) +- **num** - Number of pixels in the strand +- **ordering** - Color ordering (e.g., `O_GRB`) +- **brightness** - Strand brightness (0-255) +- **pin_data** - Data pin for NeoPixel or DotStar +- **pin_dotstar_clock** - Clock pin (DotStar only) +- **write** - Optional initial write (used during check-in) + +### Write + +- **pin_data** - Data pin of the strand +- **color** - 32-bit color value (MSB: white for RGBW or ignored for RGB, then red, green, blue LSB) + +### Added (response) + +- **is_success** - True if strand initialized successfully +- **pin_data** - Data pin of the responding strand + +## Sequence Diagrams + +### Create: NeoPixel + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_NEOPIXEL
num: 8
ordering: O_GRB
brightness: 128
pin_data: "D5" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" +``` + +### Write: NeoPixel + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { write } +Note over IO, Device: pin_data: "D5"
color: 0x00FF0000 (red) +``` + +### Update: NeoPixel + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { remove } +Note over IO, Device: pin_data: "D5" + +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_NEOPIXEL
num: 16
ordering: O_GRB
brightness: 64
pin_data: "D5" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" +``` + +### Delete: NeoPixel + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { remove } +Note over IO, Device: pin_data: "D5" +``` + +### Sync: NeoPixel +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_NEOPIXEL
num: 8
ordering: O_GRB
brightness: 128
pin_data: "D5" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" + +IO->>Device: ws.pixels.B2D { write } +Note over IO, Device: pin_data: "D5"
color: 0x0000FF00 (green, from feed's last_value) +``` + + +### Create: DotStar + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_DOTSTAR
num: 12
ordering: O_BRG
brightness: 128
pin_data: "D5"
pin_dotstar_clock: "D6" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" +``` + +### Write: DotStar + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { write } +Note over IO, Device: pin_data: "D5"
color: 0x0000FF00 (green) +``` + +### Update: DotStar + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { remove } +Note over IO, Device: pin_data: "D5" + +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_DOTSTAR
num: 24
ordering: O_BRG
brightness: 64
pin_data: "D5"
pin_dotstar_clock: "D6" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" +``` + +### Delete: DotStar + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { remove } +Note over IO, Device: pin_data: "D5" +``` + +### Sync: DotStar +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pixels.B2D { add } +Note over IO, Device: type: T_DOTSTAR
num: 12
ordering: O_BRG
brightness: 128
pin_data: "D5"
pin_dotstar_clock: "D6" + +Device->>IO: ws.pixels.D2B { added } +Note over Device,IO: is_success: true
pin_data: "D5" + +IO->>Device: ws.pixels.B2D { write } +Note over IO, Device: pin_data: "D5"
color: 0x00FF0000 (red, from feed's last_value) +``` diff --git a/proto/wippersnapper/docs/pwm.md b/proto/wippersnapper/docs/pwm.md new file mode 100644 index 00000000..ce2257c9 --- /dev/null +++ b/proto/wippersnapper/docs/pwm.md @@ -0,0 +1,169 @@ +# pwm.proto + +This file details the WipperSnapper messaging API for interfacing with PWM output components. + +PWM components either have a fixed frequency with a variable duty cycle _or_ a variable frequency with a fixed duty cycle. + +## WipperSnapper Components + +The following WipperSnapper components utilize `pwm.proto`: + +* Dimmable LED (Fixed Frequency, variable Duty Cycle) +* Piezo Buzzer (Variable Frequency, fixed Duty Cycle) + +## Architecture Overview + +The v2 PWM API uses message envelopes: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Attach/allocate a PWM pin + - `remove` - Detach a PWM pin + - `write` - Write duty cycle or frequency to a pin + +- **D2B (DeviceToBroker)** - Responses from device to Adafruit IO + - `added` - Confirmation of pin attachment + +## Message Details + +### Add + +Attaches a PWM pin. On ESP32, this attaches a pin to a LEDC channel/timer group. + +- **pin** - The pin to attach +- **frequency** - PWM frequency in Hz +- **resolution** - Resolution in bits +- **is_inverted** - If true, inverts the output (active low) +- **write** - Optional initial write (used during check-in) + +### Write + +Writes either a duty cycle or frequency (mutually exclusive via oneof): + +- **pin** - The pin to write to +- **duty_cycle** - Duty cycle value (range 0 to 2^resolution) +- **frequency** - Frequency in Hz (duty cycle fixed at 50%) + +### Added (response) + +- **pin** - The pin that was configured +- **did_attach** - True if attachment was successful + +## Sequence Diagrams + +### Create: Dimmable LED + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D5"
frequency: 5000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D5"
did_attach: true +``` + + +### Write: Dimmable LED +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { write } +Note over IO, Device: pin: "D5"
duty_cycle: 128 +``` + +### Update: Dimmable LED +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { remove } +Note over IO, Device: pin: "D5" + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D5"
frequency: 5000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D5"
did_attach: true +``` + +### Delete: Dimmable LED +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { remove } +Note over IO, Device: pin: "D5" +``` + +### Sync: Dimmable LED +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D5"
frequency: 5000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D5"
did_attach: true + +IO->>Device: ws.pwm.B2D { write } +Note over IO, Device: pin: "D5"
duty_cycle: 128 (from feed's last_value) +``` + +### Create: Piezo Buzzer + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D6"
frequency: 1000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D6"
did_attach: true +``` + + +### Write: Piezo Buzzer +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { write } +Note over IO, Device: pin: "D6"
frequency: 440 (any >0 to play, 0 to stop) +``` + +### Update: Piezo Buzzer +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { remove } +Note over IO, Device: pin: "D6" + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D6"
frequency: 1000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D6"
did_attach: true +``` + +### Delete: Piezo Buzzer +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.pwm.B2D { remove } +Note over IO, Device: pin: "D6" +``` + +### Sync: Piezo Buzzer +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.pwm.B2D { add } +Note over IO, Device: pin: "D6"
frequency: 1000
resolution: 12 + +Device->>IO: ws.pwm.D2B { added } +Note over IO, Device: pin: "D6"
did_attach: true + +IO->>Device: ws.pwm.B2D { write } +Note over IO, Device: pin: "D6"
frequency: 440 (from feed's last_value) +``` diff --git a/proto/wippersnapper/docs/servo.md b/proto/wippersnapper/docs/servo.md new file mode 100644 index 00000000..9e544cec --- /dev/null +++ b/proto/wippersnapper/docs/servo.md @@ -0,0 +1,101 @@ +# servo.proto + +This file details the WipperSnapper messaging API for interfacing with servo output components. + +## WipperSnapper Components + +The following WipperSnapper components utilize `servo.proto`: +* [Generic Servo](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/servo/servo) + +## Architecture Overview + +The v2 Servo API uses message envelopes: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Attach a servo to a pin + - `remove` - Detach a servo from a pin + - `write` - Write a pulse width to a servo + +- **D2B (DeviceToBroker)** - Responses from device to Adafruit IO + - `added` - Confirmation of servo attachment + +## Message Details + +### Add + +- **servo_pin** - Pin to attach the servo to +- **freq** - PWM frequency (default 50Hz) +- **min_pulse_width** - Minimum pulse length in uS (default 500uS) +- **max_pulse_width** - Maximum pulse length in uS (default 2500uS) +- **write** - Optional initial write (used during check-in) + +### Write + +- **servo_pin** - Pin to address +- **pulse_width** - Pulse width in uS (device converts to duty cycle at 50Hz) + +### Added (response) + +- **attach_success** - True if servo was attached successfully +- **servo_pin** - Pin that was configured + +## Sequence Diagrams + +### Create: Servo + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.servo.B2D { add } +Note over IO, Device: servo_pin: "D9"
freq: 50
min_pulse_width: 500
max_pulse_width: 2500 + +Device->>IO: ws.servo.D2B { added } +Note over IO, Device: attach_success: true
servo_pin: "D9" +``` + +### Write: Servo + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.servo.B2D { write } +Note over IO, Device: servo_pin: "D9"
pulse_width: 1500 +``` + +### Update: Servo + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.servo.B2D { remove } +Note over IO, Device: servo_pin: "D9" + +IO->>Device: ws.servo.B2D { add } +Note over IO, Device: servo_pin: "D9"
freq: 50
min_pulse_width: 500
max_pulse_width: 2500 + +Device->>IO: ws.servo.D2B { added } +Note over IO, Device: attach_success: true
servo_pin: "D9" +``` + +### Delete: Servo + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.servo.B2D { remove } +Note over IO, Device: servo_pin: "D9" +``` + +### Sync: Servo + +```mermaid +sequenceDiagram +autonumber +IO->>Device: ws.servo.B2D { add } +Note over IO, Device: servo_pin: "D9"
freq: 50
min_pulse_width: 500
max_pulse_width: 2500 + +Device->>IO: ws.servo.D2B { added } +Note over IO, Device: attach_success: true
servo_pin: "D9" +``` diff --git a/proto/wippersnapper/docs/signal.md b/proto/wippersnapper/docs/signal.md new file mode 100644 index 00000000..e05f9239 --- /dev/null +++ b/proto/wippersnapper/docs/signal.md @@ -0,0 +1,132 @@ +# signal.proto + +This file defines the top-level message routing for all WipperSnapper v2 communication between the MQTT broker and devices. + +## Message Format + +The signal file contains two root messages: `BrokerToDevice` and `DeviceToBroker`. Each uses a `oneof` payload that routes to the appropriate component's B2D or D2B envelope message. + +### BrokerToDevice + +Sent from the broker to a device. Contains a `oneof payload` selecting one component: + +```protobuf +message BrokerToDevice { + oneof payload { + // System Events + ws.error.ErrorB2D error = 10; + + // Device Interactions + ws.checkin.B2D checkin = 20; + ws.sleep.B2D sleep = 21; + + // Component Interactions + ws.digitalio.B2D digitalio = 30; + ws.analogio.B2D analogio = 31; + ws.servo.B2D servo = 32; + ws.pwm.B2D pwm = 33; + ws.pixels.B2D pixels = 34; + ws.ds18x20.B2D ds18x20 = 35; + ws.display.B2D display = 36; + ws.uart.B2D uart = 37; + ws.i2c.B2D i2c = 38; + ws.gps.B2D gps = 39; + } +} +``` + +### DeviceToBroker + +Sent from a device to the broker. Contains a `oneof payload` selecting one component: + +```protobuf +message DeviceToBroker { + oneof payload { + // System Events + ws.error.ErrorD2B error = 10; + + // Device Interactions + ws.checkin.D2B checkin = 20; + ws.sleep.D2B sleep = 21; + + // Component Interactions + ws.digitalio.D2B digitalio = 30; + ws.analogio.D2B analogio = 31; + ws.servo.D2B servo = 32; + ws.pwm.D2B pwm = 33; + ws.pixels.D2B pixels = 34; + ws.ds18x20.D2B ds18x20 = 35; + ws.display.D2B display = 36; + ws.uart.D2B uart = 37; + ws.i2c.D2B i2c = 38; + ws.gps.D2B gps = 39; + } +} +``` + +## Payload Message Naming Conventions + +Each component defines its own B2D and D2B envelope messages. Within those envelopes, payload fields generally follow these conventions: + +* `add` - Configure and add a component to a device +* `remove` - Release a component's resources and remove it +* `write` - Write data or commands to an output component +* `event` - Sensor data or pin state from the device + +Some components have specialized operations: +* `bus_scan` / `bus_scanned` - I2C bus scanning +* `added` / `added_or_replaced` - Confirmation responses +* `removed` - Removal confirmation + +## Sequence Diagram + +### High-Level Operation + +```mermaid +sequenceDiagram +autonumber + +IO Broker->>Device: ws.signal.BrokerToDevice +Note over IO Broker,Device: oneof payload selects component
(e.g., digitalio, i2c, display, ...) + +Device->>Device: Route to component handler +Device->>Device: Process component B2D message + +Device->>IO Broker: ws.signal.DeviceToBroker +Note over IO Broker,Device: oneof payload contains response
(e.g., event, added, removed, ...) +``` + +### Example: Digital IO Write + +```mermaid +sequenceDiagram +autonumber + +IO Broker->>Device: BrokerToDevice { digitalio: B2D { write: { pin_name: "D13", value: ... } } } +Device->>Device: Route to digitalio handler +Device->>Device: Set pin D13 HIGH +``` + +## Component Summary + +| Field # | Component | Package | B2D Operations | D2B Operations | +|---------|-----------|---------|----------------|----------------| +| 10 | error | ws.error | ErrorB2D | ErrorD2B | +| 20 | checkin | ws.checkin | Response | Request, Complete | +| 21 | sleep | ws.sleep | - | - | +| 30 | digitalio | ws.digitalio | Add, Remove, Write | Event | +| 31 | analogio | ws.analogio | Add, Remove | Event | +| 32 | servo | ws.servo | Add, Remove, Write | Added | +| 33 | pwm | ws.pwm | Add, Remove, Write | Added | +| 34 | pixels | ws.pixels | Add, Remove, Write | Added | +| 35 | ds18x20 | ws.ds18x20 | Add, Remove | Added, Event | +| 36 | display | ws.display | Add, Remove, Write | AddedOrReplaced, Removed | +| 37 | uart | ws.uart | Add, Remove, Write | Added, Written, InputEvent | +| 38 | i2c | ws.i2c | Scan, DeviceAddOrReplace, DeviceRemove | Scanned, DeviceAddedOrReplaced, DeviceRemoved, DeviceEvent | +| 39 | gps | ws.gps | - | - | + +## Related Documentation + +- [checkin.md](checkin.md) - Device registration and component initialization +- [wippersnapper_device_overview.md](wippersnapper_device_overview.md) - Complete device flow +- Individual component docs: [digitalio](digitalio.md), [analogio](analogio.md), [i2c](i2c.md), [display](display.md), [pwm](pwm.md), [servo](servo.md), [pixels](pixels.md), [ds18x20](ds18x20.md), [uart](uart.md) diff --git a/proto/wippersnapper/docs/spi.md b/proto/wippersnapper/docs/spi.md new file mode 100644 index 00000000..4f3a741b --- /dev/null +++ b/proto/wippersnapper/docs/spi.md @@ -0,0 +1,64 @@ +# spi.proto + +Reusable SPI bus and device pin configuration, shared across WipperSnapper component types. + +## DeviceConfig + +Identifies an SPI device by bus number and pin assignments for the four standard SPI signals plus chip select. + +```protobuf +message DeviceConfig { + int32 bus = 1; // SPI bus number (0, 1, ...) + string pin_mosi = 2; // MOSI pin + string pin_sck = 3; // SCK pin + string pin_miso = 4; // MISO pin (leave empty if device is write-only) + string pin_cs = 5; // Chip Select pin +} +``` + +### Field Details + +| Field | Description | +|-------|-------------| +| **bus** | SPI peripheral index. `0` selects the default/first SPI bus. | +| **pin_mosi** | Master Out Slave In. Required for all SPI devices. | +| **pin_sck** | Serial Clock. Required for all SPI devices. | +| **pin_miso** | Master In Slave Out. Optional — omit for write-only devices (displays, DACs). | +| **pin_cs** | Chip Select (active low). Each device on a shared bus needs its own CS pin. | + +### Hardware SPI vs Software SPI + +When `pin_mosi` and `pin_sck` match the board's default SPI bus pins, the firmware uses hardware SPI (fast DMA transfers). Non-default pins fall back to software bit-bang SPI (slower). + +## Usage by Other Protos + +`DeviceConfig` is imported by component protos that communicate over SPI. Each component adds its own peripheral-specific pins on top. + +### Displays (display.proto) + +Display SPI configs extend `DeviceConfig` with Data/Command and Reset pins: + +```protobuf +import "spi.proto"; + +message TftSpiConfig { + ws.spi.DeviceConfig spi = 1; // SPI bus + standard pins + string pin_dc = 2; // Data/Command pin + string pin_rst = 3; // Reset pin +} + +message EpdSpiConfig { + ws.spi.DeviceConfig spi = 1; // SPI bus + standard pins + string pin_dc = 2; // Data/Command pin + string pin_rst = 3; // Reset pin + string pin_sram_cs = 4; // SRAM Chip Select (optional) + string pin_busy = 5; // Busy signal pin +} +``` + +See [display.md](display.md) for full display API documentation. + +## Related Documentation + +- [display.md](display.md) — Display API (SPI TFT and EPD displays) +- [i2c.md](i2c.md) — I2C interface (analogous shared bus config) diff --git a/proto/wippersnapper/docs/uart.md b/proto/wippersnapper/docs/uart.md new file mode 100644 index 00000000..2b6bc265 --- /dev/null +++ b/proto/wippersnapper/docs/uart.md @@ -0,0 +1,161 @@ +# uart.proto + +This file details the WipperSnapper messaging API for interfacing with a UART bus. + +## WipperSnapper Components + +The following WipperSnapper components utilize `uart.proto`: + +* PMS* Air Quality Sensors +* Adafruit Universal GPS module using the MTK33x9 chipset +* Generic UART input/output devices +* Trinamic stepper motors / DYNAMIXEL servos + +## Architecture Overview + +The v2 UART API uses message envelopes: + +- **B2D (BrokerToDevice)** - Commands from Adafruit IO to device + - `add` - Configure a UART port and attach a device + - `remove` - Detach a device and deinitialize the port + - `write` - Write data (bytes or text) to a device + +- **D2B (DeviceToBroker)** - Responses and data from device to Adafruit IO + - `added` - Confirmation of device attachment + - `written` - Confirmation of bytes written + - `input_event` - Sensor data from a UART input device + +## Enums + +### DeviceType + +| Value | Description | +|-------|-------------| +| `DT_UNSPECIFIED` | Unspecified device type | +| `DT_GENERIC_INPUT` | Generic UART input device | +| `DT_GENERIC_OUTPUT` | Generic UART output device | +| `DT_GPS` | GPS module | +| `DT_PM25AQI` | PM2.5 air quality sensor | +| `DT_TM22XX` | Trinamic stepper driver | + +### PacketFormat + +Serial data format (data bits, parity, stop bits). Common values: + +| Value | Description | +|-------|-------------| +| `PF_8N1` | 8 data bits, no parity, 1 stop bit (most common) | +| `PF_8N2` | 8 data bits, no parity, 2 stop bits | +| `PF_8E1` | 8 data bits, even parity, 1 stop bit | +| `PF_8O1` | 8 data bits, odd parity, 1 stop bit | + +(Also supports 5/6/7-bit variants with N/E/O parity and 1/2 stop bits.) + +### GenericDeviceLineEnding + +| Value | Description | +|-------|-------------| +| `GDLE_LF` | Newline (LF) | +| `GDLE_CRLF` | Carriage return + newline (CRLF) | +| `GDLE_TIMEOUT_100MS` | 100ms timeout between reads | +| `GDLE_TIMEOUT_1000MS` | 1s timeout between reads | + +## Message Details + +### Add + +Contains two sub-messages: + +**SerialConfig** (port configuration): +- **pin_rx** / **pin_tx** - RX and TX pins +- **device_name** - Device path (CPython only, e.g., `/dev/ttyUSB0`) +- **uart_nbr** - UART port number (0, 1, 2, etc.) +- **baud_rate** - Baud rate in bits per second +- **format** - Packet format (e.g., `PF_8N1`) +- **timeout** - Max milliseconds to wait for serial data (default 1000) +- **use_sw_serial** - Use software serial instead of hardware +- **sw_serial_invert** - Invert UART signal on RX/TX pins + +**DeviceConfig** (device-specific): +- **device_type** - Type of device (`DT_GPS`, `DT_PM25AQI`, etc.) +- **device_id** - Unique identifier string +- **oneof config**: `GenericInputConfig`, `TrinamicDynamixelConfig`, or `PM25AQIConfig` + +**Optional**: `write` field for initial write during check-in. + +### Descriptor + +Used in Remove, Write, Written, and InputEvent to identify the UART device: +- **uart_nbr** - UART port number +- **type** - Device type +- **device_id** - Device identifier + +### Write + +- **descriptor** - Identifies the target device +- **oneof payload**: `bytes_data` (raw bytes) or `text_data` (string) + +### Written (response) + +- **descriptor** - Identifies the device +- **bytes_written** - Number of bytes written + +### InputEvent + +- **descriptor** - Identifies the source device +- **events** - Sensor readings (`repeated ws.sensor.Event`) + +## Sequence Diagrams + +### Attaching a UART Component + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.uart.B2D { add } +Note over IO, Device: cfg_serial: {
pin_rx: "D0", pin_tx: "D1",
uart_nbr: 1, baud_rate: 9600,
format: PF_8N1
}
cfg_device: {
device_type: DT_PM25AQI,
device_id: "pm25-1",
pm25aqi: { period: 5000 }
} + +Device->>Device: Initialize UART port 1 +Device->>Device: Attach PM2.5 driver + +Device->>IO: ws.uart.D2B { added } +Note over Device, IO: descriptor: { uart_nbr: 1, type: DT_PM25AQI, device_id: "pm25-1" }
success: true +``` + +### Sensor Data from a UART Input Device + +```mermaid +sequenceDiagram +autonumber + +loop Every period milliseconds + Device->>IO: ws.uart.D2B { input_event } + Note over Device, IO: descriptor: { uart_nbr: 1, device_id: "pm25-1" }
events: [
{type: PM25_STD, value: 12.0},
{type: PM100_STD, value: 18.0}
] +end +``` + +### Writing to a UART Output Device + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.uart.B2D { write } +Note over IO, Device: descriptor: { uart_nbr: 1, device_id: "stepper-1" }
text_data: "G1 X10 Y20" + +Device->>IO: ws.uart.D2B { written } +Note over Device, IO: descriptor: { uart_nbr: 1, device_id: "stepper-1" }
bytes_written: 11 +``` + +### Detaching a UART Component + +```mermaid +sequenceDiagram +autonumber + +IO->>Device: ws.uart.B2D { remove } +Note over IO, Device: descriptor: { uart_nbr: 1, device_id: "pm25-1" } + +Device->>Device: Detach driver, deinitialize port +``` diff --git a/proto/wippersnapper/docs/wippersnapper_device_overview.md b/proto/wippersnapper/docs/wippersnapper_device_overview.md new file mode 100644 index 00000000..5b1103a4 --- /dev/null +++ b/proto/wippersnapper/docs/wippersnapper_device_overview.md @@ -0,0 +1,746 @@ +# WipperSnapper v2 Happy Path: Complete Device Flow + +This document demonstrates the complete "happy path" flow for a WipperSnapper v2 device, showcasing the new B2D/D2B envelope architecture. This serves as a comprehensive overview of how all the WipperSnapper protocol buffers work together in the v2 API. + +## Key Changes in v2 + +### Message Envelope Architecture + +**v2 introduces a cleaner message organization using envelopes:** + +- **B2D (BrokerToDevice)** - All commands from Adafruit IO to device +- **D2B (DeviceToBroker)** - All responses and data from device to Adafruit IO + +Each component (digitalio, analogio, i2c, etc.) has its own B2D and D2B envelope messages with specific payloads. + +### Benefits of v2 Architecture + +1. **Cleaner code organization** - Envelope pattern groups related messages +2. **Easier routing** - Top-level message type identifies component +3. **Better extensibility** - Adding new message types doesn't break existing code +4. **Type safety** - Stronger typing with oneof payloads + +--- + +## Overview + +The complete flow consists of: + +1. **Device Registration** - Device checks in with Adafruit IO +2. **Component Addition** - Adding various hardware components +3. **Data Flow** - Sensors send data, outputs receive commands +4. **Component Management** - Updating and removing components + +--- + +## 1. Complete Boot-to-Operation Example + +### The Complete Flow: Device Boot → Component Registration → Operation + +This example shows a device booting up, checking in with Adafruit IO, receiving its component configuration, and then operating normally. We'll follow an LED on D13 through its complete lifecycle. + +```mermaid +sequenceDiagram +autonumber +participant Device as WipperSnapper Device +participant IO as Adafruit IO +participant Boards as Boards Repository +participant LED as LED on D13 + +Note over Device: POWER ON - Device Boots + +Device->>Device: Initialize firmware +Device->>Device: Load persistent config (if any) + +Note over Device,IO: PHASE 1: Check-In + +Device->>IO: ws.checkin.D2B {
request: {
hardware_uid: "ESP32-C3-A1B2C3",
firmware_version: "2.0.0"
}
} + +IO->>Boards: Lookup "ESP32-C3" +Boards->>IO: Board definition found + +IO->>IO: Load stored components
for this device + +IO->>Device: ws.checkin.B2D {
response: {
response: R_OK,
total_gpio_pins: 20,
total_analog_pins: 6,
reference_voltage: 3.3,
component_adds: {
digitalio_adds: [{
pin_name: "D13",
gpio_direction: D_OUTPUT,
write: { pin_name: "D13", value: ... }
}],
i2c_adds: [{
device_description: {device_address: 0x77},
device_name: "bme280",
device_period: 60.0,
device_sensor_types: [TEMPERATURE, HUMIDITY]
}]
}
}
} + +Note over Device: PHASE 2: Component Initialization + +Device->>Device: Configure GPIO controller
(20 pins, 3.3V) +Device->>Device: Configure Analog controller
(6 pins, 3.3V ref) + +Note over Device: Process component_adds[0]: Digital Pin + +Device->>LED: Configure D13 as OUTPUT +LED->>Device: Pin configured, initial value: LOW + +Note over Device: Process component_adds[1]: I2C Sensor + +Device->>Device: Initialize I2C bus +Device->>Device: Initialize BME280 at 0x77 +Device->>Device: Start polling timer (60s) + +Device->>IO: ws.checkin.D2B {
complete: {}
} + +Note over Device,IO: PHASE 3: Normal Operation Begins + +Note over Device: User Action: Turn LED ON via Dashboard + +IO->>Device: ws.digitalio.B2D {
write: {
pin_name: "D13",
value: true
}
} + +Device->>LED: Set pin HIGH +Note over LED: LED turns ON ✓ + +Note over Device: 60 seconds pass... + +Device->>IO: ws.i2c.D2B {
device_event: {
device_description: {device_address: 0x77},
device_events: [
{type: TEMPERATURE, value: 23.5},
{type: HUMIDITY, value: 45.0}
]
}
} + +Note over Device: User Action: Turn LED OFF (2nd Write) + +IO->>Device: ws.digitalio.B2D {
write: {
pin_name: "D13",
value: false
}
} + +Device->>LED: Set pin LOW +Note over LED: LED turns OFF ✓ + +Note over Device,IO: System continues operating... +``` + +### Key Points in This Flow: + +1. **Boot Phase**: Device initializes and loads any local persistent config +2. **Check-In Request**: Device identifies itself to IO with hardware_uid and firmware version +3. **Check-In Response**: IO returns board capabilities AND all configured components +4. **Component Initialization**: Device processes each component in `component_adds` array +5. **Complete Signal**: Device signals it's ready for normal operation +6. **First Write**: User triggers the first write operation (LED ON) +7. **Sensor Data**: I2C sensor sends periodic data +8. **Second Write**: User triggers second write operation (LED OFF) + +--- + +## 2. Device Check-In Structure + +### Check-In Request (D2B) + +```protobuf +ws.checkin.D2B { + request: { + hardware_uid: "boardname-MACADDR", + firmware_version: "2.0.0" + } +} +``` + +### Check-In Response (B2D) with Components + +```protobuf +ws.checkin.B2D { + response: { + response: R_OK, + total_gpio_pins: 20, + total_analog_pins: 6, + reference_voltage: 3.3, + + // All pre-configured components sent here! + component_adds: { + digitalio_adds: [{...}, {...}], + analogio_adds: [{...}], + i2c_adds: [{...}], + display_adds: [{...}], + // ... more component types + } + } +} +``` + +### Complete Signal (D2B) + +```protobuf +ws.checkin.D2B { + complete: {} +} +``` + +This signals the device has finished initialization and is ready for normal operation. + +--- + +## 3. Component Registration Details + +### ComponentAdds Message Structure + +The `ComponentAdds` message contains separate repeated fields for each component type: + +```protobuf +message ComponentAdds { + repeated ws.digitalio.Add digitalio_adds = 1; + repeated ws.analogio.Add analogio_adds = 2; + repeated ws.servo.Add servo_adds = 3; + repeated ws.pwm.Add pwm_adds = 4; + repeated ws.pixels.Add pixels_adds = 5; + repeated ws.ds18x20.Add ds18x20_adds = 6; + repeated ws.uart.Add uart_adds = 7; + repeated ws.i2c.DeviceAddOrReplace i2c_adds = 8; + repeated ws.display.Add display_adds = 9; +} +``` + +### Example: Multiple Components at Check-In + +```protobuf +component_adds: { + // Digital Output: Status LED + digitalio_adds: [ + { + pin_name: "D13", + gpio_direction: D_OUTPUT, + write: { pin_name: "D13", value: ... } + }, + // Digital Input: Button + { + pin_name: "D2", + gpio_direction: D_INPUT_PULL_UP, + sample_mode: SM_EVENT + } + ], + + // Analog Input: Battery Monitor + analogio_adds: [ + { + pin_name: "A1", + period: 30.0, + read_mode: SENSOR_TYPE_VOLTAGE + } + ], + + // I2C Sensor: Temperature/Humidity + i2c_adds: [ + { + device_description: {device_address: 0x77}, + device_name: "bme280", + device_period: 60.0, + device_sensor_types: [TEMPERATURE, HUMIDITY, PRESSURE] + } + ], + + // Display + display_adds: [ + { + type: DISPLAY_CLASS_TFT, + driver: "ST7789", + spi_tft: { spi: { ... }, pin_dc: "...", pin_rst: "..." }, + config_display: { width: 240, height: 135 } + } + ] +} +``` + +### Processing ComponentAdd Array + +The device processes each `ComponentAdd` in sequence: + +1. **Extract component type** from oneof +2. **Initialize hardware** for that component +3. **Configure parameters** from the Add message +4. **Store state** for persistent operation +5. **Continue to next** component + +If any component fails to initialize, the device should log the error but continue processing remaining components. + +--- + +## 4. Component Lifecycle with B2D/D2B Envelopes + +All component interactions in v2 use the envelope pattern. Here's how it works: + +```mermaid +graph LR + IO[Adafruit IO] -->|B2D Envelope| Device[Device] + Device -->|D2B Envelope| IO + + subgraph "B2D Commands" + B1[Add Component] + B2[Update Component] + B3[Remove Component] + B4[Write to Output] + end + + subgraph "D2B Responses" + D1[Component Added] + D2[Component Updated] + D3[Component Removed] + D4[Sensor Data Event] + end + + style IO fill:#e1f5ff + style Device fill:#fff4e6 +``` + +--- + +## 5. Adding Components During Runtime + +After initial check-in, components can be added dynamically during operation: + +### 5.1 Runtime Component Addition vs Check-In + +**Difference:** +- **At Check-In**: Components sent in `component_adds` array during check-in response +- **At Runtime**: Components sent as individual B2D messages + +### 5.2 Digital GPIO Input (Button) - Runtime Addition + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant GPIO as GPIO Controller + +IO->>Device: ws.digitalio.B2D {
add: {
pin_name: "D2",
gpio_direction: D_INPUT_PULL_UP,
sample_mode: SM_EVENT
}
} + +Device->>GPIO: Configure pin D2 as input with pull-up +GPIO->>GPIO: Attach interrupt for state changes +GPIO->>Device: Pin configured + +Note over Device: Button pressed/released +GPIO->>Device: State change detected + +Device->>IO: ws.digitalio.D2B {
event: {
pin_name: "D2",
value: false
}
} +``` + +### 5.3 Analog Input (Battery Monitor) - Runtime Addition + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant ADC as ADC Controller + +IO->>Device: ws.analogio.B2D {
add: {
pin_name: "A1",
period: 10.0,
read_mode: SENSOR_TYPE_VOLTAGE
}
} + +Device->>ADC: Configure A1 for voltage reading +ADC->>Device: Pin configured, start polling + +loop Every 10 seconds + ADC->>ADC: Read analog value + ADC->>Device: Voltage reading + Device->>IO: ws.analogio.D2B {
event: {
pin_name: "A1",
value: 3.7V
}
} +end +``` + +### 5.4 I2C Sensor (BME280 - Multi-Sensor) - Runtime Addition + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant I2C as I2C Controller +participant BME280 as BME280 Sensor + +Note over IO: Step 1: Scan I2C Bus +IO->>Device: ws.i2c.B2D {
bus_scan: {
scan_default_bus: true
}
} + +Device->>I2C: Scan for devices +I2C->>Device: Found: [0x77] + +Device->>IO: ws.i2c.D2B {
bus_scanned: {
bus_found_devices: [{device_address: 0x77}],
bus_status: BS_SUCCESS
}
} + +Note over IO: Step 2: Add Device +IO->>Device: ws.i2c.B2D {
device_add_replace: {
device_description: {device_address: 0x77},
device_name: "bme280",
device_period: 5.0,
device_sensor_types: [TEMPERATURE, HUMIDITY, PRESSURE]
}
} + +Device->>I2C: Initialize BME280 +I2C->>BME280: Configure sensors +BME280->>I2C: Ready + +Device->>IO: ws.i2c.D2B {
device_added_replaced: {
device_description: {device_address: 0x77},
bus_status: BS_SUCCESS,
device_status: DS_SUCCESS
}
} + +Note over Device: Step 3: Periodic Data +loop Every 5 seconds + I2C->>BME280: Read all sensors + BME280->>I2C: Temp: 23.5°C, Humidity: 45%, Pressure: 1013hPa + Device->>IO: ws.i2c.D2B {
device_event: {
device_description: {device_address: 0x77},
device_events: [
{type: TEMPERATURE, value: 23.5},
{type: HUMIDITY, value: 45.0},
{type: PRESSURE, value: 1013.25}
]
}
} +end +``` + +### 5.5 E-Paper Display (UC8179) - Runtime Addition + +```mermaid +sequenceDiagram +autonumber +participant IO as Adafruit IO +participant Device as WipperSnapper Device +participant Display as Display Controller +participant Driver as UC8179 Driver + +IO->>Device: ws.display.B2D {
name: "weather-display",
add: {
type: DISPLAY_CLASS_EPD,
driver: "UC8179",
panel: "5.83-648x480-mono",
spi_epd: { spi: {bus: 0, ...}, pin_dc: "D10", pin_busy: "D11" },
config_epd: {
mode: EPD_MODE_MONO,
properties: { width: 648, height: 480, text_size: 2 }
}
}
} + +Device->>Display: Initialize display +Display->>Driver: Load UC8179 driver +Driver->>Display: Driver ready + +Device->>IO: ws.display.D2B {
name: "weather-display",
did_succeed: true,
added_or_replaced: {}
} + +Note over IO: Write to Display +IO->>Device: ws.display.B2D {
name: "weather-display",
write: { message: "Temp: 23.5C\nHumidity: 45%" }
} + +Device->>Display: Render text +Display->>Driver: Update display +Driver->>Display: Wait for busy pin +Display->>Device: Update complete +``` + +--- + +## 6. Complete System Architecture + +Here's how all v2 components work together: + +```mermaid +graph TB + subgraph "Adafruit IO Broker" + IO[MQTT Broker] + UI[Web Dashboard] + end + + subgraph "Message Envelopes" + B2D[B2D Envelopes
Commands to Device] + D2B[D2B Envelopes
Responses from Device] + end + + subgraph "WipperSnapper Device" + Device[Device Core
Message Router] + + subgraph "Input Controllers" + DigitalIn[Digital Input] + AnalogIn[Analog Input] + I2CSensor[I2C Sensors] + end + + subgraph "Output Controllers" + DigitalOut[Digital Output] + Display[Display Controller] + I2COut[I2C Outputs] + end + end + + subgraph "Physical Hardware" + Button[Button on D2] + Battery[Battery on A1] + BME[BME280 @ 0x77] + EPD[E-Paper Display] + LED[LED on D13] + end + + UI -->|User Commands| IO + IO <-->|B2D/D2B| Device + + Device --> DigitalIn + Device --> AnalogIn + Device --> I2CSensor + Device --> DigitalOut + Device --> Display + Device --> I2COut + + DigitalIn <--> Button + AnalogIn <--> Battery + I2CSensor <--> BME + Display <--> EPD + DigitalOut <--> LED + + style IO fill:#e1f5ff + style Device fill:#fff4e6 + style UI fill:#e1f5ff +``` + +--- + +## 7. Message Envelope Structure + +### Example: Digital IO Messages + +**B2D (Commands to Device):** +```protobuf +message B2D { + oneof payload { + Add add = 1; // Add/configure pin + Remove remove = 2; // Remove pin + Write write = 3; // Write to output pin + } +} +``` + +**D2B (Responses from Device):** +```protobuf +message D2B { + oneof payload { + Event event = 1; // Pin value changed + } +} +``` + +### Message Flow Example + +``` +Adafruit IO Device + | | + | ws.digitalio.B2D | + | add: {pin_name: "D2", ...} | + | --------------------------------> | + | | Configure hardware + | | + | ws.digitalio.D2B | + | event: {pin_name: "D2", ...} | + | <-------------------------------- | + | | +``` + +--- + +## 8. Component Summary Table + +| Component | B2D Messages | D2B Messages | Direction | +|-----------|-------------|--------------|-----------| +| **digitalio** | add, remove, write | event | Input & Output | +| **analogio** | add, remove | event | Input only | +| **i2c** | bus_scan, device_add_replace, device_remove | bus_scanned, device_added_replaced, device_removed, device_event | Input & Output | +| **display** | Add, Remove, Write | AddedOrReplaced, Removed | Output only | +| **pwm** | add, remove, write | - | Output only | +| **servo** | add, remove, write | - | Output only | +| **pixels** | add, remove, write | - | Output only | +| **ds18x20** | add, remove | event | Input only | +| **uart** | add, remove | event | Input only | + +--- + +## 9. Data Flow Patterns + +### Input Components (Sensors send data) + +```mermaid +sequenceDiagram + participant Sensor + participant Device + participant IO as Adafruit IO + + loop Periodic or Event-driven + Sensor->>Device: Read sensor value + Device->>Device: Package in D2B envelope + Device->>IO: D2B{event: {...}} + end +``` + +### Output Components (Commands from IO) + +```mermaid +sequenceDiagram + participant IO as Adafruit IO + participant Device + participant Output + + IO->>Device: B2D{write: {...}} + Device->>Device: Extract from envelope + Device->>Output: Apply command + Output->>Device: Action complete +``` + +--- + +## 10. Error Handling + +### Status Codes + +Components return status codes in their responses: + +**I2C Example:** +- `BS_SUCCESS` - Bus operation successful +- `BS_ERROR_PULLUPS` - Missing pull-up resistors +- `DS_SUCCESS` - Device operation successful +- `DS_NOT_FOUND` - Device not found at address + +**Error Flow:** +```mermaid +sequenceDiagram + IO->>Device: B2D{device_add_replace: {...}} + Device->>Device: Attempt to initialize + alt Success + Device->>IO: D2B{device_added_replaced: {status: DS_SUCCESS}} + else Failure + Device->>IO: D2B{device_added_replaced: {status: DS_NOT_FOUND}} + IO->>IO: Display error to user + end +``` + +--- + +## 11. Component Lifecycle States + +```mermaid +stateDiagram-v2 + [*] --> NotConfigured + NotConfigured --> Adding: B2D(add) + Adding --> Active: D2B(added) success + Adding --> Error: D2B(added) failure + Active --> Operating: Normal operation + Operating --> Active: Continue + Active --> Updating: B2D(add) with new config + Updating --> Active: D2B(added) success + Active --> Removing: B2D(remove) + Removing --> [*]: D2B(removed) + Error --> [*]: User fixes issue +``` + +--- + +## 12. Best Practices for v2 + +### Message Routing +1. **Envelope pattern** - Always wrap messages in appropriate B2D/D2B envelope +2. **Type safety** - Use oneof payloads for compile-time type checking +3. **Component isolation** - Each component has its own envelope namespace + +### Component Management +1. **Add before use** - Always add component before trying to use it +2. **Check responses** - Verify D2B responses for success/failure +3. **Cleanup** - Remove components when no longer needed +4. **Status codes** - Check and handle all status codes appropriately + +### Performance +1. **Polling periods** - Choose appropriate periods for input components +2. **Event-driven** - Use event mode (SM_EVENT) for infrequent state changes +3. **Batch operations** - Group related component additions together + +--- + +## 13. Migration from v1 to v2 + +### Key Differences + +| Aspect | v1 | v2 | +|--------|----|----| +| **Message Organization** | Flat message structure | Envelope pattern (B2D/D2B) | +| **Component Namespacing** | Global proto namespace | Component-specific envelopes | +| **Display Support** | Limited | Extended (multiple interface types) | +| **I2C Flexibility** | Basic | Advanced (multiplexers, alt buses, GPS) | +| **Status Reporting** | Single status field | Separate Bus/Device status | + +### Migration Example + +**v1 I2C Add:** +```protobuf +I2CDeviceInitRequest { + i2c_device_address: 0x77, + i2c_device_name: "bme280", + ... +} +``` + +**v2 I2C Add:** +```protobuf +ws.i2c.B2D { + device_add_replace: { + device_description: {device_address: 0x77}, + device_name: "bme280", + ... + } +} +``` + +--- + +## 14. Complete Example: Weather Station with Check-In + +This example shows a weather station device booting up, receiving its configuration during check-in, and operating: + +```mermaid +sequenceDiagram + participant Device + participant IO as Adafruit IO + participant BME as BME280 + participant Display as E-Paper + participant LED + + Note over Device: BOOT + + Device->>IO: checkin.D2B{request} + IO->>Device: checkin.B2D{
response: {
component_adds: [BME280, E-Paper, LED]
}
} + + Device->>BME: Initialize + Device->>Display: Initialize + Device->>LED: Initialize + + Device->>IO: checkin.D2B{complete} + + Note over Device,LED: NORMAL OPERATION + + loop Every 60 seconds + BME->>Device: Read temp, humidity, pressure + Device->>IO: i2c.D2B{device_event: {...}} + IO->>Device: display.B2D{name, write: {message: "Temp: 23°C"}} + Device->>Display: Update display + IO->>Device: digitalio.B2D{write: LED blink} + Device->>LED: Blink to show update + end +``` + +--- + +## 15. Original Weather Station Example + +Here's a complete example showing multiple components working together: + +```mermaid +sequenceDiagram + participant IO as Adafruit IO + participant Device + participant BME as BME280 (I2C) + participant Display as E-Paper + participant LED as Status LED + + Note over IO,LED: System Initialization + IO->>Device: Add BME280 sensor + IO->>Device: Add E-Paper display + IO->>Device: Add Status LED + + Note over IO,LED: Normal Operation + loop Every 60 seconds + BME->>Device: Read temp, humidity, pressure + Device->>IO: D2B{device_event: {...}} + IO->>Device: display.B2D{name, write: {message: "Temp: 23°C"}} + Device->>Display: Update display + IO->>Device: B2D{digitalio.write: LED blink} + Device->>LED: Blink to show update + end +``` + +--- + +## Next Steps + +- For component-specific details, see individual `.md` files: + - [i2c.md](i2c.md) - I2C sensors and devices with v2 envelopes + - [display.md](display.md) - Display controllers with multiple interface types + - [spi.md](spi.md) - Shared SPI bus and device pin configuration + - [digitalio.md](digitalio.md) - Digital GPIO with B2D/D2B + - [analogio.md](analogio.md) - Analog input with B2D/D2B + - [pwm.md](pwm.md), [servo.md](servo.md), [pixels.md](pixels.md), etc. + +- For hardware definitions: + - [WipperSnapper_Boards](https://github.com/adafruit/Wippersnapper_Boards) + - [WipperSnapper_Components](https://github.com/adafruit/Wippersnapper_Components) + +--- + +## Summary + +The v2 WipperSnapper API brings: + + - ✅ **Cleaner architecture** with B2D/D2B envelopes + - ✅ **Better organization** with component-specific namespaces + - ✅ **Enhanced display support** for various interface types + - ✅ **Improved I2C flexibility** with multiplexers and alternate buses + - ✅ **Stronger typing** with protobuf oneof patterns + - ✅ **Easier extensibility** for future features + +The envelope pattern makes the codebase more maintainable while providing a clear, consistent interface for all WipperSnapper components. diff --git a/proto/wippersnapper/ds18x20.options b/proto/wippersnapper/ds18x20.options new file mode 100644 index 00000000..556d329c --- /dev/null +++ b/proto/wippersnapper/ds18x20.options @@ -0,0 +1,7 @@ +# ds18x20.options +ws.ds18x20.Add.onewire_pin max_size:5 +ws.ds18x20.Added.onewire_pin max_size:5 +ws.ds18x20.Remove.onewire_pin max_size:5 +ws.ds18x20.Event.onewire_pin max_size:5 +ws.ds18x20.Add.types max_count:2 +ws.ds18x20.Event.events max_count:2 diff --git a/proto/wippersnapper/ds18x20.proto b/proto/wippersnapper/ds18x20.proto new file mode 100644 index 00000000..040d4f27 --- /dev/null +++ b/proto/wippersnapper/ds18x20.proto @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2023 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +// WipperSnapper's Protobuf communication API for DS18X20 Maxim Temperature ICs +syntax = "proto3"; +package ws.ds18x20; +import "sensor.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + Event event = 2; + } +} + +/** +* Add represents a to initialize +* a DS18X20 Maxim temperature sensor, from the broker. +* NOTE: This API currently only supports ONE device per OneWire bus. +*/ +message Add { + string onewire_pin = 1; /** The desired pin to use as a OneWire bus. */ + int32 sensor_resolution = 2; /** The desired sensor resolution (9, 10, 11, or 12 bits). */ + float period = 3; /** The desired period to read the sensor, in seconds. */ + repeated ws.sensor.Type types = 4; /** SI types used by the DS18x20 sensor. */ +} + +/** +* Added represents a device's response +* to a Add message. +*/ +message Added { + bool is_initialized = 1; /** True if the 1-wire bus has been initialized successfully, False otherwise. */ + string onewire_pin = 2; /** The pin being used as a OneWire bus. */ +} + +/** +* Remove represents a to de-initialize a DS18X20 +* Maxim temperature sensor, from the broker. +*/ +message Remove { + string onewire_pin = 1; /** The desired onewire bus to de-initialize a DS18x sensor on and release. */ +} + +/** +* Event event represents data from **one** DS18X20 sensor. +*/ +message Event { + string onewire_pin = 1; /** The desired pin to use as a OneWire bus. */ + repeated ws.sensor.Event events = 2; /** The DS18X20's SensorEvent. */ +} diff --git a/proto/wippersnapper/ds18x20/v1/ds18x20.md b/proto/wippersnapper/ds18x20/v1/ds18x20.md deleted file mode 100644 index 7627f842..00000000 --- a/proto/wippersnapper/ds18x20/v1/ds18x20.md +++ /dev/null @@ -1,58 +0,0 @@ - -# ds18x20.proto - -This file details the API used by hardware running Adafruit WipperSnapper firmware for interfacing with a DS18B20 temperature sensor. - -**Note about design and implementation**: WipperSnapper defines a pin as a OneWire bus. Only one DS18x20 sensor is allowed per pin. To use multiple DS18x20 sensors, use multiple pins. - -## WipperSnapper Component Definitions - -The following JSON component definition type(s) reference `ds18x20.proto`: -* [ds18x20](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/ds18x20) - -## Sequence Diagrams - -### Create: DS18x20 - -Request from a broker to: -1) Initialize a OneWire bus on a desired pin -2) Initialize and configure a DS18x20 Maxim temperature sensor on the OneWire bus - -```mermaid - -sequenceDiagram - -autonumber - -IO-->>Device: Ds18x20InitRequest -Device->>IO: Ds18x20InitResponse -``` - -### Delete: DS18x20 - -Request from a broker to: -1) De-initialize a DS18x20 object's driver -2) Release the pin from being used as a OneWire bus - -```mermaid - -sequenceDiagram - -autonumber - -IO->>Device: Ds18x20DeInitRequest -``` - -### Sensor Event: DS18x20 - -Message containing data and metadata from a DS18x20 sensor. - -```mermaid - -sequenceDiagram - -autonumber - -Device->>IO: Ds18x20DeviceEvent -Note over Device,IO: This message contains i2c.SensorEvent,
which contains the sensor's value unit.
Since a device may have >1DS18X20
sensor, it also contains the sensor's pin
for addressing -``` diff --git a/proto/wippersnapper/ds18x20/v1/ds18x20.proto b/proto/wippersnapper/ds18x20/v1/ds18x20.proto deleted file mode 100644 index 76e65a59..00000000 --- a/proto/wippersnapper/ds18x20/v1/ds18x20.proto +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -// WipperSnapper's Protobuf communication API for DS18X20 Maxim Temperature ICs - -syntax = "proto3"; - -package wippersnapper.ds18x20.v1; -import "nanopb/nanopb.proto"; -import "wippersnapper/i2c/v1/i2c.proto"; - -/** -* Ds18x20InitRequest represents a request to initialize -* a DS18X20 Maxim temperature sensor, from the broker. -* NOTE: This API only supports ONE DS18X20 device PER OneWire bus. -*/ -message Ds18x20InitRequest { - string onewire_pin = 1 [(nanopb).max_size = 5]; /** The desired pin to use as a OneWire bus. */ - int32 sensor_resolution = 2; /** The desired sensor resolution (9, 10, 11, or 12 bits). */ - repeated wippersnapper.i2c.v1.I2CDeviceSensorProperties i2c_device_properties = 3[(nanopb).max_count = 2]; /** Properties for the DS18x20 sensor. */ -} - -/** -* ds18x20InitDs18x20InitResponseResponse represents a device's response -* to a ds18x20InitRequest message. -*/ -message Ds18x20InitResponse { - bool is_initialized = 1; /** True if the 1-wire bus has been initialized successfully, False otherwise. */ - string onewire_pin = 2 [(nanopb).max_size = 5]; /** The pin being used as a OneWire bus. */ -} - -/** -* Ds18x20DeInitRequest represents a request to de-initialize a DS18X20 -* Maxim temperature sensor, from the broker. -*/ -message Ds18x20DeInitRequest { - string onewire_pin = 1 [(nanopb).max_size = 5]; /** The desired onewire bus to de-initialize a DS18x sensor on and release. */ -} - -/** -* Ds18x20DeviceEvent event represents data from **one** DS18X20 sensor. -*/ -message Ds18x20DeviceEvent { - string onewire_pin = 1 [(nanopb).max_size = 5]; /** The desired pin to use as a OneWire bus. */ - repeated wippersnapper.i2c.v1.SensorEvent sensor_event = 2 [(nanopb).max_count = 2]; /** The DS18X20's SensorEvent. */ -} \ No newline at end of file diff --git a/proto/wippersnapper/error.options b/proto/wippersnapper/error.options new file mode 100644 index 00000000..8b73234b --- /dev/null +++ b/proto/wippersnapper/error.options @@ -0,0 +1,2 @@ +# error.options +ws.error.ErrorB2D submsg_callback:true diff --git a/proto/wippersnapper/error.proto b/proto/wippersnapper/error.proto new file mode 100644 index 00000000..d646b3cb --- /dev/null +++ b/proto/wippersnapper/error.proto @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2025 Brent Rubell, Loren Norman, Tyeth Gundry for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.error; +import "i2c.proto"; +import "uart.proto"; + +/* + * ComponentType + * Enum representing the class/type of component + * that an error message is associated with. + */ +enum ComponentType { + COMPONENT_TYPE_UNSPECIFIED = 0; /** Invalid Component Type. */ + COMPONENT_TYPE_DIGITALIO = 1; /** Digital IO Component. */ + COMPONENT_TYPE_ANALOGIO = 2; /** Analog IO Component. */ + COMPONENT_TYPE_PWM = 3; /** PWM Component. */ + COMPONENT_TYPE_SERVO = 4; /** Servo Component. */ + COMPONENT_TYPE_PIXELS = 5; /** Pixels Component. */ + COMPONENT_TYPE_DS18X20 = 6; /** DS18X20 Component. */ + COMPONENT_TYPE_UART = 7; /** UART Component. */ + COMPONENT_TYPE_I2C = 8; /** I2C Component. */ + COMPONENT_TYPE_GPS = 9; /** GPS Component. */ +} + +/* + * ErrorIOBan + * The ErrorIOBan message is sent from the broker to a device + * to inform the device that it has been banned from IO. + */ +message ErrorIOBan { +} + +/* + * ErrorIOThrottle + * The ErrorIOThrottle message is sent from the broker to a device + * to inform the device that it has been rate-limited by IO. + */ +message ErrorIOThrottle { + uint32 timeout = 1; +} + +/* + * ErrorB2D + * The ErrorB2D message is sent from the broker to a device + * to inform the device of an error that has occurred + * from IO. + */ +message ErrorB2D { + oneof payload { + ErrorIOBan io_ban = 1; + ErrorIOThrottle io_throttle = 2; + } +} + +/* + * ErrorD2B + * The ErrorD2B message is sent from a device to the broker + * to report an error that has occurred on a specific component. + * It contains a way to identify the specific component along + * with a human-readable error message. + */ +message ErrorD2B { + ComponentType type = 1; + oneof component_id { + string pin = 2; + ws.i2c.Descriptor i2c = 3; + ws.uart.Descriptor uart = 4; + } + string error = 5; + +} + + diff --git a/proto/wippersnapper/expander.proto b/proto/wippersnapper/expander.proto new file mode 100644 index 00000000..5d5bd4dc --- /dev/null +++ b/proto/wippersnapper/expander.proto @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2026 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.expander; +import "i2c.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + } +} + +/** + DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + Removed removed = 2; + } +} + +/** +* Add adds an expander to the hardware. +*/ +message Add { + i2c.Add cfg_i2c = 1; /** I2C configuration for the expander. */ +} + +/** +* Added represents a message from the hardware indicating an expander was added. +*/ +message Added { + i2c.Added response_i2c = 1; /** I2C configuration response. */ +} + +/** +* Remove removes an analog pin from the hardware. +*/ +message Remove { + i2c.Remove cfg_i2c = 1; /** I2C configuration for the expander to remove. */ +} + +/** +* Removed represents a message from the device indicating an expander was removed. +*/ +message Removed { + i2c.Removed response_i2c = 1; /** I2C configuration response. */ +} diff --git a/proto/wippersnapper/gps.options b/proto/wippersnapper/gps.options new file mode 100644 index 00000000..3550518e --- /dev/null +++ b/proto/wippersnapper/gps.options @@ -0,0 +1,21 @@ +# gps.options +ws.gps.Config.commands_pmtks max_size:90 +ws.gps.Config.commands_pmtks max_count:8 +ws.gps.Config.commands_ubxes max_size:128 +ws.gps.Config.commands_ubxes max_count:8 +ws.gps.RMCResponse.fix_status max_size:2 +ws.gps.RMCResponse.lat max_size:12 +ws.gps.RMCResponse.lat_dir max_size:6 +ws.gps.RMCResponse.lon max_size:12 +ws.gps.RMCResponse.lon_dir max_size:6 +ws.gps.RMCResponse.speed max_size:8 +ws.gps.RMCResponse.angle max_size:7 +ws.gps.GPGGAResponse.lat max_size:12 +ws.gps.GPGGAResponse.lat_dir max_size:6 +ws.gps.GPGGAResponse.lon max_size:12 +ws.gps.GPGGAResponse.lon_dir max_size:6 +ws.gps.GPGGAResponse.hdop max_size:6 +ws.gps.GPGGAResponse.altitude max_size:8 +ws.gps.GPGGAResponse.geoid_height max_size:10 +ws.gps.Event.rmc_responses max_count:10 +ws.gps.Event.gga_responses max_count:10 diff --git a/proto/wippersnapper/gps.proto b/proto/wippersnapper/gps.proto new file mode 100644 index 00000000..e3f2c331 --- /dev/null +++ b/proto/wippersnapper/gps.proto @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2026 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.gps; +import "i2c.proto"; +import "uart.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + Removed removed = 2; + Event event = 3; + } +} + +/** + * Add represents a message to add or replace a GPS device, + * including the transport and GPS-specific configuration options. + */ +message Add { + i2c.Add add_i2c = 1; + uart.Add add_uart = 2; + Config config = 3; +} + +/** + * Added represents a message indicating a GPS device was added or replaced. + */ +message Added { + i2c.Added added_i2c = 1; + uart.Added added_uart = 2; +} + +/** + * Remove represents a message to remove a GPS device, + * including the transport-specific options. + */ +message Remove { + i2c.Remove remove_i2c = 1; + uart.Remove remove_uart = 2; +} + +/** + * Removed represents a message indicating a GPS device was removed. + */ +message Removed { + i2c.Removed removed_i2c = 1; + uart.Removed removed_uart = 2; +} + +/** + * Config represents a message containing configuration data to set up and configure a GPS. + * Since GPS devices can output lots of data, this message allows users to select which data they want to receive + * and a resulting command string to initialize the GPS device with the selected options will be generated. + */ +message Config { + repeated string commands_pmtks = 1; /** List of PMTK commands in string format. **/ + repeated bytes commands_ubxes = 2; /** List of UBX commands in bytes format. **/ + int32 period = 3; /** Desired period to poll the GPS module, in milliseconds */ +} + +/** DateTime represents the date and time information from a GPRMC/GPGGA string **/ +message DateTime { + int32 hour = 1; /** Hour of the day (0-23) **/ + int32 minute = 2; /** Minute of the hour (0-59) **/ + int32 seconds = 3; /** Seconds of the minute (0-59) **/ + int32 milliseconds = 4; /** Milliseconds (0-999) **/ + int32 day = 5; /** Day of the month (1-31) **/ + int32 month = 6; /** Month of the year (1-12) **/ + int32 year = 7; /** Year (e.g., 2023) **/ +} + +/** RMCResponse represents the response from a GPS RMC (Recommended Minimum Specific GPS/Transit Data) message. **/ +message RMCResponse { + DateTime datetime = 1; /** Date and time of the GPS data **/ + string fix_status = 2; /** Fix status: 'A' for active, 'V' for void **/ + string lat = 3; /** Latitude in decimal degrees **/ + string lat_dir = 4; /** Latitude direction: 'North' or 'South' **/ + string lon = 5; /** Longitude in decimal degrees **/ + string lon_dir = 6; /** Longitude direction: 'East' or 'West' **/ + string speed = 7; /** Speed, in knots **/ + string angle = 8; /** Course/heading angle, in degrees **/ +} + +/** GPGGAResponse represents the response from a GPS GGA (Global Positioning System Fix Data) message. **/ +message GPGGAResponse { + DateTime datetime = 1; /** Date and time of the GPS data **/ + string lat = 2; /** Latitude in decimal degrees **/ + string lat_dir = 3; /** Latitude direction: 'N' or 'S' **/ + string lon = 4; /** Longitude in decimal degrees **/ + string lon_dir = 5; /** Longitude direction: 'E' or 'W' **/ + int32 fix_quality = 6; /** Quality of the GPS fix (0-3) **/ + int32 num_satellites = 7; /** Number of satellites in use **/ + string hdop = 8; /** HDOP- horizontal dilution of precision **/ + string altitude = 9; /** Altitude in meters above MSL **/ + string geoid_height = 10; /** Diff between geoid height and WGS84 height **/ +} + +/** Event represents a collection of GPS event responses, including RMC and GGA data. **/ +message Event { + repeated RMCResponse rmc_responses = 1; /** List of RMC responses **/ + repeated GPGGAResponse gga_responses = 2; /** List of GGA responses **/ +} diff --git a/proto/wippersnapper/i2c.options b/proto/wippersnapper/i2c.options new file mode 100644 index 00000000..290e4a82 --- /dev/null +++ b/proto/wippersnapper/i2c.options @@ -0,0 +1,5 @@ +# i2c.options +ws.i2c.Probed.results max_count:16 +ws.i2c.Add.name max_size:16 +ws.i2c.Add.types max_count:16 +ws.i2c.Event.events max_count:16 diff --git a/proto/wippersnapper/i2c.proto b/proto/wippersnapper/i2c.proto new file mode 100644 index 00000000..733a5326 --- /dev/null +++ b/proto/wippersnapper/i2c.proto @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2021-2024 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.i2c; +import "sensor.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Probe probe = 1; + Add add = 2; + Remove remove = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Probed probed = 1; + Added added = 2; + Removed removed = 3; + Event event = 4; + } +} + +/** +* BusStatus represents the status of a board's I2C bus +*/ +enum BusStatus { + BS_UNSPECIFIED = 0; /** Unspecified error occurred. **/ + BS_SUCCESS = 1; /** I2C bus successfully initialized. **/ + BS_ERROR_HANG = 2; /** I2C Bus hang, user should reset their board if this persists. **/ + BS_ERROR_PULLUPS = 3; /** I2C bus failed to initialize - SDA or SCL needs a pull up. **/ + BS_ERROR_WIRING = 4; /** I2C bus failed to communicate - Please check your wiring. **/ + BS_ERROR_INVALID_CHANNEL = 5; /** I2C MUX failed - Output channel must be within range 0-7. **/ +} + +/** +* Status represents the state of an I2C device/peripheral +*/ +enum Status { + S_UNSPECIFIED = 0; /** Unspecified error occurred. **/ + S_SUCCESS = 1; /** I2C device successfully initialized. **/ + S_FAIL_INIT = 2; /** I2C device failed to initialize. **/ + S_FAIL_DEINIT = 3; /** I2C device failed to deinitialize. **/ + S_FAIL_UNSUPPORTED_SENSOR = 4; /** WipperSnapper version is outdated and does not include this device. **/ + S_NOT_FOUND = 5; /** I2C device not found on the bus. **/ +} +/** + * Represents a separate I2C address space, either a Bus or a Mux. + * - when describing a bus, specify only the scl and sda pins + * - when describing a single mux channel, specify all fields + * - when describing an entire mux (all channels), leave mux_channel blank + */ +message AddressSpace { + uint32 pin_scl = 1; /** Pin name for the I2C SCL line **/ + uint32 pin_sda = 2; /** Pin name for the I2C SDA line **/ + uint32 mux_address = 3; /** Optional - I2C multiplexer address. **/ + uint32 mux_channel = 4; /** Optional - I2C multiplexer channel, or blank for "all channels" **/ +} + +/** + * Request an I2C probe of one or more address spaces, optionally narrowing the + * scope of the probe to particular addresses. + * - a v1-style Scan would contain a single AddressSpace and no Address narrowing (scan all 112) + * - a v2-style Probe would contain every AddressSpace and a ist of specific Addresses to probe for + */ +message Probe { + repeated AddressSpace address_spaces = 1; /** One or more AddressSpaces, with no mux_channels **/ + repeated uint32 addresses = 2; /** Optional - Specific addresses to probe on each AddressSpace. Empty means "all address". **/ +} + +/** + * The results of scanning a particular AddressSpace. + * - success with found addresses populates the found_addresses list + * - an error populates the relevant bus_status and contains no found_addresses + */ +message AddressSpaceResult { + AddressSpace address_space = 1; /** Which AddressSpace was probed **/ + BusStatus bus_status = 2; /** BS_SUCCESS on success, anything else denotes an error on this AddressSpace **/ + repeated uint32 found_addresses = 3; /** List of I2C addresses where a device was detected, empty in case of error **/ +} + +/** + * Response from a Probe request, with results for every AddressSpace that either: + * - found 1 or more addresses + * - had an error + * (Successful probes that did not find anything do not need to be returned.) + */ +message Probed { + repeated AddressSpaceResult results = 1; /** A success or error result for each AddressSpace probed **/ +} + +/** +* Descriptor represents a specific device's I2C address within an address space. +*/ +message Descriptor { + AddressSpace address_space = 1; /** Which address space this device lives in, with mux_channel required. **/ + uint32 address = 2; /** Device's i2c address, either on the bus or the mux **/ +} + +/** +* Add is a message for initializing (or replacing/updating) an i2c device. +*/ +message Add { + Descriptor descriptor = 1; /** The I2c device's address and metadata. */ + /** The I2c device's name, MUST MATCH the name on the JSON + * definition file on the Wippersnapper_Components repo. */ + string name = 2; + float period = 3; /** The desired period to update the I2c device's sensor(s), in seconds. */ + map types = 4; /** SI Types for each sensor on the I2c device. */ +} + +/** +* Added contains the response from a device after processing a Add message. +*/ +message Added { + Descriptor descriptor = 1; /** The I2c device's address and metadata, unless there was an error. **/ + BusStatus bus_status = 2; /** The I2c bus' status, in case of bus error. **/ + Status status = 3; /** The I2c device's status, in case of device error. **/ +} + +/** +* Remove represents a request to de-init an i2c device. +*/ +message Remove { + Descriptor descriptor = 1; /** The I2c device's address and metadata. */ +} + +/** +* Removed represents a response to a I2cRemove message. +*/ +message Removed { + Descriptor descriptor = 1; /** The I2c device's address and metadata. */ + bool did_remove = 2; /** True if the I2C device was successfully removed from the controller, False otherwise. **/ +} + +/** +* Each Event represents data from **one** I2C device, containing one or more sensors. +*/ +message Event { + Descriptor descriptor = 1; /** The I2c device's address and metadata. */ + map events = 2; /** An event from each of the device's configured sensors. */ +} + diff --git a/proto/wippersnapper/i2c/v1/i2c.md b/proto/wippersnapper/i2c/v1/i2c.md deleted file mode 100644 index 8b11e123..00000000 --- a/proto/wippersnapper/i2c/v1/i2c.md +++ /dev/null @@ -1,95 +0,0 @@ - -# i2c.proto - -This file details the API used by hardware running Adafruit WipperSnapper firmware for interfacing with the I2C bus and I2C sensors. - -## WipperSnapper Component Definitions - -The following JSON component definition type(s) reference `i2c.proto`: -* [i2c](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/i2c) - -## Sequence Diagrams - -### I2C Scan - -On Adafruit.io, an I2C scan can be initialized one of two ways: -1) User clicks "I2C Scan" button -2) Users clicks an I2C component from the Component Picker - -**Note:** The I2C scan always contains a I2CBusInitRequest message in case the bus was not previously initialized. - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: I2CBusScanRequest
(contains I2CBusInitRequest) -Device->>App: I2CBusInitRequest -App->>Device: BusResponse -Device->>App: i2c_port_number -App->>I2C Class: Perform scan on
i2c_port_number -I2C Class->>App: addresses_found -App->>Device: I2CBusScanResponse -Device->>IO: I2CBusScanResponse -``` - -### Create a new I2C device - -**Note:** I2C devices may contain _multiple_ sensors (i.e: one device can contain a temperature and humidity sensor). To work with multiple sensors, I2C commands typically contain a `I2CDeviceSensorProperties` sub-message, detailing the properties of the I2C device's sensor. - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: I2CDeviceInitRequest
(contains I2CBusInitRequest) -Device->>App: I2CBusInitRequest -App->>Device: BusResponse -Device->>App: I2CDeviceInitRequest -App->>I2C Class: i2c_device_address, i2c_device_name,
i2c_device_properties -Note over App,I2C Class: At this point, the I2C sensor is configured
and ready to send data to Adafruit IO. -I2C Class->>App: I2CDeviceInitResponse -App->>Device: I2CDeviceInitResponse -Device->>IO: I2CDeviceInitResponse -``` - -### Update an existing I2C device - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: I2CDeviceUpdateRequest -Device->>App: I2CDeviceUpdateRequest -App->>I2C Class: I2CDeviceUpdateRequest -Note over App,I2C Class: Update the properties of the "sub-sensors"
specified within i2c_device_properties array. -I2C Class->>App: I2CDeviceUpdateResponse -App->>Device: I2CDeviceUpdateResponse -Device->>IO: I2CDeviceUpdateResponse -``` - -### Sending data from an I2C component - -The process of sending data from an I2C component involves a device sending a `I2CDeviceEvent` message to the broker. Since an i2c component may have more than one sub-component (i.e: a component may contain both a temperature sensor and a humidity sensor), the `sensor_event` is a repeated submessage array which contains the value and corresponding SI unit for all sub-sensors. - -While the sequence diagram for this type of message looks simple, the process involves work on the MQTT broker to unpack and parse each `sensor_event` message: - -```mermaid -sequenceDiagram -autonumber - -Device->>IO: I2CDeviceEvent -``` - - -### Delete an I2C device - -The process of deleting an I2C device is straightforward and only requires the device's unique I2C address: - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: I2CDeviceDeinitRequest -Device->>IO: I2CDeviceDeinitResponse -``` - - diff --git a/proto/wippersnapper/i2c/v1/i2c.proto b/proto/wippersnapper/i2c/v1/i2c.proto deleted file mode 100644 index eb74fa4d..00000000 --- a/proto/wippersnapper/i2c/v1/i2c.proto +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-FileCopyrightText: 2021-2025 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.i2c.v1; -import "nanopb/nanopb.proto"; - -/** -* BusResponse represents the state of the I2C bus, from a device. -*/ -enum BusResponse { - BUS_RESPONSE_UNSPECIFIED = 0; /** Unspecified error occurred. **/ - BUS_RESPONSE_SUCCESS = 1; /** I2C bus successfully initialized. **/ - BUS_RESPONSE_ERROR_HANG = 2; /** I2C Bus hang, user should reset their board if this persists. **/ - BUS_RESPONSE_ERROR_PULLUPS = 3; /** I2C bus failed to initialize - SDA or SCL needs a pull up. **/ - BUS_RESPONSE_ERROR_WIRING = 4; /** I2C bus failed to communicate - Please check your wiring. **/ - BUS_RESPONSE_UNSUPPORTED_SENSOR = 5; /** WipperSnapper firmware is outdated and does not include the sensor type - Please update your WipperSnapper firmware. **/ - BUS_RESPONSE_DEVICE_INIT_FAIL = 6; /** I2C device failed to initialize. **/ - BUS_RESPONSE_DEVICE_DEINIT_FAIL = 7; /** I2C device failed to de-initialize. **/ -} - -/** -* I2CBusInitRequest represents a request to -* initialize the I2C bus from the broker. -*/ -message I2CBusInitRequest { - int32 i2c_pin_scl = 1; /** The desired I2C SCL pin. */ - int32 i2c_pin_sda = 2; /** The desired I2C SDA pin. */ - uint32 i2c_frequency = 3; /** The desired I2C SCL frequency, in Hz. Default is 100000Hz. */ - int32 i2c_port_number = 4; /** The I2C port number. */ -} - -/** -* I2CBusInitResponse represents a response to I2CBusInitRequest -*/ -message I2CBusInitResponse { - bool is_initialized = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; /** True if the I2C port has been initialized successfully, False otherwise. */ - BusResponse bus_response = 2; /** Whether the I2C bus initialized properly or failed. **/ -} - -/** -* I2CBusSetFrequency represents a request to change the -* I2C clock speed to a desired frequency, in Hz. -*/ -message I2CBusSetFrequency { - uint32 frequency = 1; /** The desired I2C SCL frequency, in Hz. */ - int32 bus_id = 2; /** An optional I2C bus identifier, if multiple exist. */ -} - -/** -* I2CBusScanRequest represents the parameters required to execute -* a device's I2C scan. -*/ -message I2CBusScanRequest { - int32 i2c_port_number = 1; /** The desired I2C port to scan. */ - I2CBusInitRequest bus_init_request = 2; /** The I2C bus initialization request. */ -} - -/** -* I2CBusScanResponse represents a list of I2C addresses -* found on the bus after I2CBusScanRequest has executed. -*/ -message I2CBusScanResponse { - repeated uint32 addresses_found = 1 [packed=true, (nanopb).max_count = 120]; /** The 7-bit addresses of the I2C devices found on the bus, empty if not found. */ - BusResponse bus_response = 2; /** The I2C bus' status. **/ -} - -/** -* I2CDeviceSensorProperties contains -* the properties of an I2C device's sensor such as -* its type and period. -*/ -message I2CDeviceSensorProperties { - SensorType sensor_type = 1; - uint32 sensor_period = 2; -} - - -/** -* Represents a list of I2CDeviceInitRequest messages. -*/ -message I2CDeviceInitRequests { - repeated I2CDeviceInitRequest list = 1; -} - -/** -* I2CDeviceInitRequest is a wrapper message for -* an I2C device initialization request. -*/ -message I2CDeviceInitRequest { - int32 i2c_port_number = 1; /** The desired I2C port to initialize an I2C device on. */ - I2CBusInitRequest i2c_bus_init_req = 2; /** An I2C bus initialization request. */ - uint32 i2c_device_address = 3; /** The 7-bit I2C address of the device on the bus. */ - string i2c_device_name = 4[(nanopb).max_size = 256]; /** The I2C device's name, MUST MATCH the name on the JSON definition file on https://github.com/adafruit/Wippersnapper_Components. */ - repeated I2CDeviceSensorProperties i2c_device_properties = 5[(nanopb).max_count = 15]; /** Properties of each sensor on the I2C device. */ - bool is_output_device = 6; /** True if the I2C device is an I2C output device, False otherwise (default). */ - I2COutputAdd i2c_output_add = 7; /** The configuration for an I2C output device. */ -} - -/** -* I2CDeviceInitResponse contains the response from a -* device after processing a I2CDeviceInitRequest message. -*/ -message I2CDeviceInitResponse { - bool is_success = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; /** !!DEPRECATED!! True if i2c device initialized successfully, false otherwise. */ - uint32 i2c_device_address = 2; /** The 7-bit I2C address of the device on the bus. */ - BusResponse bus_response = 3; /** The I2C bus' status. **/ -} - -/** -* I2CDeviceUpdateRequest is a wrapper message which -* contains a message to update a specific device's properties. -*/ -message I2CDeviceUpdateRequest { - int32 i2c_port_number = 1; /** The desired I2C port. */ - uint32 i2c_device_address = 2; /** The 7-bit I2C address of the device on the bus. */ - string i2c_device_name = 3[(nanopb).max_size = 256]; /** The I2C device's name, MUST MATCH the name on the JSON file. */ - repeated I2CDeviceSensorProperties i2c_device_properties = 4[(nanopb).max_count = 15]; /** Properties for the I2C device's sensors. */ -} - -/** -* I2CDeviceUpdateResponse represents if an I2C device's -* sensor(s) is/are successfully updated. -*/ -message I2CDeviceUpdateResponse { - uint32 i2c_device_address = 1; /** The 7-bit I2C address of the device which was updated. */ - bool is_success = 2 [deprecated = true, (nanopb).type = FT_IGNORE]; /** !!DEPRECATED!! True if the update request succeeded, False otherwise. */ - BusResponse bus_response = 3; /** The I2C bus' status. **/ -} - -/** -* I2CDeviceDeinitRequest is a wrapper message containing -* a deinitialization request for a specific i2c device. -*/ -message I2CDeviceDeinitRequest { - int32 i2c_port_number = 1; /** The desired I2C port to de-initialize an I2C device on. */ - uint32 i2c_device_address = 2; /** The 7-bit I2C address of the device on the bus. */ -} - -/** -* I2CDeviceDeinitResponse represents if an I2C device's -* sensor(s) is/are successfully de-initialized. -*/ -message I2CDeviceDeinitResponse { - bool is_success = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; /** True if the deinitialization request succeeded, False otherwise. */ - uint32 i2c_device_address = 2; /** The 7-bit I2C address of the device which was initialized. */ - BusResponse bus_response = 3; /** The I2C bus' status. **/ -} - -/** Adafruit Unified Sensor Library Messages. */ - -/** -* SensorType allows us determine what types of units the sensor uses, etc. -*/ -enum SensorType { - SENSOR_TYPE_UNSPECIFIED = 0; /** Sensor value type which is not defined by this list, "Raw Value: {value}". */ - SENSOR_TYPE_ACCELEROMETER = 1; /** Acceleration, in meter per second per second, "{value}m/s/s". */ - SENSOR_TYPE_MAGNETIC_FIELD = 2; /** Magnetic field strength, in micro-Tesla, "{value}µT". */ - SENSOR_TYPE_ORIENTATION = 3; /** Orientation angle, in degrees, "{value}°". */ - SENSOR_TYPE_GYROSCOPE = 4; /** Angular rate, in radians per second, "{value}rad/s". */ - SENSOR_TYPE_LIGHT = 5; /** Light-level, non-unit-specific (For a unit-specific measurement, see: Lux), , "Raw Value: {value}". */ - SENSOR_TYPE_PRESSURE = 6; /** Pressure, in hectopascal, , "{value}hPa". */ - SENSOR_TYPE_PROXIMITY = 8; /** Distance from an object to a sensor, non-unit-specific, "Raw Value: {value}". */ - SENSOR_TYPE_GRAVITY = 9; /** Metres per second squared, "{value}m/s^2". */ - SENSOR_TYPE_LINEAR_ACCELERATION = 10; /** Acceleration not including gravity, in meter per second squared, "{value}m/s^2". */ - SENSOR_TYPE_ROTATION_VECTOR = 11; /** An angle in radians, "{value} rad".*/ - SENSOR_TYPE_RELATIVE_HUMIDITY = 12; /** in percent (%), "{value}%". */ - SENSOR_TYPE_AMBIENT_TEMPERATURE = 13; /** Temperature of the air around a sensor, in degrees Celsius, "{value}°C". */ - SENSOR_TYPE_OBJECT_TEMPERATURE = 14; /** Temperature of the object a sensor is touching/pointed at, in degrees Celsius, "{value}°C".*/ - SENSOR_TYPE_VOLTAGE = 15; /** Volts, "{value}V". */ - SENSOR_TYPE_CURRENT = 16; /** Milliamps, "{value}mA". */ - SENSOR_TYPE_COLOR = 17; /** Values are in 0..1.0 RGB channel luminosity and 32-bit RGBA format. "Color: {value}".*/ - SENSOR_TYPE_RAW = 18; /** Sensor reads a value which is not defined by this list, "Raw Value: {value}".*/ - SENSOR_TYPE_PM10_STD = 19; /** Standard Particulate Matter 1.0, in ppm, "{value}ppm". */ - SENSOR_TYPE_PM25_STD = 20; /** Standard Particulate Matter 2.5, in ppm, "{value}ppm". */ - SENSOR_TYPE_PM100_STD = 21; /** Standard Particulate Matter 100, in ppm, "{value}ppm". */ - SENSOR_TYPE_PM10_ENV = 22; /** Environmental Particulate Matter 1.0, in ppm, "{value}ppm". */ - SENSOR_TYPE_PM25_ENV = 23; /** Environmental Particulate Matter 2.5, in ppm, "{value}ppm". */ - SENSOR_TYPE_PM100_ENV = 24; /** Environmental Particulate Matter 100, in ppm, "{value}ppm".*/ - SENSOR_TYPE_CO2 = 25; /** Measured CO2, in ppm, "{value}ppm". */ - SENSOR_TYPE_GAS_RESISTANCE = 26; /** Proportional to the amount of VOC particles in the air, in Ohms, "{value}Ω". */ - SENSOR_TYPE_ALTITUDE = 27; /** Values are in meters (m), "${$v} m". */ - SENSOR_TYPE_LUX = 28; /** Light level, in lux, "Lux: {value}". */ - SENSOR_TYPE_ECO2 = 29; /** equivalent/estimated CO2 in ppm (estimated from some other measurement), "{value}ppm". */ - SENSOR_TYPE_UNITLESS_PERCENT = 30; /** Percentage, unit-less, "{value}%". */ - SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT = 31; /** Temperature of the air around a sensor, in degrees Fahrenheit, "{value}°F". */ - SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT = 32; /** Temperature of the object a sensor is touching/pointed at, in degrees Fahrenheit, "{value}°F".*/ - SENSOR_TYPE_VOC_INDEX = 33; /** Values are an index from 1-500 with 100 being normal, "${$v} VOC".*/ - SENSOR_TYPE_NOX_INDEX = 34; /** Values are an index from 1-500 with 100 being normal, "${$v} NOx".*/ - SENSOR_TYPE_TVOC = 35; /** Values are in parts per billion (ppb), "${$v} ppb". */ -} - -/** -* SensorEvent is used to return the sensor's value and type. -*/ -message SensorEvent { - SensorType type = 1; /** The sensor's type and corresponding SI unit */ - float value = 2; /** The sensor's value */ -} - -/** -* Each I2CDeviceEvent represents data from **one** I2C sensor. -* NOTE: An I2CDeviceEvent can have multiple sensor events if -* the I2C device contains > 1 sensor. -*/ -message I2CDeviceEvent { - uint32 sensor_address = 1; /** The 7-bit I2C address of the I2C device. */ - repeated SensorEvent sensor_event = 2[(nanopb).max_count = 15]; /** A, optionally repeated, SensorEvent from a sensor. */ -} - -/** -* I2CDeviceOutputWrite represents a request to write to an I2C output device. -* NOTE: This message is similar to the I2CDeviceOutputWrite message on -* the api-v2 branch but NOT identical. -*/ -message I2CDeviceOutputWrite { - uint32 i2c_device_address = 1; /** The 7-bit I2C address of the device on the bus. */ - string i2c_device_name = 2[(nanopb).max_size = 256]; /** The I2C device's name, MUST MATCH the name on the JSON definition file on https://github.com/adafruit/Wippersnapper_Components. */ - oneof output_msg { - LEDBackpackWrite write_led_backpack = 3; /** Optional - If the I2C device is a LED backpack, fill this field. **/ - CharLCDWrite write_char_lcd = 4; /** Optional - If the I2C device is a character LCD, fill this field. **/ - SSD1306Write write_ssd1306 = 5; /** Optional - If the I2C device is a SSD1306 OLED display, fill this field. **/ - } -} - -///*** I2C Output Device Messages (from i2c_output.proto in api v2) ***/// - -/** -* LEDBackpackAlignment represents all text alignment -* options for LED backpack displays -*/ -enum LEDBackpackAlignment { - LED_BACKPACK_ALIGNMENT_UNSPECIFIED = 0; /** Unspecified alignment option. **/ - LED_BACKPACK_ALIGNMENT_LEFT = 1; /** (Default) Left-aligned. **/ - LED_BACKPACK_ALIGNMENT_RIGHT = 2; /** Right-aligned. **/ -} - -/** -* Desired SSD1306 text 'magnification' size. -*/ -enum SSD1306TextSize { - SSD1306_TEXT_SIZE_UNSPECIFIED = 0; /** Unspecified text size. **/ - SSD1306_TEXT_SIZE_1 = 1; /** Default text size, 6x8px. **/ - SSD1306_TEXT_SIZE_2 = 2; /** Larger text size option, 12x16px. **/ -} - -/** -* LEDBackpackConfig represents the configuration for a LED backpack display. -*/ -message LEDBackpackConfig { - int32 brightness = 1; /** Desired brightness of the LED backpack, from 0 (off) to 15 (full brightness). **/ - LEDBackpackAlignment alignment = 2; /** Desired text alignment for the LED backpack. **/ -} - -/** -* CharLCDConfig represents the configuration for a character LCD display. -*/ -message CharLCDConfig { - uint32 rows = 1; /** Number of rows for the character LCD. **/ - uint32 columns = 2; /** Number of columns for the character LCD. **/ -} - -/** -* SSD1306Config represents the configuration for a SSD1306 OLED display. -*/ -message SSD1306Config { - uint32 width = 1; /** Width of the display. **/ - uint32 height = 2; /** Height of the display. **/ - SSD1306TextSize text_size = 3; /** Desired text 'magnification' size. **/ -} - -/** -* I2COutputAdd represents a request from the broker to add an I2C output device to a device. -*/ -message I2COutputAdd { - oneof config { - LEDBackpackConfig led_backpack_config = 1; /** Configuration for LED backpack. **/ - CharLCDConfig char_lcd_config = 2; /** Configuration for character LCD. **/ - SSD1306Config ssd1306_config = 3; /** Configuration for SSD1306 OLED display. **/ - } -} - -/** -* LEDBackpackWrite represents a request from the broker to write a message to a LED backpack. -*/ -message LEDBackpackWrite { - string message = 1 [(nanopb).max_size = 128]; /** Message to write to the LED backpack. **/ -} - -/** -* CharLCDWrite represents a request from the broker to write to a character LCD. -*/ -message CharLCDWrite { - string message = 1 [(nanopb).max_size = 128]; /** Message to write to the character LCD. **/ - bool enable_backlight = 2; /** Optional field to enable/disable the backlight. Should be its own feed (0 is off, 1 is on).**/ -} - -/** -* SSD1306Write represents a request from the broker to -* write to a SSD1306 OLED display. -*/ -message SSD1306Write { - string message = 1 [(nanopb).max_size = 256]; /** Message to write to a SSD1306 OLED display. **/ -} \ No newline at end of file diff --git a/proto/wippersnapper/pin/v1/pin.proto b/proto/wippersnapper/pin/v1/pin.proto deleted file mode 100644 index 745d1522..00000000 --- a/proto/wippersnapper/pin/v1/pin.proto +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.pin.v1; -import "nanopb/nanopb.proto"; - -/** -* Mode. Specifies if a GPIO pin is an analog or digital pin. -*/ -enum Mode { - MODE_UNSPECIFIED = 0; /** Invalid Mode from Broker. */ - MODE_ANALOG = 1; /** Set up an analog pin. */ - MODE_DIGITAL = 2; /** Set up a digital pin. */ -} - -/** -* Represents a list of ConfigurePinRequest messages. -*/ -message ConfigurePinRequests { - repeated ConfigurePinRequest list = 1; -} - -/** - * Represents a request from the broker to create, update, or delete a GPIO pin. - */ -message ConfigurePinRequest { - string pin_name = 1 [(nanopb).max_size = 5]; /** The name of pin we are accessing. */ - Mode mode = 2; /** Specifies the pin's type, analog or input. */ - Direction direction = 3; /** Specifies the pin's behavior. */ - Pull pull = 4; /** Specifies an optional pullup resistor value. */ - float period = 5; /** Specifies the time between measurements, in seconds. */ - RequestType request_type = 6; /** Specifies the type of ConfigurePinRequest. */ - float aref = 7 [deprecated=true]; /** deprecated: Specifies the reference voltage used for analog input, defaults to 3.3v. */ - AnalogReadMode analog_read_mode = 8; /** ANALOG-ONLY: Specifies the read mode for an analog pin. */ - - /** - * Direction. Specifies the pin's direction, INPUT or OUTPUT. - */ - enum Direction { - DIRECTION_UNSPECIFIED = 0; /** Invalid Direction from Broker. */ - DIRECTION_INPUT = 1; /** Set the pin to behave as an input. */ - DIRECTION_OUTPUT = 2; /** Set the pin to behave as an output. */ - } - - /** - * Pull. An optional pullup resistor value - */ - enum Pull { - PULL_UNSPECIFIED = 0; /** Invalid Direction from Broker. */ - PULL_UP = 1; /** Set the pin to pull high. */ - PULL_DOWN = 2; /** Set the pin to pull low. */ - } - - /** - * Request Type. Describes the type of ConfigurePinRequest for the hardware to operate on. - */ - enum RequestType { - REQUEST_TYPE_UNSPECIFIED = 0; /** Invalid request from Broker. */ - REQUEST_TYPE_CREATE = 1; /** The request creates a pin. */ - REQUEST_TYPE_UPDATE = 2; /** The request updates a previously created pin. */ - REQUEST_TYPE_DELETE = 3; /** The request deletes a previously created pin. */ - } - - /** - * Selects the type of value read by an analog pin. - * PIN_VALUE: Raw ADC reading. - * PIN_VOLTAGE: Calculated voltage reading. - * NOTE: This is only applicable to analog pins. - */ - enum AnalogReadMode { - ANALOG_READ_MODE_UNSPECIFIED = 0; - ANALOG_READ_MODE_PIN_VALUE = 1; - ANALOG_READ_MODE_PIN_VOLTAGE = 2; - } - -} - - -/** -* Pin Event. Describes a pin's value. -*/ -message PinEvent { - string pin_name = 1 [(nanopb).max_size = 5]; /** Specifies the pin's name. */ - string pin_value = 2 [(nanopb).max_size = 12]; /** Specifies the pin's value. */ - - Mode mode = 3 [deprecated = true, (nanopb).type = FT_IGNORE]; /** DEPRECATED: Specifies the pin's mode, analog or digital. */ - float pin_value_volts = 4 [deprecated = true, (nanopb).type = FT_IGNORE]; /** DEPRECATED: Specifies an anlog pin's voltage. */ -} - -/** -* ConfigureReferenceVoltage - Changes the reference voltage used for analog inputs. -* Direction: C2D -*/ -message ConfigureReferenceVoltage { - float reference_voltage = 1; /** Specifies an ADC reference voltage. */ -} - -/** -* Sends a list of PinEvents -* NOTE: Not working with nanopb decode repeated -*/ -message PinEvents { - option deprecated = true; - repeated PinEvent list = 1; -} - -/* DEPRECATED - PWM Pin API */ - -// Configures a PWM output pin -message ConfigurePWMPinRequest { - option deprecated = true; - // Pin to write to - string pin_name = 1 [(nanopb).max_size = 5, deprecated = true, (nanopb).type = FT_IGNORE]; - - // Duty cycle between always off (0) - // and always on (255) - int32 duty_cycle = 2 [deprecated = true, (nanopb).type = FT_IGNORE]; - - // Target frequency, in Hz - int32 frequency = 3 [deprecated = true, (nanopb).type = FT_IGNORE]; - - // If the frequency changes over time - // NOTE: CIRCUITPYTHON-API ONLY - bool variable_frequency = 4 [deprecated = true, (nanopb).type = FT_IGNORE]; -} - -message ConfigurePWMPinRequests { - option deprecated = true; - repeated ConfigurePWMPinRequest list = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; -} - -// Write duty cycle to a pin PWM output pin -message PWMPinEvent { - option deprecated = true; - // Duty cycle between always off (0) - // and always on (255) - int32 duty_cycle = 2 [deprecated = true, (nanopb).type = FT_IGNORE]; -} - -message PWMPinEvents { - option deprecated = true; - repeated PWMPinEvent list = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; -} \ No newline at end of file diff --git a/proto/wippersnapper/pixels.options b/proto/wippersnapper/pixels.options new file mode 100644 index 00000000..bcb977e7 --- /dev/null +++ b/proto/wippersnapper/pixels.options @@ -0,0 +1,6 @@ +# pixels.options +ws.pixels.Add.pin_data max_size: 6 +ws.pixels.Add.pin_dotstar_clock max_size: 6 +ws.pixels.Added.pin_data max_size: 6 +ws.pixels.Remove.pin_data max_size: 6 +ws.pixels.Write.pin_data max_size: 6 diff --git a/proto/wippersnapper/pixels.proto b/proto/wippersnapper/pixels.proto new file mode 100644 index 00000000..e479b4bb --- /dev/null +++ b/proto/wippersnapper/pixels.proto @@ -0,0 +1,99 @@ +// SPDX-FileCopyrightText: 2022-2023 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +// Addressable Pixels API for Adafruit WipperSnapper +syntax = "proto3"; +package ws.pixels; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + Removed removed = 2; + } +} + +/** +* Type defines the type/model of pixel strand. +*/ +enum Type { + T_UNSPECIFIED = 0; /** Unspecified pixel type, error. */ + T_NEOPIXEL = 1; /** NeoPixel pixel strand. */ + T_DOTSTAR = 2; /** DotStar pixel strand. */ +} + +/** +* Order defines the color ordering. +*/ +enum Order { + O_UNSPECIFIED = 0; /** Unspecified color ordering, error. */ + O_GRB = 1; /** DEFAULT for NeoPixels - Green, Red, Blue */ + O_GRBW = 2; /** Green, Red, Blue, White */ + O_RGB = 3; /** Red, Green, Blue */ + O_RGBW = 4; /** Red, Green, Blue, White */ + O_BRG = 5; /** DEFAULT for DotStars - Blue, Red, Green */ + O_RBG = 6; /** Red, Blue Green */ + O_GBR = 7; /** Green, Blue, Red */ + O_BGR = 8; /** Blue, Green, Red */ +} + +/** +* Add represents a call from IO to a device. +* Adds a strand of addressable pixels. +* Initial brightness is always 128. +*/ +message Add { + Type type = 1; /** Defines the model/type of pixel strand */ + uint32 num = 2; /** Number of pixels attached to strand. */ + Order ordering = 3; /** Defines the pixel strand's color ordering. */ + uint32 brightness = 4; /** Strand brightness, 0 to 255 */ + string pin_data = 5; /** Data pin a NeoPixel or DotStar strand is connected to. */ + string pin_dotstar_clock = 6; /** Clock pin a DotStar strand is connected to. */ + Write write = 7; /** Optional initial write for a strand of pixels, used during check-in. */ +} + +/** +* Added represents response from a WipperSnapper +* device to IO after a Add call +*/ +message Added { + bool is_success = 1; /** True if the strand initialized successfully, False otherwise. */ + string pin_data = 2; /** Data pin the responding strand is connected to. */ +} + +/** +* Removes a strand of addressable pixels and release the resources and pin. +*/ +message Remove { + string pin_data = 1; /** Data pin the pixel strand is connected to. */ +} + +/** +* Removed represents response from a WipperSnapper device to IO after a Remove call +*/ +message Removed { + bool is_success = 1; /** True if the strand was removed successfully, False otherwise. */ + string pin_data = 2; /** Data pin the responding strand is connected to. */ +} + + +/** +* Writes to a strand of pixels. +*/ +message Write { + string pin_data = 1; /** Data pin a strand is connected to. */ + uint32 color = 2; /** 32-bit color value. Most significant byte is white (for RGBW pixels) or ignored (for RGB pixels), + next is red, then green, and least significant byte is blue. */ +} diff --git a/proto/wippersnapper/pixels/v1/pixels.md b/proto/wippersnapper/pixels/v1/pixels.md deleted file mode 100644 index 8cf851b3..00000000 --- a/proto/wippersnapper/pixels/v1/pixels.md +++ /dev/null @@ -1,120 +0,0 @@ -# pixels.proto - -This file details the WipperSnapper messaging API for interfacing with a strand of addressable RGB(W) pixels (Adafruit NeoPixel/WS2812b, DotStar/APA102). - -## WipperSnapper Components - -The following component definitions reference `pixels.proto`: -* [Adafruit_DotStar](https://github.com/adafruit/Wippersnapper_Components/pull/44) -* [Adafruit_NeoPixels](https://github.com/adafruit/Wippersnapper_Components/pull/44) - -## Sequence Diagrams - -### Create: NeoPixel - -```mermaid -sequenceDiagram -autonumber -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` according to form
`pixels_pin_neopixel` according to form
`pixels_pin_dotstar_data` is unused
`pixels_pin_dotstar_clock` is unused -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -``` - -### Write: NeoPixel - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsWriteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_pin_data` according to DB
`pixels_color` according to picker
-``` - -### Update: NeoPixel - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsDeleteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_NEOPIXEL
pixels_pin_data according to DB -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` of 0
`pixels_pin_neopixel` according to form
`pixels_pin_dotstar_data` is unused
`pixels_pin_dotstar_clock` is unused -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -``` - -### Delete: NeoPixel - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsDeleteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_pin_data` according to DB -``` - -### Sync: NeoPixel -```mermaid -sequenceDiagram -autonumber -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` according to form
`pixels_pin_neopixel` according to form
`pixels_pin_dotstar_data` is unused
`pixels_pin_dotstar_clock` is unused -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -IO->>Device: PixelsWriteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_pin_data` according to DB
`pixels_color` according to feed's last_value
-``` - - -### Create: DotStar - -```mermaid -sequenceDiagram -autonumber -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_DOTSTAR
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` according to form
`pixels_pin_neopixel` unused
`pixels_pin_dotstar_data` according to form
`pixels_pin_dotstar_clock` according to form -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -``` - -### Write: DotStar - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsWriteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_DOTSTAR
`pixels_pin_data` according to DB
`pixels_color` according to picker
-``` - -### Update: DotStar - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsDeleteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_DOTSTAR
pixels_pin_data according to DB -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_DOTSTAR
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` of 0
`pixels_pin_neopixel` is unused
`pixels_pin_dotstar_data` according to form
`pixels_pin_dotstar_clock` according to form -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -``` - -### Delete: DotStar - -```mermaid -sequenceDiagram -autonumber -IO->>Device: PixelsDeleteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_DOTSTAR
`pixels_pin_data` according to DB -``` - -### Sync: DotStar -```mermaid -sequenceDiagram -autonumber -IO-->>Device: PixelsCreateRequest -Note over IO, Device: Contains:
`pixels_type` of PIXELS_TYPE_DOTSTAR
`pixels_num` according to form
`pixels_ordering` according to form
`pixels_brightness` according to form
`pixels_pin_neopixel` is unused
`pixels_pin_dotstar_data` according to form
`pixels_pin_dotstar_clock` according to form -Device->>IO: PixelsCreateResponse -Note over Device,IO: `is_success`, true if init'd OK -IO->>Device: PixelsWriteRequest -Note over IO, Device: Contains
`pixels_type` of PIXELS_TYPE_NEOPIXEL
`pixels_pin_data` according to DB
`pixels_color` according to feed's last_value
-``` diff --git a/proto/wippersnapper/pixels/v1/pixels.proto b/proto/wippersnapper/pixels/v1/pixels.proto deleted file mode 100644 index 348abb1f..00000000 --- a/proto/wippersnapper/pixels/v1/pixels.proto +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -// Addressable Pixels API for Adafruit WipperSnapper - -syntax = "proto3"; - -package wippersnapper.pixels.v1; -import "nanopb/nanopb.proto"; - -/** -* PixelsType defines the type/model of pixel strand. -*/ -enum PixelsType { - PIXELS_TYPE_UNSPECIFIED = 0; - PIXELS_TYPE_NEOPIXEL = 1; - PIXELS_TYPE_DOTSTAR = 2; -} - -/** -* PixelsOrder defines the color ordering. -*/ -enum PixelsOrder { - PIXELS_ORDER_UNSPECIFIED = 0; /** Unspecified color ordering, error. */ - PIXELS_ORDER_GRB = 1; /** DEFAULT for NeoPixels - Green, Red, Blue */ - PIXELS_ORDER_GRBW = 2; /** Green, Red, Blue, White */ - PIXELS_ORDER_RGB = 3; /** Red, Green, Blue */ - PIXELS_ORDER_RGBW = 4; /** Red, Green, Blue, White */ - PIXELS_ORDER_BRG = 5; /** DEFAULT for DotStars - Blue, Red, Green */ - PIXELS_ORDER_RBG = 6; /** Red, Blue Green */ - PIXELS_ORDER_GBR = 7; /** Green, Blue, Red */ - PIXELS_ORDER_BGR = 8; /** Blue, Green, Red */ -} - -/** -* PixelsCreateRequest represents a call from IO to a device. -* Creates a strand of addressable pixels. -* Initial brightness is always 128. -*/ -message PixelsCreateRequest { - PixelsType pixels_type = 1; /** Defines the model/type of pixel strand */ - uint32 pixels_num = 2; /** Number of pixels attached to strand. */ - PixelsOrder pixels_ordering = 3; /** Defines the pixel strand's color ordering. */ - uint32 pixels_brightness = 4; /** Strand brightness, 0 to 255 */ - string pixels_pin_neopixel = 5 [(nanopb).max_size = 6]; /** Generic pin a NeoPixel strand is connected to. */ - string pixels_pin_dotstar_data = 6 [(nanopb).max_size = 6]; /** Data pin a DotStar strand is connected to. */ - string pixels_pin_dotstar_clock = 7 [(nanopb).max_size = 6]; /** Clock pin a DotStar strand is connected to. */ -} - -/** -* PixelsCreateResponse represents response from a WipperSnapper -* device to IO after a PixelsCreateRequest call -*/ -message PixelsCreateResponse { - bool is_success = 1; /** True if the strand initialized successfully, False otherwise. */ - string pixels_pin_data = 2 [(nanopb).max_size = 6]; /** Data pin the responding strand is connected to. */ -} - -/** -* PixelCreateRequest represents a call from IO to a device -* Deletes a strand of addressable pixels and release the resources and pin. -*/ -message PixelsDeleteRequest { - PixelsType pixels_type = 1; /** Defines the model/type of pixel strand */ - string pixels_pin_data = 2 [(nanopb).max_size = 6]; /** Data pin a strand is connected to. */ -} - -/** -* PixelsWriteRequest represents a call from IO to a device. -* Writes to a strand of pixels. -*/ -message PixelsWriteRequest { - PixelsType pixels_type = 1; /** Defines the model/type of pixel strand */ - string pixels_pin_data = 2 [(nanopb).max_size = 6]; /** Data pin a strand is connected to. */ - uint32 pixels_color = 3; /* 32-bit color value. Most significant byte is white (for RGBW pixels) or ignored (for RGB pixels), next is red, then green, and least significant byte is blue. */ -} diff --git a/proto/wippersnapper/pwm.options b/proto/wippersnapper/pwm.options new file mode 100644 index 00000000..0b0ff7fe --- /dev/null +++ b/proto/wippersnapper/pwm.options @@ -0,0 +1,5 @@ +# pwm.options +ws.pwm.Add.pin max_size:16 +ws.pwm.Added.pin max_size:16 +ws.pwm.Remove.pin max_size:16 +ws.pwm.Write.pin max_size:16 diff --git a/proto/wippersnapper/pwm.proto b/proto/wippersnapper/pwm.proto new file mode 100644 index 00000000..1a08def5 --- /dev/null +++ b/proto/wippersnapper/pwm.proto @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2022-2023 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.pwm; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + } +} + +/** +* Add represents a to a device to attach/allocate a PWM pin. +* On ESP32 Arduino, this will "attach" a pin to a LEDC channel/timer group. +* On non-ESP32 Arduino, this does nothing. +*/ +message Add { + string pin = 1; /** The pin to be attached. */ + int32 frequency = 2; /** PWM frequency of an analog pin, in Hz. **/ + int32 resolution = 3; /** The resolution of an analog pin, in bits. **/ + bool is_inverted = 4; /** If true, inverts the pin's output (active low). */ + Write write = 5; /** Optional initial write for a PWM pin, used during check-in. */ +} + +/** +* Added represents a response from a device's execution of an +* Add message. +*/ +message Added { + string pin = 1; /** The ed pin. */ + bool did_attach = 2; /** True if Add successful, False otherwise. */ +} + +/** +* Remove represents a to stop PWM'ing and release the pin for re-use. +* On ESP32, this will "detach" a pin from a LEDC channel/timer group. +* On non-ESP32 Arduino, this calls digitalWrite(LOW) on the pin +*/ +message Remove { + string pin = 1; /** The PWM pin to de-initialized. */ +} + +/** +* Write represents writing to a pin either: +* - a duty cycle (frequency is fixed) This is used for controlling LEDs. +* - a frequency in Hz (duty cycle fixed at 50%) This is used for playing tones using a piezo buzzer or speaker +* This value will be changed by the slider on Adafruit IO. +*/ +message Write { + string pin = 1; /** The pin to write to. */ + oneof payload { + int32 duty_cycle = 2; /** The desired duty cycle (range is from 0 to (2 ** duty_resolution)). **/ + int32 frequency = 3; /** The desired PWM frequency, in Hz. **/ + } +} diff --git a/proto/wippersnapper/pwm/v1/pwm.md b/proto/wippersnapper/pwm/v1/pwm.md deleted file mode 100644 index 206de648..00000000 --- a/proto/wippersnapper/pwm/v1/pwm.md +++ /dev/null @@ -1,135 +0,0 @@ - -# pwm.proto - -This file details the WipperSnapper messaging API for interfacing with PWM output components. - -PWM components either have a fixed frequency with a variable duty cycle _or_ a variable frequency with a fixed duty cycle. - -## WipperSnapper Components - -The following WipperSnapper components utilize `pwm.proto`: - -* Dimmable LED (Fixed Frequency, variable Duty Cycle) - -* Piezo Buzzer (Variable Frequency, fixed Duty Cycle) - - -## Sequence Diagrams - -### Create: Dimmable LED - -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to form
`frequency` of 5000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. -``` - - -### Write: Dimmable LED -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMWriteDutyCycleRequest -Note over IO, Device: The duty_cycle (0->255) from the
IO slider widget is written to the `pin`. -``` - -### Update: Dimmable LED -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMDetachRequest -Note over IO, Device: Detaches GPIO pin from a timer - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to form
`frequency` of 5000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. -``` - -### Delete: Dimmable LED -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMDetachRequest -Note over IO, Device: Detaches GPIO pin from a timer -``` - -### Sync: Dimmable LED -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to DB
`frequency` of 5000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. - -IO->>Device: PWMWriteDutyCycleRequest -Note over IO, Device: duty_cycle (0->255) from IO feed's last_value. -``` - -### Create: Piezo Buzzer - -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to form
`frequency` of 1000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. -``` - - -### Write: Piezo Buzzer -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMWriteFrequencyRequest -Note over IO, Device: Any frequency > 0Hz to play a tone, 0Hz to turn off -``` - -### Update: Piezo Buzzer -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMDetachRequest -Note over IO, Device: Detaches GPIO pin from a timer - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to form
`frequency` of 1000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. -``` - -### Delete: Piezo Buzzer -```mermaid -sequenceDiagram -autonumber -IO->>Device: PWMDetachRequest -Note over IO, Device: Detaches GPIO pin from a timer -``` - -### Sync: Piezo Buzzer -```mermaid -sequenceDiagram -autonumber - -IO-->>Device: PWMAttachRequest -Note over IO, Device: Contains:
`pin` according to DB
`frequency` of 1000Hz
`resolution` of 12 bits - -Device->>IO: PWMAttachResponse -Note over IO, Device: Contains:
`pin` from
corresponding PWMAttachRequest msg.
`did_attach` True if attached. - -IO->>Device: PWMWriteFrequencyRequest -Note over IO, Device: frequency, in Hz, from IO feed's last_value. -``` diff --git a/proto/wippersnapper/pwm/v1/pwm.proto b/proto/wippersnapper/pwm/v1/pwm.proto deleted file mode 100644 index 5b464b51..00000000 --- a/proto/wippersnapper/pwm/v1/pwm.proto +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.pwm.v1; -import "nanopb/nanopb.proto"; - -/** -* PWMAttachRequest represents a request to a device to attach/allocate a PWM pin. -* On ESP32 Arduino, this will "attach" a pin to a LEDC channel/timer group. -* On non-ESP32 Arduino, this does nothing. -*/ -message PWMAttachRequest { - string pin = 1 [(nanopb).max_size = 6]; /** The pin to be attached. */ - int32 frequency = 2; /** PWM frequency of an analog pin, in Hz. **/ - int32 resolution = 3; /** The resolution of an analog pin, in bits. **/ -} - -/** -* PWMAttachResponse represents a response from a device's execution of an -* AttachRequest message. -*/ -message PWMAttachResponse { - string pin = 1 [(nanopb).max_size = 6]; /** The requested pin. */ - bool did_attach = 2; /** True if AttachRequest successful, False otherwise. */ -} - -/** -* PWMDetachRequest represents a request to stop PWM'ing and release the pin for re-use. -* On ESP32, this will "detach" a pin from a LEDC channel/timer group. -* On non-ESP32 Arduino, this calls digitalWrite(LOW) on the pin -*/ -message PWMDetachRequest { - string pin = 1 [(nanopb).max_size = 6]; /** The PWM pin to de-initialized. */ -} - -/** -* WriteDutyCycleRequest represents a request to write a duty cycle to a pin with a frequency (fixed). -* This is used for controlling LEDs. -*/ -message PWMWriteDutyCycleRequest { - string pin = 1 [(nanopb).max_size = 6]; /** The pin to write to. */ - int32 duty_cycle = 2; /** The desired duty cycle to write. This value will be changed by the slider on Adafruit IO. **/ -} - -/** -* WriteDutyCycle represents a wrapper request to write duty cycles to multiple pins. -* This is used for controlling RGB/RGBW LEDs. -*/ -message PWMWriteDutyCycleMultiRequest { - repeated PWMWriteDutyCycleRequest write_duty_cycle_req = 1 [(nanopb).max_count = 4]; -} - -/** -* WriteFrequencyRequest represents a request to write a Frequency, in Hz, to a pin with a duty cycle of 50%. -* This is used for playing tones using a piezo buzzer or speaker. -*/ -message PWMWriteFrequencyRequest { - string pin = 1 [(nanopb).max_size = 6]; /** The pin to write to. */ - int32 frequency = 2; /** The desired PWM frequency, in Hz. This value will be changed by the slider on Adafruit IO. **/ -} \ No newline at end of file diff --git a/proto/wippersnapper/sensor.proto b/proto/wippersnapper/sensor.proto new file mode 100644 index 00000000..5462ab6a --- /dev/null +++ b/proto/wippersnapper/sensor.proto @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 Brent Rubell, Loren Norman for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.sensor; + +/** +* Type allows us determine what types of units the sensor uses, etc. +*/ +enum Type { + T_UNSPECIFIED = 0; /** Sensor value type which is not defined by this list, "Raw Value: {value}". */ + T_ACCELEROMETER = 1; /** Acceleration, in meter per second per second, "{value}m/s/s". */ + T_MAGNETIC_FIELD = 2; /** Magnetic field strength, in micro-Tesla, "{value}µT". */ + T_ORIENTATION = 3; /** Orientation angle, in degrees, "{value}°". */ + T_GYROSCOPE = 4; /** Angular rate, in radians per second, "{value}rad/s". */ + T_LIGHT = 5; /** Light-level, non-unit-specific (Or see: Lux), "Raw Value: {value}". */ + T_PRESSURE = 6; /** Pressure, in hectopascal, , "{value}hPa". */ + T_PROXIMITY = 8; /** Distance from an object to a sensor, non-unit-specific, "Raw Value: {value}". */ + T_GRAVITY = 9; /** Metres per second squared, "{value}m/s^2". */ + T_LINEAR_ACCELERATION = 10; /** Acceleration not including gravity, in meter per second squared, "{value}m/s^2". */ + T_ROTATION_VECTOR = 11; /** An angle in radians, "{value} rad".*/ + T_RELATIVE_HUMIDITY = 12; /** in percent (%), "{value}%". */ + T_AMBIENT_TEMPERATURE = 13; /** Temperature of the air around a sensor, in degrees Celsius, "{value}°C". */ + T_OBJECT_TEMPERATURE = 14; /** Temperature of the object a sensor is touching/pointed at, in degrees Celsius, "{value}°C".*/ + T_VOLTAGE = 15; /** Volts, "{value}V". */ + T_CURRENT = 16; /** Milliamps, "{value}mA". */ + T_COLOR = 17; /** Values are in 0..1.0 RGB channel luminosity and 32-bit RGBA format. "Color: {value}".*/ + T_RAW = 18; /** Sensor reads a value which is not defined by this list, "Raw Value: {value}".*/ + T_PM10_STD = 19; /** Standard Particulate Matter 1.0, in ppm, "{value}ppm". */ + T_PM25_STD = 20; /** Standard Particulate Matter 2.5, in ppm, "{value}ppm". */ + T_PM100_STD = 21; /** Standard Particulate Matter 100, in ppm, "{value}ppm". */ + T_PM10_ENV = 22; /** Environmental Particulate Matter 1.0, in ppm, "{value}ppm". */ + T_PM25_ENV = 23; /** Environmental Particulate Matter 2.5, in ppm, "{value}ppm". */ + T_PM100_ENV = 24; /** Environmental Particulate Matter 100, in ppm, "{value}ppm".*/ + T_CO2 = 25; /** Measured CO2, in ppm, "{value}ppm". */ + T_GAS_RESISTANCE = 26; /** Proportional to the amount of VOC particles in the air, in Ohms, "{value}Ω". */ + T_ALTITUDE = 27; /** Values are in meters (m), "${$v} m". */ + T_LUX = 28; /** Light level, in lux, "Lux: {value}". */ + T_ECO2 = 29; /** equivalent/estimated CO2 in ppm (estimated from some other measurement), "{value}ppm". */ + T_UNITLESS_PERCENT = 30; /** Percentage, unit-less, "{value}%". */ + T_AMBIENT_TEMPERATURE_FAHRENHEIT = 31; /** Temperature of the air around a sensor, in degrees Fahrenheit, "{value}°F". */ + T_OBJECT_TEMPERATURE_FAHRENHEIT = 32; /** Temperature of the object a sensor is touching/pointed at, in Fahrenheit, "{value}°F".*/ + T_VOC_INDEX = 33; /** Values are an index from 1-500 with 100 being normal, "${$v} VOC".*/ + T_NOX_INDEX = 34; /** Values are an index from 1-500 with 100 being normal, "${$v} NOx".*/ + T_TVOC = 35; /** Values are in parts per billion (ppb), "${$v} ppb". */ + T_BYTES = 36; /** Values are in bytes, "${$v} bytes". */ + T_BOOLEAN = 37; /** Values are boolean, "Boolean Value: ${$v}". */ +} + +/** +* Event is used to return the sensor's value and type. +*/ +message Event { + Type type = 1; /** The sensor's type and corresponding SI unit */ + oneof value { + float float_value = 2; /** The sensor's value as a float. */ + bytes bytes_value = 3; /** The sensor's value as a byte array. */ + Event3DVector vector_value = 4; /** The sensor's 3D vector values, as floats. */ + EventOrientation orientation_value = 5; /** The sensor's orientation values, as floats. */ + EventColor color_value = 6; /** The sensor's color values, as floats. */ + bool bool_value = 7; /** The sensor's value, as a boolean. */ + } + + /** + * EventColor is used to return a sensor's color values in RGB colorspace. + */ + message EventColor { + float r = 1; /** The sensor's red channel value as a float. */ + float g = 2; /** The sensor's green channel value as a float. */ + float b = 3; /** The sensor's blue channel value as a float. */ + float a = 4; /** The sensor's (optional) alpha channel value as a float. */ + } + + /** + * Event3DVector is used to return a sensor's 3D vector values. + */ + message Event3DVector { + float x = 1; /** The sensor's x-axis value as a float. */ + float y = 2; /** The sensor's y-axis value as a float. */ + float z = 3; /** The sensor's z-axis value as a float. */ + } + + /** + * EventOrientation is used to return an orientation sensor's values. + */ + message EventOrientation { + float roll = 1; /** The sensor's roll value as a float. */ + float pitch = 2; /** The sensor's pitch value as a float. */ + float heading = 3; /** The sensor's heading value as a float. */ + } +} diff --git a/proto/wippersnapper/servo.options b/proto/wippersnapper/servo.options new file mode 100644 index 00000000..66eaff1c --- /dev/null +++ b/proto/wippersnapper/servo.options @@ -0,0 +1,5 @@ +# servo.options +ws.servo.Add.servo_pin max_size:6 +ws.servo.Added.servo_pin max_size:6 +ws.servo.Remove.servo_pin max_size:6 +ws.servo.Write.servo_pin max_size:6 diff --git a/proto/wippersnapper/servo.proto b/proto/wippersnapper/servo.proto new file mode 100644 index 00000000..d6af8a72 --- /dev/null +++ b/proto/wippersnapper/servo.proto @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2022-2024 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.servo; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + } +} + +/** +* Add represents a request to attach a servo to a pin. +*/ +message Add { + string servo_pin = 1; /** The name of pin to attach a servo to. */ + int32 freq = 2; /** The overall PWM frequency, default sent by Adafruit IO is 50Hz. **/ + int32 min_pulse_width = 3; /** The minimum pulse length in uS. Default sent by Adafruit IO is 500uS. **/ + int32 max_pulse_width = 4; /** The maximum pulse length in uS. Default sent by Adafruit IO is 2500uS. **/ + Write write = 5; /** Optional initial write for a servo pin, used during check-in. **/ +} + +/** +* Added represents the result of attaching a servo to a pin. +*/ +message Added { + bool attach_success = 1; /** True if a servo was attached successfully, False otherwise. **/ + string servo_pin = 2; /** The name of pin we're responding about. */ +} + +/** +* Remove represents a request to detach a servo from a pin and de-initialize the pin for other uses. +*/ +message Remove { + string servo_pin = 1; /** The name of pin to use as a servo pin. */ +} + +/** +* Write represents a message to write the servo's position. +* +* NOTE: Position is sent from Adafruit IO as a pulse width in uS between 0uS +* and 2500uS. The client application must convert pulse width to duty cycle w/fixed +* freq of 50Hz prior to writing to the servo pin. +*/ +message Write { + string servo_pin = 1; /** The name of pin we're addressing. */ + int32 pulse_width = 2; /** The pulse width to write to the servo, in uS **/ +} diff --git a/proto/wippersnapper/servo/v1/servo.md b/proto/wippersnapper/servo/v1/servo.md deleted file mode 100644 index a828f027..00000000 --- a/proto/wippersnapper/servo/v1/servo.md +++ /dev/null @@ -1,79 +0,0 @@ - -# servo.proto - - This file details the WipperSnapper messaging API for interfacing with servo output components. - -## WipperSnapper Components - - - -The following WipperSnapper components utilize `servo.proto`: -* [Generic Servo](https://github.com/adafruit/Wippersnapper_Components/tree/main/components/servo/servo) - - -## Sequence Diagrams - - - -### Create: Servo - -```mermaid -sequenceDiagram -autonumber -IO-->>Device: ServoAttachRequest -Note over IO, Device: Contains:
`servo_pin` from form
`servo_freq` of 50Hz
`min_pulse_width` from form
`max_pulse_width` from form - -Device->>IO: ServoAttachResponse -Note over IO, Device: Contains: Success code and servo's pin -``` - -### Write: Servo - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: ServoWriteRequest -Note over IO, Device: Position is sent from Adafruit IO as a pulse width
between 500uS and 2500uS.
The client application must convert pulse width to duty cycle
with fixed freq of 50Hz prior to writing to the servo pin. -``` - - - -### Update: Servo - -```mermaid -sequenceDiagram -autonumber - -IO->>Device: ServoDetachRequest -Note over IO, Device: Deinits servo object, releases gpio pin -IO-->>Device: ServoAttachRequest -Note over IO, Device: Contains:
`servo_pin` from form
`servo_freq` of 50Hz
`min_pulse_width` from form
`max_pulse_width` from form -Device->>IO: ServoAttachResponse -Note over IO, Device: Contains: Success code and servo's pin -``` - - - -### Delete: Servo - -```mermaid -sequenceDiagram -autonumber -IO->>Device: ServoDetachRequest -Note over IO, Device: Contains:
`servo_pin` from DB. -``` - - - -### Sync: Servo - -```mermaid -sequenceDiagram -autonumber -IO-->>Device: ServoAttachRequest -Note over IO, Device: Contains:
`servo_pin` from form
`servo_freq` of 50Hz
`min_pulse_width` from form
`max_pulse_width` from form - -Device->>IO: ServoAttachResponse -Note over IO, Device: Contains: Success code and servo's pin -``` \ No newline at end of file diff --git a/proto/wippersnapper/servo/v1/servo.proto b/proto/wippersnapper/servo/v1/servo.proto deleted file mode 100644 index 92a5ec5e..00000000 --- a/proto/wippersnapper/servo/v1/servo.proto +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.servo.v1; -import "nanopb/nanopb.proto"; - -/** -* ServoAttachRequest represents a request to attach a servo to a pin. -*/ -message ServoAttachRequest { - string servo_pin = 1 [(nanopb).max_size = 6]; /** The name of pin to attach a servo to. */ - int32 servo_freq = 2; /** The overall PWM frequency, default sent by Adafruit IO is 50Hz. **/ - int32 min_pulse_width = 3; /** The minimum pulse length in uS. Default sent by Adafruit IO is 500uS. **/ - int32 max_pulse_width = 4; /** The maximum pulse length in uS. Default sent by Adafruit IO is 2500uS. **/ -} - -/** -* ServoAttachResponse represents the result of attaching a servo to a pin. -*/ -message ServoAttachResponse { - bool attach_success = 1; /** True if a servo was attached successfully, False otherwise. **/ - string servo_pin = 2 [(nanopb).max_size = 6]; /** The name of pin we're responding about. */ -} - -/** -* ServoDetachRequest represents a request to detach a servo from a pin and de-initialize the pin for other uses. -*/ -message ServoDetachRequest { - string servo_pin = 1 [(nanopb).max_size = 5]; /** The name of pin to use as a servo pin. */ -} - -/** -* ServoWriteReq represents a message to write the servo's position. -* -* NOTE: Position is sent from Adafruit IO as a pulse width in uS between 500uS -* and 2500uS. The client application must convert pulse width to duty cycle w/fixed -* freq of 50Hz prior to writing to the servo pin. -*/ -message ServoWriteRequest { - string servo_pin = 1 [(nanopb).max_size = 5]; /** The name of pin we're addressing. */ - int32 pulse_width = 2; /** The pulse width to write to the servo, in uS **/ -} diff --git a/proto/wippersnapper/signal.options b/proto/wippersnapper/signal.options new file mode 100644 index 00000000..1bad0e3a --- /dev/null +++ b/proto/wippersnapper/signal.options @@ -0,0 +1,3 @@ +# signal.options +ws.signal.BrokerToDevice submsg_callback:true +ws.signal.DeviceToBroker submsg_callback:true diff --git a/proto/wippersnapper/signal.proto b/proto/wippersnapper/signal.proto new file mode 100644 index 00000000..c9f622a2 --- /dev/null +++ b/proto/wippersnapper/signal.proto @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2020-2025 Brent Rubell, Loren Norman for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.signal; +import "error.proto"; +import "expander.proto"; +import "checkin.proto"; +import "analogio.proto"; +import "digitalio.proto"; +import "display.proto"; +import "ds18x20.proto"; +import "i2c.proto"; +import "pixels.proto"; +import "pwm.proto"; +import "servo.proto"; +import "sleep.proto"; +import "uart.proto"; +import "gps.proto"; + +/* + * BrokerToDevice + * The BrokerToDevice message is sent from the broker to the device. + * It contains a oneof payload, which is a union of all the possible + * message-types that can be sent from the broker to a device. + */ +message BrokerToDevice { + oneof payload { + // System Events + ws.error.ErrorB2D error = 10; + + // Device Interactions + ws.checkin.B2D checkin = 20; + ws.sleep.B2D sleep = 21; + + // Component Interactions + ws.digitalio.B2D digitalio = 30; + ws.analogio.B2D analogio = 31; + ws.servo.B2D servo = 32; + ws.pwm.B2D pwm = 33; + ws.pixels.B2D pixels = 34; + ws.ds18x20.B2D ds18x20 = 35; + ws.display.B2D display = 36; + ws.uart.B2D uart = 37; + ws.i2c.B2D i2c = 38; + ws.gps.B2D gps = 39; + ws.expander.B2D expander = 40; + } +} + +/* + * DeviceToBroker + * The DeviceToBroker message is sent from the device to the broker. + * It contains a oneof payload, which is a union of all the possible + * message-types that can be sent from a device to the broker. + */ +message DeviceToBroker { + oneof payload { + // System Events + ws.error.ErrorD2B error = 10; + + // Device Interactions + ws.checkin.D2B checkin = 20; + ws.sleep.D2B sleep = 21; + + // Component Interactions + ws.digitalio.D2B digitalio = 30; + ws.analogio.D2B analogio = 31; + ws.servo.D2B servo = 32; + ws.pwm.D2B pwm = 33; + ws.pixels.D2B pixels = 34; + ws.ds18x20.D2B ds18x20 = 35; + ws.display.D2B display = 36; + ws.uart.D2B uart = 37; + ws.i2c.D2B i2c = 38; + ws.gps.D2B gps = 39; + ws.expander.D2B expander = 40; + } +} diff --git a/proto/wippersnapper/signal/v1/signal.md b/proto/wippersnapper/signal/v1/signal.md deleted file mode 100644 index bc137c85..00000000 --- a/proto/wippersnapper/signal/v1/signal.md +++ /dev/null @@ -1,26 +0,0 @@ - -# signal.proto - -This file details the `signal.proto` message used to communicate between WipperSnapper clients. The signal file contains high-level `oneof` messages that "wrap" the following protocol buffer APIs: `pin.proto`, `i2c.proto`, `servo.proto`, `pwm.proto`, `ds18x20.proto`, `pixels .proto`, `uart.proto`. - -## Sequence Diagrams - -### Generalized `msgRequest` and `msgResponse` - -Within `signal.proto`, each `.proto` API contains both a `request` and `response` message. The `request` message is a command sent from the broker to a device. The `response` message is a command sent from the device to the broker. - -```mermaid -sequenceDiagram -autonumber - -IO Broker->>Device Client: ServoRequest -Note over IO Broker,Device Client: /:username/wprsnpr/:clientId/signals/device/servo -Device Client->>App: ServoRequest -App->>(nanopb) Encoder/Decoder: ServoRequest -(nanopb) Encoder/Decoder->>Component Class: ServoAttachRequest -Component Class->>(nanopb) Encoder/Decoder: Result of ServoAttachRequest -(nanopb) Encoder/Decoder->>App: ServoResponse -App->>Device Client: ServoResponse -Device Client->>IO Broker: ServoResponse -Note over Device Client,IO Broker: /:username/wprsnpr/:clientId/signals/broker/servo -``` diff --git a/proto/wippersnapper/signal/v1/signal.proto b/proto/wippersnapper/signal/v1/signal.proto deleted file mode 100644 index 14afa53c..00000000 --- a/proto/wippersnapper/signal/v1/signal.proto +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-FileCopyrightText: 2020-2025 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.signal.v1; - -// Non-WipperSnapper -import "nanopb/nanopb.proto"; - -// WipperSnapper -import "wippersnapper/pin/v1/pin.proto"; -import "wippersnapper/i2c/v1/i2c.proto"; -import "wippersnapper/servo/v1/servo.proto"; -import "wippersnapper/pwm/v1/pwm.proto"; -import "wippersnapper/ds18x20/v1/ds18x20.proto"; -import "wippersnapper/pixels/v1/pixels.proto"; -import "wippersnapper/uart/v1/uart.proto"; -import "wippersnapper/display/v1/display.proto"; - -/** -* UARTRequest represents a UART command sent to a device. -*/ -message UARTRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.uart.v1.UARTDeviceAttachRequest req_uart_device_attach = 1; - wippersnapper.uart.v1.UARTDeviceDetachRequest req_uart_device_detach = 2; - } -} - -/** -* UARTResponse represents a UART command from a device. -*/ -message UARTResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.uart.v1.UARTDeviceAttachResponse resp_uart_device_attach = 1; - wippersnapper.uart.v1.UARTDeviceEvent resp_uart_device_event = 2; - } -} - -/** -* Ds18x20Request represents a Ds18x20 command sent to a device. -*/ -message Ds18x20Request { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.ds18x20.v1.Ds18x20InitRequest req_ds18x20_init = 1; - wippersnapper.ds18x20.v1.Ds18x20DeInitRequest req_ds18x20_deinit = 2; - } -} - -/** -* Ds18x20Response represents a Ds18x20 message from the device. -*/ -message Ds18x20Response { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.ds18x20.v1.Ds18x20InitResponse resp_ds18x20_init = 1; - wippersnapper.ds18x20.v1.Ds18x20DeviceEvent resp_ds18x20_event = 2; - } -} - -/** -* I2CRequest represents the broker's request for a specific i2c command to a device. -*/ -message I2CRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.i2c.v1.I2CBusInitRequest req_i2c_init = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; - wippersnapper.i2c.v1.I2CBusScanRequest req_i2c_scan = 2; - wippersnapper.i2c.v1.I2CBusSetFrequency req_i2c_set_freq = 3; - wippersnapper.i2c.v1.I2CDeviceInitRequest req_i2c_device_init = 4; - wippersnapper.i2c.v1.I2CDeviceDeinitRequest req_i2c_device_deinit = 5; - wippersnapper.i2c.v1.I2CDeviceUpdateRequest req_i2c_device_update = 6; - wippersnapper.i2c.v1.I2CDeviceInitRequests req_i2c_device_init_requests = 7; - wippersnapper.i2c.v1.I2CDeviceOutputWrite req_i2c_device_out_write = 8; - } -} - -/** -* I2CResponse represents the device's response to an I2C-specific message from IO. -*/ -message I2CResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.i2c.v1.I2CBusInitResponse resp_i2c_init = 1 [deprecated = true, (nanopb).type = FT_IGNORE]; - wippersnapper.i2c.v1.I2CBusScanResponse resp_i2c_scan = 2; - wippersnapper.i2c.v1.I2CDeviceInitResponse resp_i2c_device_init = 3; - wippersnapper.i2c.v1.I2CDeviceDeinitResponse resp_i2c_device_deinit = 4; - wippersnapper.i2c.v1.I2CDeviceUpdateResponse resp_i2c_device_update = 5; - wippersnapper.i2c.v1.I2CDeviceEvent resp_i2c_device_event = 6; - } -} - -/** -* ServoRequest represents the broker's request across the servo sub-topic. -*/ -message ServoRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.servo.v1.ServoAttachRequest servo_attach = 1; - wippersnapper.servo.v1.ServoDetachRequest servo_detach = 2; - wippersnapper.servo.v1.ServoWriteRequest servo_write = 3; - } -} - -/** -* ServoResponse represents the device's response across the servo sub-topic. -*/ -message ServoResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.servo.v1.ServoAttachResponse servo_attach_resp = 1; - } -} - -/** -* PixelsRequest represents the broker's request across the pixels sub-topic. -*/ -message PixelsRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.pixels.v1.PixelsCreateRequest req_pixels_create = 1; - wippersnapper.pixels.v1.PixelsDeleteRequest req_pixels_delete = 2; - wippersnapper.pixels.v1.PixelsWriteRequest req_pixels_write = 3; - } -} - -message PixelsResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.pixels.v1.PixelsCreateResponse resp_pixels_create = 1; - } -} - -message CreateSignalRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - // Create, update or remove a GPIO pin - wippersnapper.pin.v1.ConfigurePinRequests pin_configs = 6; - // Update a pins state - wippersnapper.pin.v1.PinEvent pin_event = 15; - // Create, update or remove a PWM output pin - wippersnapper.pin.v1.ConfigurePWMPinRequests pwm_pin_config = 10 [deprecated = true, (nanopb).type = FT_IGNORE]; - // Write duty cycle to a PWM output pin - wippersnapper.pin.v1.PWMPinEvents pwm_pin_event = 12 [deprecated = true, (nanopb).type = FT_IGNORE]; - // Update a pin's state - wippersnapper.pin.v1.PinEvents pin_events = 7; - } -} - -/** -* Response from a signal message payload (device->broker) -*/ -message SignalResponse { - oneof payload { - bool configuration_complete = 1; /** True if a device successfully completed a ConfigurePinRequests message, False otherwise. */ - } -} - -/** -* PWMRequest represents a broker's request across the PWM sub-topic. -*/ -message PWMRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.pwm.v1.PWMAttachRequest attach_request = 1; - wippersnapper.pwm.v1.PWMDetachRequest detach_request = 2; - wippersnapper.pwm.v1.PWMWriteDutyCycleRequest write_duty_request = 3; - wippersnapper.pwm.v1.PWMWriteDutyCycleMultiRequest write_duty_multi_request = 4; - wippersnapper.pwm.v1.PWMWriteFrequencyRequest write_freq_request = 5; - } -} - -/** -* PWMRequest represents a devices's response across the PWM sub-topic. -*/ -message PWMResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.pwm.v1.PWMAttachResponse attach_response = 1; - } -} - -/** -* DisplayRequest represents a broker's request across the display sub-topic. -*/ -message DisplayRequest { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.display.v1.DisplayAddOrReplace display_add = 1; - wippersnapper.display.v1.DisplayRemove display_remove = 2; - wippersnapper.display.v1.DisplayWrite display_write = 3; - } -} - -/** -* DisplayResponse represents a devices's response across the display sub-topic. -*/ -message DisplayResponse { - option (nanopb_msgopt).submsg_callback = true; - oneof payload { - wippersnapper.display.v1.DisplayAddedOrReplaced display_added = 1; - wippersnapper.display.v1.DisplayRemoved display_removed = 2; - } -} \ No newline at end of file diff --git a/proto/wippersnapper/sleep.options b/proto/wippersnapper/sleep.options new file mode 100644 index 00000000..bd695450 --- /dev/null +++ b/proto/wippersnapper/sleep.options @@ -0,0 +1,2 @@ +# sleep.options +ws.sleep.Ext0Config.pin_name max_size: 32 diff --git a/proto/wippersnapper/sleep.proto b/proto/wippersnapper/sleep.proto new file mode 100644 index 00000000..5b6b8c7b --- /dev/null +++ b/proto/wippersnapper/sleep.proto @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2025 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.sleep; + +/** + * Device sleep modes + */ +enum SleepMode { + S_UNSPECIFIED = 0; /** Sleep mode is unspecified. */ + S_LIGHT = 1; /** Light sleep mode. */ + S_DEEP = 2; /** Deep sleep mode. */ +} + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + SleepConfig sleep_config = 1; /** Message containing required information to enter deep sleep. */ + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Goodnight goodnight = 1; /** Goodnight event. */ + } +} + +/** + * Tell the broker the device is going to sleep now. + */ +message Goodnight { + +} + +/** + * Timer configuration for wakeup source. + */ +message TimerConfig { + uint32 duration = 1; /** How long the timer will run before triggering a wakeup, in seconds. */ +} + +/** + * Pin configuration for ext0 RTC wakeup source. + */ +message Ext0Config { + string pin_name = 1; /** Pin to wake up from. */ + bool level = 2; /** Input level which will trigger wakeup (False = low, True = high) */ + bool pull = 3; /** Enable internal pull-up or pull-down resistors */ +} + +/** + * Contains configuration to enter a sleep mode. + */ +message SleepConfig { + SleepMode mode = 1; /** Sleep mode. */ + oneof config { + TimerConfig timer = 2; /** Timer configuration for wakeup source. */ + Ext0Config ext0 = 3; /** EXT0 RTC configuration for wakeup source. */ + } +} diff --git a/proto/wippersnapper/spi.options b/proto/wippersnapper/spi.options new file mode 100644 index 00000000..d2d65d0a --- /dev/null +++ b/proto/wippersnapper/spi.options @@ -0,0 +1,6 @@ +# spi.options +# Nanopb configuration for spi.proto +ws.spi.Config.pin_mosi max_size:6 +ws.spi.Config.pin_sck max_size:6 +ws.spi.Config.pin_miso max_size:6 +ws.spi.Config.pin_cs max_size:6 diff --git a/proto/wippersnapper/spi.proto b/proto/wippersnapper/spi.proto new file mode 100644 index 00000000..9a93eadc --- /dev/null +++ b/proto/wippersnapper/spi.proto @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2025 Tyeth Gundry for Adafruit Industries +// SPDX-License-Identifier: MIT +// SPI interface for Adafruit WipperSnapper +// Reusable SPI bus and device pin configuration, shared across component types. + +syntax = "proto3"; +package ws.spi; + +/** SPI device configuration — bus selection and pin assignments. */ +message Config { + int32 bus = 1; /** SPI bus number */ + string pin_mosi = 2; /** Pin for MOSI */ + string pin_sck = 3; /** Pin for SCK */ + string pin_miso = 4; /** Pin for MISO */ + string pin_cs = 5; /** Pin for Chip Select */ +} diff --git a/proto/wippersnapper/uart.options b/proto/wippersnapper/uart.options new file mode 100644 index 00000000..f9da67ae --- /dev/null +++ b/proto/wippersnapper/uart.options @@ -0,0 +1,7 @@ +# uart.options +ws.uart.DeviceConfig.id max_size: 32 +ws.uart.SerialConfig.pin_rx max_size: 16 +ws.uart.SerialConfig.pin_tx max_size: 16 +ws.uart.GenericInputConfig.types max_count:15 +ws.uart.PM25AQIConfig.types max_count:15 +ws.uart.InputEvent.events max_count:15 diff --git a/proto/wippersnapper/uart.proto b/proto/wippersnapper/uart.proto new file mode 100644 index 00000000..0c9e4344 --- /dev/null +++ b/proto/wippersnapper/uart.proto @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: 2023-2025 Brent Rubell for Adafruit Industries +// SPDX-License-Identifier: MIT +syntax = "proto3"; +package ws.uart; +import "sensor.proto"; + +/** + * BrokerToDevice message envelope + */ +message B2D { + oneof payload { + Add add = 1; + Remove remove = 2; + Write write = 3; + } +} + +/** + * DeviceToBroker message envelope + */ +message D2B { + oneof payload { + Added added = 1; + Removed removed = 2; + Written written = 3; + InputEvent input_event = 4; + } +} + +/** +* PacketFormat contains the configuration data, parity, and stop bits for a serial port. +*/ +enum PacketFormat { + PF_UNSPECIFIED = 0; // Format was not specified by IO. + PF_8N1 = 1; // 8 data bits, no parity, 1 stop bit + PF_5N1 = 2; // 5 data bits, no parity, 1 stop bit + PF_6N1 = 3; // 6 data bits, no parity, 1 stop bit + PF_7N1 = 4; // 7 data bits, no parity, 1 stop bit + PF_5N2 = 5; // 5 data bits, no parity, 2 stop bits + PF_6N2 = 6; // 6 data bits, no parity, 2 stop bits + PF_7N2 = 7; // 7 data bits, no parity, 2 stop bits + PF_8N2 = 8; // 8 data bits, no parity, 2 stop bits + PF_5E1 = 9; // 5 data bits, even parity, 1 stop bit + PF_6E1 = 10; // 6 data bits, even parity, 1 stop bit + PF_7E1 = 11; // 7 data bits, even parity, 1 stop bit + PF_8E1 = 12; // 8 data bits, even parity, 1 stop bit + PF_5E2 = 13; // 5 data bits, even parity, 2 stop bits + PF_6E2 = 14; // 6 data bits, even parity, 2 stop bits + PF_7E2 = 15; // 7 data bits, even parity, 2 stop bits + PF_8E2 = 16; // 8 data bits, even parity, 2 stop bits + PF_5O1 = 17; // 5 data bits, odd parity, 1 stop bit + PF_6O1 = 18; // 6 data bits, odd parity, 1 stop bit + PF_7O1 = 19; // 7 data bits, odd parity, 1 stop bit + PF_8O1 = 20; // 8 data bits, odd parity, 1 stop bit + PF_5O2 = 21; // 5 data bits, odd parity, 2 stop bits + PF_6O2 = 22; // 6 data bits, odd parity, 2 stop bits + PF_7O2 = 23; // 7 data bits, odd parity, 2 stop bits + PF_8O2 = 24; // 8 data bits, odd parity, 2 stop bits +} + +/** +* DeviceType represents the type of device connected to the UART port. +* This is used to determine the driver to use for the device. +*/ +enum DeviceType { + DT_UNSPECIFIED = 0; /** Unspecified device type. */ + DT_GENERIC_INPUT = 1; /** Use UART input. */ + DT_GENERIC_OUTPUT = 2; /** Use the Generic UART output driver. */ + DT_GPS = 3; /** Use the GPS driver. */ + DT_PM25AQI = 4; /** Use the PM2.5 driver. */ + DT_TM22XX = 5; /** Use the TM22XX stepper driver. */ +} + +/** +* GenericDeviceLineEnding represents the line ending used by the device. +* This is used to determine how to parse the incoming data. +*/ +enum GenericDeviceLineEnding { + GDLE_UNSPECIFIED = 0; /** Unspecified line ending. */ + GDLE_LF = 1; /** Newline (LF). */ + GDLE_CRLF = 2; /** Carriage return (CR) and newline (LF). */ + GDLE_TIMEOUT_100MS = 3; /** 100ms timeout - sensor returns every 100ms with a new Event. */ + GDLE_TIMEOUT_1000MS = 4; /** 1s timeout - device returns every 1s with a new Event. */ +} + +/** +* SerialConfig represents a message to configure the Serial port (eg: Hardware Serial, Software Serial). +* This message is never sent directly, it is packed inside Add. +*/ +message SerialConfig { + string pin_rx = 1; /** The pin on which to receive on. */ + string pin_tx = 2; /** The pin on which to transmit with. */ + string name = 3; /** (For CPython ONLY) The device name, i.e: /dev/ttyUSB0. */ + uint32 uart_nbr = 4; /** The UART port number to use, eg: 0, 1, 2, etc. */ + uint32 baud_rate = 5; /** The desired baudrate, in bits per second. */ + PacketFormat format = 6; /** The data, parity, and stop bits. */ + float timeout = 7; /** Maximum milliseconds to wait for serial data. Defaults to 1000 ms. */ + bool use_sw_serial = 8; /** Use software serial instead of hardware serial. Defaults to False. */ + bool sw_serial_invert = 9; /** Optional: Inverts the UART signal on RX and TX pins. Defaults to False. */ +} + +/** +* GenericInputConfig represents a message sent from IO to a device +* containing device-specific configuration info for generic UART input devices. +*/ +message GenericInputConfig { + string name = 1; /** The name used to identify the device. */ + GenericDeviceLineEnding line_ending = 2; /** The line ending used by the device. */ + int32 period = 3; /** The period to poll the device, in milliseconds */ + repeated ws.sensor.Type types = 4; /** SI Types for each sensor on the UART device. */ +} + +/** +* TrinamicDynamixelConfig represents a message sent from IO to a device +* containing device-specific configuration info for Trinamic stepper or DYNAMIXEL servos. +*/ +message TrinamicDynamixelConfig { + uint32 id = 1; /** The device identifier, used for sub-addressing (multiple servos on one UART). */ +} + +/** +* PM25AQIConfig represents a message sent from IO to a device +* containing device-specific configuration info for PM2.5 AQI sensors. +*/ +message PM25AQIConfig { + bool is_pm1006 = 1; /** True if the device is a PM1006 AQ sensor, Defaults to False. */ + int32 period = 2; /** The period to poll the device, in milliseconds */ + repeated ws.sensor.Type types = 3; /** SI Types for each sensor on the I2c device. */ +} + +/** +* DeviceConfig represents a message sent from IO to a device +* containing device-specific configuration data. +* This message is never sent directly, it is packed inside Add. +*/ +message DeviceConfig { + DeviceType type = 1; /** The type of device connected to the UART port. */ + string id = 2; /** The unique identifier string for the UART device. */ + oneof config { + GenericInputConfig generic_input = 3; /** OPTIONAL configuration for a generic UART input device. */ + TrinamicDynamixelConfig trinamic_dynamixel = 4; /** OPTIONAL configuration for a Trinamic stepper or DYNAMIXEL servo. */ + PM25AQIConfig pm25aqi = 5; /** OPTIONAL configuration for a PM2.5 AQI sensor. */ + } +} + +/** +* Descriptor represents a message that uniquely identifies a UART device. +*/ +message Descriptor { + uint32 uart_nbr = 1; /** The UART port number (eg: 0, 1, 2, etc.) that the device is attached to. */ + DeviceType type = 2; /** The category of device attached to the UART port, corresponds to its driver type. */ + string id = 3; /** The unique identifier string for the UART device. */ +} + +/** +* UartAdd represents a message sent from IO to a device +* to configure a device on a UART port for communication. +*/ +message Add { + SerialConfig cfg_serial = 1; /** The Serial configuration. */ + DeviceConfig cfg_device = 2; /** The device-specific configuration. */ + Write write = 3; /** Optional initial write for a UART device, used during check-in. */ +} + +/** +* Added represents a message sent from a device to IO to +* confirm that a device has been attached to the UART port. +*/ +message Added { + Descriptor descriptor = 1; /** The descriptor of the device that was added. */ + bool success = 2; /** True if the device on the UART port was successfully initialized, False otherwise. */ +} + +/* +* Remove represents a message sent from IO to a device +* to detach a driver from the UART port and deinitialize the port. +*/ +message Remove { + Descriptor descriptor = 1; /** The descriptor of the device to remove. */ +} + +/* +* Removed represents a message sent from a device to IO to +* confirm that a device has been detached from the UART port. +*/ +message Removed { + Descriptor descriptor = 1; /** The descriptor of the device that was removed. */ + bool did_remove = 2; /** True if the device on the UART port was successfully removed, False otherwise. */ +} + +/** +* Write represents a message sent from IO to a device +* to write data to a device. +*/ +message Write { + Descriptor descriptor = 1; /** The descriptor of the device to write to. */ + // Payload + oneof payload { + bytes bytes_data = 2; /** Raw data to send to the device, corresponds to the Wiring API Serial.write(). */ + string text_data = 3; /** String to send to the device, corresponds to the Wiring API Serial.print(). */ + } +} + +/** +* Written represents the number of bytes written to a device.enum +* This message is sent from a device to IO to confirm that data has been written to the device. +*/ +message Written { + Descriptor descriptor = 1; /** The descriptor of the device that was written to. */ + uint32 bytes_written = 2; /** The number of bytes written to the device. */ +} + +/** +* InputEvent represents a message sent from a device to IO +* containing data from a UART input device. +* This message is sent from a device to IO to report sensor data. +* It can contain multiple SensorEvents if the device has multiple sensors. +*/ +message InputEvent { + Descriptor descriptor = 1; /** The descriptor of the device that generated the input event. */ + repeated ws.sensor.Event events = 2; /** Required, but optionally repeated, SensorEvent from a sensor. */ +} diff --git a/proto/wippersnapper/uart/v1/uart.md b/proto/wippersnapper/uart/v1/uart.md deleted file mode 100644 index 2e697d6e..00000000 --- a/proto/wippersnapper/uart/v1/uart.md +++ /dev/null @@ -1,61 +0,0 @@ - -# uart.proto - -This file details the WipperSnapper messaging API for interfacing with a UART bus. - -## WipperSnapper Components - -The following WipperSnapper components utilize `uart.proto`: - -* PMS* Air Quality Sensors -* Adafruit Universal GPS module using the MTK33x9 chipset - - -## Sequence Diagrams - -### Attaching a UART Component to a device running WipperSnapper - -```mermaid -sequenceDiagram -autonumber - -IO-->>WS Device: UARTDeviceAttachRequest - -WS Device-->>WS Device Decoder: UARTDeviceAttachRequest - -WS Device Decoder-->>WS Device UART: UARTBusData -Note over WS Device Decoder, WS Device UART: Initialize UART bus using configuration (UARTBusData). - -WS Device Decoder-->>WS Device UART: device_id, polling_interval -Note over WS Device Decoder, WS Device UART: Initialize UART device on the UART bus and associate it with a driver and a polling period. - -WS Device UART-->>WS Device: UARTDeviceAttachResponse - -WS Device-->>IO: UARTDeviceAttachResponse -Note over WS Device, IO: Returns true if successful, False if not. -``` - -### Attaching a UART Component to a device running WipperSnapper - -```mermaid -sequenceDiagram -autonumber - -Device-->>IO Broker: UARTDeviceEvent -IO Broker -->>IO Backend: Parse out repeated sensor_event into apropriate feeds for device_id -``` - -### Detaching a UART Component from a device running WipperSnapper - -```mermaid -sequenceDiagram -autonumber - -IO Broker --> Device: UARTDeviceDetachRequest -Device --> UART Class: Detach UART device from UART bus according to device_id. -``` - - - - - diff --git a/proto/wippersnapper/uart/v1/uart.proto b/proto/wippersnapper/uart/v1/uart.proto deleted file mode 100644 index ba0fa502..00000000 --- a/proto/wippersnapper/uart/v1/uart.proto +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Brent Rubell for Adafruit Industries -// SPDX-License-Identifier: MIT -syntax = "proto3"; - -package wippersnapper.uart.v1; -import "nanopb/nanopb.proto"; -import "wippersnapper/i2c/v1/i2c.proto"; - -/** -* UARTBusData represents a message to configure a UART bus for communication with a device. -* NOTE: This message is never sent directly, it is packed inside UARTDeviceAttachRequest. -*/ -message UARTBusData { - int32 baudrate = 1; /** The baudrate to use for UART communication (may be a common baud rate such as: 1200bps, 2400bps, 4800bps, 19200bps, 38400bps, 57600bps, or 115200bps). */ - string pin_rx = 2[(nanopb).max_size = 6]; /** The pin on which to receive UART stream data. */ - string pin_tx = 3[(nanopb).max_size = 6]; /** The pin on which to transmit UART stream data. */ - bool is_invert = 4; /** Inverts the UART signal on RX and TX pins. Defaults to False. */ -} - -/** -* UARTDeviceAttachRequest represents a message sent from IO to a device -* to configure the UART bus (if not already configured) and attach a device. -*/ -message UARTDeviceAttachRequest { - UARTBusData bus_info = 1; /** The UART bus configuration. */ - string device_id = 2[(nanopb).max_size = 15]; /** The unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - int32 polling_interval = 3; /** The polling interval, in milliseconds, to use for the device. */ -} - -/** -* UARTDeviceAttachResponse represents a message sent from a device to IO to -* confirm that a device has been attached to the UART bus. -*/ -message UARTDeviceAttachResponse { - string device_id = 1[(nanopb).max_size = 15]; /** The unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - bool is_success = 2; /** True if the UARTInitRequest was successful, False otherwise. */ -} - -/* -* UARTDeviceDetachRequest represents a message sent from IO to a device -* to detach a device from the UART bus. -*/ -message UARTDeviceDetachRequest { - string device_id = 1[(nanopb).max_size = 15]; /** The unique identifier of the device to detach from the UART bus. */ -} - -/** -* UARTDeviceEvent represents incoming data from a UART sensor. -*/ -message UARTDeviceEvent { - string device_id = 1[(nanopb).max_size = 15]; /** The unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - repeated wippersnapper.i2c.v1.SensorEvent sensor_event = 2[(nanopb).max_count = 15]; /** A, optionally repeated, SensorEvent from a sensor. */ -} diff --git a/protolint.yml b/protolint.yml new file mode 100644 index 00000000..40db1ecf --- /dev/null +++ b/protolint.yml @@ -0,0 +1,23 @@ +# Protolint configuration file for WipperSnapper +# configuration file reference: https://github.com/yoheimuta/protolint/blob/master/_example/config/.protolint.yaml +--- +lint: + rules: + all_default: true + remove: + - IMPORTS_SORTED + - FIELDS_HAVE_COMMENT + - MESSAGE_NAMES_EXCLUDE_PREPOSITIONS + - ENUM_FIELD_NAMES_PREFIX + + rules_option: + max_line_length: + max_chars: 150 + tab_chars: 2 + ignores: + - id: FIELDS_HAVE_COMMENT + files: + - proto/wippersnapper/signal.proto + - id: MESSAGE_NAMES_EXCLUDE_PREPOSITIONS + files: + - proto/wippersnapper/signal.proto