diff --git a/.github/workflows/build-clang-doxy.yml b/.github/workflows/build-clang-doxy.yml index a240e12de..7a7987c2b 100644 --- a/.github/workflows/build-clang-doxy.yml +++ b/.github/workflows/build-clang-doxy.yml @@ -47,7 +47,7 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 @@ -213,7 +213,7 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 @@ -321,7 +321,7 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - name: Download stable Nanopb id: download-nanopb @@ -448,8 +448,8 @@ jobs: - name: Install extra Arduino libraries run: | git clone --quiet https://github.com/adafruit/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino - name: Download stable Nanopb id: download-nanopb @@ -565,69 +565,6 @@ jobs: path: | wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - # NOTE: This does NOT release artifacts, it only builds - build-samd-non-fs: - name: 🏗️SAMD🚫⧾🔱 - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - arduino-platform: ["mkrwifi1010", "nano_33_iot"] - steps: - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - ref: ci-wippersnapper - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/arduino-libraries/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA - git clone --quiet https://github.com/arduino-libraries/Servo.git /home/runner/Arduino/libraries/Servo - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire - - name: Download stable Nanopb - id: download-nanopb - continue-on-error: true - run: | - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - - if: ${{ failure() || steps.download-nanopb.outcome != 'success' }} - name: Restore cached nanopb - id: cache-nanopb-restore - uses: actions/cache/restore@v4 - env: - cache-name: cache-node-modules - with: - path: ./nanopb-0.4.8.tar.gz - key: nanopb-0.4.8.tar.gz - - if: ${{ steps.download-nanopb.outcome == 'success' }} - name: Save nanopb to cache - id: cache-nanopb-save - uses: actions/cache/save@v4 - env: - cache-name: cache-node-modules - with: - path: ./nanopb-0.4.8.tar.gz - key: nanopb-0.4.8.tar.gz - - name: Install stable Nanopb - run: | - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: build SAMD (no-FS) platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - build-esp8266: name: 🏗️ESP8266 runs-on: ubuntu-latest @@ -654,8 +591,8 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino library run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - name: Download stable Nanopb id: download-nanopb continue-on-error: true @@ -742,7 +679,7 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 @@ -843,7 +780,7 @@ jobs: run: bash ci/actions_install.sh - name: Install extra Arduino libraries run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - name: Download stable Nanopb id: download-nanopb @@ -942,7 +879,7 @@ jobs: merge-job-build-files: name: Merge Artifacts for build-files runs-on: ubuntu-latest - needs: [build-esp32sx-esptool, build-esp32sx, build-esp32, build-esp8266, build-samd, build-rp2040, build-samd-non-fs] + needs: [build-esp32sx-esptool, build-esp32sx, build-esp32, build-esp8266, build-samd, build-rp2040] steps: - name: Merge Artifacts from Builds @@ -974,7 +911,6 @@ jobs: build-esp32, build-esp32sx, build-esp8266, - build-samd-non-fs, build-rp2040, ] steps: @@ -992,7 +928,7 @@ jobs: run: bash ci/actions_install.sh - name: clang - run: python3 ci/run-clang-format.py -r -e "ci/*" -e "bin/*" -e src/nanopb -e src/wippersnapper -e src/pb.h -e src/provisioning/tinyusb src/ + run: python3 ci/run-clang-format.py -r -e "ci/*" -e "bin/*" -e src/nanopb -e src/protos -e src/wippersnapper -e src/pb.h -e src/provisioning/tinyusb src/ - name: doxygen env: diff --git a/.gitignore b/.gitignore index b21993810..c8c95f2de 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,6 @@ examples/Wippersnapper_demo_offline/build/ report.xml # VSCode settings -.vscode/settings.json \ No newline at end of file +.vscode/settings.json + +CLAUDE.md \ No newline at end of file diff --git a/Doxyfile b/Doxyfile index 199b283ea..d56896e55 100644 --- a/Doxyfile +++ b/Doxyfile @@ -90,7 +90,7 @@ CREATE_SUBDIRS = NO # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. -CREATE_SUBDIRS_LEVEL = 8 +# CREATE_SUBDIRS_LEVEL = 8 # Unsupported by current Doxygen version # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII @@ -219,7 +219,7 @@ JAVADOC_AUTOBRIEF = NO # interpreted by doxygen. # The default value is: NO. -JAVADOC_BANNER = NO +# JAVADOC_BANNER = NO # Unsupported by current Doxygen version # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If @@ -247,7 +247,7 @@ MULTILINE_CPP_IS_BRIEF = NO # documentation blocks is shown as doxygen documentation. # The default value is: YES. -PYTHON_DOCSTRING = YES +# PYTHON_DOCSTRING = YES # Unsupported by current Doxygen version # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. @@ -319,7 +319,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # separated into more groups, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_SLICE = NO +# OPTIMIZE_OUTPUT_SLICE = NO # Unsupported by current Doxygen version # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given @@ -372,7 +372,7 @@ TOC_INCLUDE_HEADINGS = 0 # The default value is: DOXYGEN. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. -MARKDOWN_ID_STYLE = DOXYGEN +# MARKDOWN_ID_STYLE = DOXYGEN # Unsupported by current Doxygen version # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can @@ -496,7 +496,7 @@ LOOKUP_CACHE_SIZE = 0 # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. -NUM_PROC_THREADS = 1 +# NUM_PROC_THREADS = 1 # Unsupported by current Doxygen version # If the TIMESTAMP tag is set different from NO then each generated page will # contain the date or date and time when the page was generated. Setting this to @@ -504,7 +504,7 @@ NUM_PROC_THREADS = 1 # Possible values are: YES, NO, DATETIME and DATE. # The default value is: NO. -TIMESTAMP = NO +# TIMESTAMP = NO # Unsupported by current Doxygen version #--------------------------------------------------------------------------- # Build related configuration options @@ -530,7 +530,7 @@ EXTRACT_PRIVATE = NO # methods of a class will be included in the documentation. # The default value is: NO. -EXTRACT_PRIV_VIRTUAL = NO +# EXTRACT_PRIV_VIRTUAL = NO # Unsupported by current Doxygen version # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. @@ -574,7 +574,7 @@ EXTRACT_ANON_NSPACES = NO # parameters remain unnamed in the output. # The default value is: YES. -RESOLVE_UNNAMED_PARAMS = YES +# RESOLVE_UNNAMED_PARAMS = YES # Unsupported by current Doxygen version # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these @@ -649,7 +649,7 @@ HIDE_COMPOUND_REFERENCE= NO # will show which file needs to be included to use the class. # The default value is: YES. -SHOW_HEADERFILE = YES +# SHOW_HEADERFILE = YES # Unsupported by current Doxygen version # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. @@ -867,7 +867,7 @@ WARN_IF_DOC_ERROR = YES # parameters have no documentation without warning. # The default value is: YES. -WARN_IF_INCOMPLETE_DOC = YES +# WARN_IF_INCOMPLETE_DOC = YES # Unsupported by current Doxygen version # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return @@ -885,7 +885,7 @@ WARN_NO_PARAMDOC = YES # will automatically be disabled. # The default value is: NO. -WARN_IF_UNDOC_ENUM_VAL = NO +# WARN_IF_UNDOC_ENUM_VAL = NO # Unsupported by current Doxygen version # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS @@ -922,7 +922,7 @@ WARN_FORMAT = "$file:$line: $text" # See also: WARN_FORMAT # The default value is: at line $line of file $file. -WARN_LINE_FORMAT = "at line $line of file $file" +# WARN_LINE_FORMAT = "at line $line of file $file" # Unsupported by current Doxygen version # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard @@ -963,7 +963,7 @@ INPUT_ENCODING = UTF-8 # form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding # "INPUT_ENCODING" for further information on supported encodings. -INPUT_FILE_ENCODING = +# INPUT_FILE_ENCODING = # Unsupported by current Doxygen version # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and @@ -1134,7 +1134,7 @@ USE_MDFILE_AS_MAINPAGE = # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. -FORTRAN_COMMENT_AFTER = 72 +# FORTRAN_COMMENT_AFTER = 72 # Unsupported by current Doxygen version #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1347,7 +1347,7 @@ HTML_EXTRA_FILES = # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE = AUTO_LIGHT +# HTML_COLORSTYLE = AUTO_LIGHT # Unsupported by current Doxygen version # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to @@ -1388,7 +1388,7 @@ HTML_COLORSTYLE_GAMMA = 80 # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_DYNAMIC_MENUS = YES +# HTML_DYNAMIC_MENUS = YES # Unsupported by current Doxygen version # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the @@ -1403,7 +1403,7 @@ HTML_DYNAMIC_SECTIONS = NO # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_CODE_FOLDING = YES +# HTML_CODE_FOLDING = YES # Unsupported by current Doxygen version # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand @@ -1446,7 +1446,7 @@ DOCSET_FEEDNAME = "Doxygen generated docs" # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. -DOCSET_FEEDURL = +# DOCSET_FEEDURL = # Unsupported by current Doxygen version # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. @@ -1543,7 +1543,7 @@ TOC_EXPAND = NO # protocol see https://www.sitemaps.org # This tag requires that the tag GENERATE_HTML is set to YES. -SITEMAP_URL = +# SITEMAP_URL = # Unsupported by current Doxygen version # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that @@ -1667,7 +1667,7 @@ GENERATE_TREEVIEW = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -FULL_SIDEBAR = NO +# FULL_SIDEBAR = NO # Unsupported by current Doxygen version # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. @@ -1698,7 +1698,7 @@ EXT_LINKS_IN_WINDOW = NO # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. -OBFUSCATE_EMAILS = YES +# OBFUSCATE_EMAILS = YES # Unsupported by current Doxygen version # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see @@ -1709,7 +1709,7 @@ OBFUSCATE_EMAILS = YES # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FORMULA_FORMAT = png +# HTML_FORMULA_FORMAT = png # Unsupported by current Doxygen version # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful @@ -1724,7 +1724,7 @@ FORMULA_FONTSIZE = 10 # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. -FORMULA_MACROFILE = +# FORMULA_MACROFILE = # Unsupported by current Doxygen version # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering @@ -1746,7 +1746,7 @@ USE_MATHJAX = NO # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_VERSION = MathJax_2 +# MATHJAX_VERSION = MathJax_2 # Unsupported by current Doxygen version # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax @@ -1935,7 +1935,7 @@ MAKEINDEX_CMD_NAME = makeindex # The default value is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_MAKEINDEX_CMD = makeindex +# LATEX_MAKEINDEX_CMD = makeindex # Unsupported by current Doxygen version # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some @@ -2069,7 +2069,7 @@ LATEX_BIB_STYLE = plain # LATEX_OUTPUT directory will be used. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EMOJI_DIRECTORY = +# LATEX_EMOJI_DIRECTORY = # Unsupported by current Doxygen version #--------------------------------------------------------------------------- # Configuration options related to the RTF output @@ -2203,7 +2203,7 @@ XML_PROGRAMLISTING = YES # The default value is: NO. # This tag requires that the tag GENERATE_XML is set to YES. -XML_NS_MEMB_FILE_SCOPE = NO +# XML_NS_MEMB_FILE_SCOPE = NO # Unsupported by current Doxygen version #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output @@ -2243,7 +2243,7 @@ GENERATE_AUTOGEN_DEF = NO # database with symbols found by doxygen stored in tables. # The default value is: NO. -GENERATE_SQLITE3 = NO +# GENERATE_SQLITE3 = NO # Unsupported by current Doxygen version # The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be # put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put @@ -2251,7 +2251,7 @@ GENERATE_SQLITE3 = NO # The default directory is: sqlite3. # This tag requires that the tag GENERATE_SQLITE3 is set to YES. -SQLITE3_OUTPUT = sqlite3 +# SQLITE3_OUTPUT = sqlite3 # Unsupported by current Doxygen version # The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db # database file will be recreated with each doxygen run. If set to NO, doxygen @@ -2259,7 +2259,7 @@ SQLITE3_OUTPUT = sqlite3 # The default value is: YES. # This tag requires that the tag GENERATE_SQLITE3 is set to YES. -SQLITE3_RECREATE_DB = YES +# SQLITE3_RECREATE_DB = YES # Unsupported by current Doxygen version #--------------------------------------------------------------------------- # Configuration options related to the Perl module output @@ -2466,7 +2466,7 @@ DOT_NUM_THREADS = 0 # The default value is: fontname=Helvetica,fontsize=10. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" +# DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" # Unsupported by current Doxygen version # DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. -o . The external tool should support # output file formats "png", "eps", "svg", and "ismap". -MSCGEN_TOOL = +# MSCGEN_TOOL = # Unsupported by current Doxygen version # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile diff --git a/examples/Wippersnapper_demo/.picow_rp2040_tinyusb.test.skip b/examples/Wippersnapper_NoFS/.picow_rp2350_tinyusb.test.skip similarity index 100% rename from examples/Wippersnapper_demo/.picow_rp2040_tinyusb.test.skip rename to examples/Wippersnapper_NoFS/.picow_rp2350_tinyusb.test.skip diff --git a/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.qtpy_esp32.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6_debug.test.skip similarity index 100% rename from examples/Wippersnapper_demo/.qtpy_esp32.test.skip rename to examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6_debug.test.skip diff --git a/examples/wippersnapper_debug/.feather_esp32s3_reverse_tft_dev.generate b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip similarity index 100% rename from examples/wippersnapper_debug/.feather_esp32s3_reverse_tft_dev.generate rename to examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip diff --git a/examples/Wippersnapper_NoFS/Wippersnapper_NoFS.ino b/examples/Wippersnapper_NoFS/Wippersnapper_NoFS.ino index aa17f8133..dabf68632 100644 --- a/examples/Wippersnapper_NoFS/Wippersnapper_NoFS.ino +++ b/examples/Wippersnapper_NoFS/Wippersnapper_NoFS.ino @@ -1,36 +1,31 @@ -// Adafruit IO WipperSnapper Beta -// -// -// NOTE: This software is a BETA release and in active development. -// Please report bugs or errors to -// https://github.com/adafruit/Adafruit_Wippersnapper_Arduino/issues +// Adafruit IO WipperSnapper // // This sketch is for devices which lack USB-MSD or LittleFS support such -// as the Arduino MKR WiFi 1010, Arduino Nano 33 IoT. +// as the ESP32Dev for Wokwi Simulator // // Adafruit invests time and resources providing this open source code. // Please support Adafruit and open source hardware by purchasing // products from Adafruit! // -// Brent Rubell for Adafruit Industries, 2021 +// Brent Rubell for Adafruit Industries, 2025 // // All text above must be included in any redistribution. -#include "Wippersnapper_Networking.h" - +#include "ws_adapters.h" +#define WS_DEBUG // Enable debug output /************************ Adafruit IO Config *******************************/ // Visit io.adafruit.com if you need to create an account, // or if you need your Adafruit IO key. #define IO_USERNAME "YOUR_AIO_USERNAME" #define IO_KEY "YOUR_AIO_KEY" - +#define IO_URL "io.adafruit.com" +#define IO_PORT 8883 /**************************** WiFi Config ***********************************/ #define WIFI_SSID "YOUR_WIFI_SSID" #define WIFI_PASS "YOUR_WIFI_PASSWORD" -#include "Wippersnapper_Networking.h" -ws_adapter_wifi wipper(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); +ws_adapter_wifi wipper(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS, IO_URL, IO_PORT); void setup() { // Provisioning must occur prior to serial init. diff --git a/examples/Wippersnapper_NoFS/picow_rp2350_tinyusb.test.skip b/examples/Wippersnapper_NoFS/picow_rp2350_tinyusb.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.feather_esp32c6.generate b/examples/Wippersnapper_demo/.feather_esp32c6.generate new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.picow_rp2040_tinyusb.generate b/examples/Wippersnapper_demo/.picow_rp2040_tinyusb.generate new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.picow_rp2350.generate b/examples/Wippersnapper_demo/.picow_rp2350.generate new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.picow_rp2350_tinyusb.generate b/examples/Wippersnapper_demo/.picow_rp2350_tinyusb.generate new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.qtpy_esp32.generate b/examples/Wippersnapper_demo/.qtpy_esp32.generate new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo_offline_netiface/.picow_rp2350_tinyusb.test.skip b/examples/Wippersnapper_demo_offline_netiface/.picow_rp2350_tinyusb.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo_offline_netiface/.picow_rp2350_tinyusb.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32c6.test.skip b/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32c6.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32c6_debug.test.skip b/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32c6_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32c6_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip b/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo_offline_netiface/.wippersnapper_feather_esp32s3_reverse_tft_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo_offline_netiface/Wippersnapper_demo_offline_netiface.ino b/examples/Wippersnapper_demo_offline_netiface/Wippersnapper_demo_offline_netiface.ino index 7acf22341..bae08fb25 100644 --- a/examples/Wippersnapper_demo_offline_netiface/Wippersnapper_demo_offline_netiface.ino +++ b/examples/Wippersnapper_demo_offline_netiface/Wippersnapper_demo_offline_netiface.ino @@ -1,5 +1,5 @@ -// Adafruit IO WipperSnapper -// USE ONLY WITH DEVICES WITHOUT A NETWORK ADAPTER LIKE RP2040 PICO +// Adafruit IO WipperSnapper - Offline Mode with Network Interface +// USE ONLY WITH DEVICES WITH A NETWORK ADAPTER LIKE ESP32-x // // Adafruit invests time and resources providing this open source code. // Please support Adafruit and open source hardware by purchasing diff --git a/examples/Wippersnapper_demo_offline_netiface/picow_rp2350_tinyusb.test.skip b/examples/Wippersnapper_demo_offline_netiface/picow_rp2350_tinyusb.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/examples/Wippersnapper_demo_offline_nonetiface/Wippersnapper_demo_offline_nonetiface.ino b/examples/Wippersnapper_demo_offline_nonetiface/Wippersnapper_demo_offline_nonetiface.ino index 8ddacaa21..51028f92e 100644 --- a/examples/Wippersnapper_demo_offline_nonetiface/Wippersnapper_demo_offline_nonetiface.ino +++ b/examples/Wippersnapper_demo_offline_nonetiface/Wippersnapper_demo_offline_nonetiface.ino @@ -1,5 +1,5 @@ -// Adafruit IO WipperSnapper - Offline Mode -// USE ONLY WITH DEVICES WITH A NETWORK ADAPTER LIKE ESP32-x +// Adafruit IO WipperSnapper - Offline Mode without Network Interface +// USE ONLY WITH DEVICES WITHOUT A NETWORK ADAPTER LIKE RP2040 PICO // // Adafruit invests time and resources providing this open source code. // Please support Adafruit and open source hardware by purchasing diff --git a/examples/wippersnapper_debug/.feather_esp32s3_reverse_tft_debug.generate b/examples/wippersnapper_debug/.feather_esp32s3_reverse_tft_debug.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.feather_esp32s3_reverse_tft_debug.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.picow_rp2350_tinyusb.test.skip b/examples/wippersnapper_debug/.picow_rp2350_tinyusb.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.picow_rp2350_tinyusb.test.skip @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6.test.skip b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32s3_reverse_tft_debug.generate b/examples/wippersnapper_debug/.wippersnapper_feather_esp32s3_reverse_tft_debug.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_feather_esp32s3_reverse_tft_debug.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/picow_rp2350_tinyusb.test.skip b/examples/wippersnapper_debug/picow_rp2350_tinyusb.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/library.properties b/library.properties index 53a75ad48..810670201 100644 --- a/library.properties +++ b/library.properties @@ -7,4 +7,5 @@ paragraph=Arduino application for Adafruit.io WipperSnapper category=Communication url=https://github.com/adafruit/Adafruit_Wippersnapper_Arduino architectures=* -depends=SdFat - Adafruit Fork, Adafruit SPIFlash, Adafruit NeoPixel, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit INA260 Library, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, Sensirion I2C SEN66, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS28, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork, Adafruit BusIO, Adafruit Unified Sensor, Sensirion Core, Adafruit GFX Library, RTClib, StreamUtils, Adafruit SHT4x Library +depends=SdFat - Adafruit Fork, Adafruit SPIFlash, Adafruit NeoPixel, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit INA260 Library, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, Sensirion I2C SEN66, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VCNL4200 Library, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS28, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork, Adafruit BusIO, Adafruit Unified Sensor, Sensirion Core, Adafruit GFX Library, RTClib, StreamUtils, Adafruit SHT4x Library, Adafruit GPS Library, Adafruit uBlox, Adafruit LED Backpack Library, Adafruit LiquidCrystal + diff --git a/platformio.ini b/platformio.ini index 4087e388a..c3c5fd565 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,6 +17,14 @@ monitor_speed = 115200 extra_scripts = upload_no_build.py lib_compat_mode = strict lib_deps = + ;;;;;;;;;;; FunHouse / LVGL Boards uncomment these ;;;;;;;;;;;;;; + ; https://github.com/adafruit/Adafruit_HX8357_Library.git + ; https://github.com/adafruit/Adafruit_ILI9341.git + ; https://github.com/adafruit/Adafruit_STMPE610.git + ; https://github.com/adafruit/Adafruit-ST7735-Library.git + ; https://github.com/adafruit/Adafruit_TouchScreen.git + ; https://github.com/brentru/lvgl.git#wippersnapper + ; https://github.com/brentru/Adafruit_LvGL_Glue.git#development adafruit/Adafruit TinyUSB Library adafruit/Adafruit Zero DMA Library adafruit/Adafruit SPIFlash @@ -74,6 +82,9 @@ lib_deps = adafruit/Adafruit STMPE610 adafruit/Adafruit TouchScreen adafruit/Adafruit MQTT Library + adafruit/Adafruit LED Backpack Library + adafruit/Adafruit LiquidCrystal + adafruit/Adafruit SSD1306 https://github.com/adafruit/Adafruit_SHT4X.git bblanchon/ArduinoJson https://github.com/adafruit/SdFat.git @@ -86,6 +97,8 @@ lib_deps = https://github.com/adafruit/RTClib.git https://github.com/bblanchon/ArduinoStreamUtils.git https://github.com/Sensirion/arduino-i2c-scd4x.git + adafruit/Adafruit GPS Library + adafruit/Adafruit uBlox ; Common build environment for ESP32 platform [common:esp32] diff --git a/src/Wippersnapper_Boards.h b/src/Wippersnapper_Boards.h index 848505401..0797d37e8 100644 --- a/src/Wippersnapper_Boards.h +++ b/src/Wippersnapper_Boards.h @@ -217,8 +217,12 @@ #define BOARD_ID "rpi-pico-w" #define USE_TINYUSB #define USE_STATUS_LED -#define STATUS_LED_PIN 32 -#define SD_CS_PIN 17 +#define STATUS_LED_PIN 64 +#elif defined(ARDUINO_RASPBERRY_PI_PICO_2W) +#define BOARD_ID "rpi-pico-2w" +#define USE_TINYUSB +#define USE_STATUS_LED +#define STATUS_LED_PIN 64 #elif defined(ARDUINO_RASPBERRY_PI_PICO) #define BOARD_ID "rpi-pico" #define USE_TINYUSB @@ -247,6 +251,12 @@ #define STATUS_NEOPIXEL_NUM NUM_NEOPIXEL #define SD_USE_SPI_1 #define SD_CS_PIN 39 +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) +#define BOARD_ID "feather-esp32c6" +#define USE_LITTLEFS +#define USE_STATUS_NEOPIXEL +#define STATUS_NEOPIXEL_PIN PIN_NEOPIXEL +#define STATUS_NEOPIXEL_NUM 1 #else #warning "Board type not identified within Wippersnapper_Boards.h!" #endif diff --git a/src/Wippersnapper_V2.cpp b/src/Wippersnapper_V2.cpp index 4c363c7d9..9bf541f58 100644 --- a/src/Wippersnapper_V2.cpp +++ b/src/Wippersnapper_V2.cpp @@ -53,22 +53,23 @@ Wippersnapper_V2::Wippersnapper_V2() { WsV2.digital_io_controller = new DigitalIOController(); WsV2.analogio_controller = new AnalogIOController(); WsV2._ds18x20_controller = new DS18X20Controller(); + WsV2._gps_controller = new GPSController(); WsV2._i2c_controller = new I2cController(); + WsV2._uart_controller = new UARTController(); + WsV2._pixels_controller = new PixelsController(); + WsV2._pwm_controller = new PWMController(); + WsV2._servo_controller = new ServoController(); }; -/**************************************************************************/ /*! @brief Wippersnapper_V2 destructor */ -/**************************************************************************/ Wippersnapper_V2::~Wippersnapper_V2() {} -/**************************************************************************/ /*! @brief Provisions a WipperSnapper device with its network configuration and Adafruit IO credentials. */ -/**************************************************************************/ void Wippersnapper_V2::provision() { // Obtain device's MAC address getMacAddr(); @@ -85,9 +86,9 @@ void Wippersnapper_V2::provision() { // Determine if app is in SDLogger mode #ifdef USE_TINYUSB - WsV2._fileSystemV2->GetPinSDCS(); + WsV2._fileSystemV2->GetSDCSPin(); #elif defined(USE_LITTLEFS) - WsV2._littleFSV2->GetPinSDCS(); + WsV2._littleFSV2->GetSDCSPin(); #elif defined(OFFLINE_MODE_WOKWI) WsV2.pin_sd_cs = 15; #endif @@ -96,37 +97,38 @@ void Wippersnapper_V2::provision() { if (WsV2._sdCardV2->isSDCardInitialized()) { return; // SD card initialized, cede control back to loop() } else { - haltErrorV2("SD initialization failed.\nDo not reformat the card!\nIs the " - "card correctly inserted?\nIs there a wiring/soldering " - "problem\nIs the config.json file malformed?\nSD CS Pin: " + - String(WsV2.pin_sd_cs)); + haltErrorV2(("SD initialization failed.\nDo not reformat the card!\nIs the " + "card correctly inserted?\nIs there a wiring/soldering " + "problem\nIs the config.json file malformed?\nSD CS Pin: " + + String(WsV2.pin_sd_cs)) + .c_str()); } #ifdef USE_DISPLAY // Initialize the display displayConfig config; - WsV2._fileSystemV2->ParseFileDisplayCfg(config); - WsV2._display = new ws_display_driver(config); + WsV2._fileSystemV2->parseDisplayConfig(config); + WsV2._displayV2 = new ws_display_driver(config); // Begin display - if (!WsV2._display->begin()) { + if (!WsV2._displayV2->begin()) { WS_DEBUG_PRINTLN("Unable to enable display driver and LVGL"); haltErrorV2("Unable to enable display driver, please check the json " "configuration!"); } - WsV2._display->enableLogging(); - releaseStatusLED(); // don't use status LED if we are using the display + WsV2._displayV2->enableLogging(); + ReleaseStatusPixel(); // don't use status LED if we are using the display // UI Setup - WsV2._ui_helper = new ws_display_ui_helper(WsV2._display); - WsV2._ui_helper->set_bg_black(); - WsV2._ui_helper->show_scr_load(); - WsV2._ui_helper->set_label_status("Validating Credentials..."); + WsV2._ui_helperV2 = new ws_display_ui_helper(WsV2._displayV2); + WsV2._ui_helperV2->set_bg_black(); + WsV2._ui_helperV2->show_scr_load(); + WsV2._ui_helperV2->set_label_status("Validating Credentials..."); #endif #ifdef USE_TINYUSB - WsV2._fileSystemV2->ParseFileSecrets(); + WsV2._fileSystemV2->parseSecrets(); #elif defined(USE_LITTLEFS) - WsV2._littleFSV2->ParseFileSecrets(); + WsV2._littleFSV2->parseSecrets(); #else check_valid_ssid(); // non-fs-backed, sets global credentials within network // iface @@ -137,88 +139,73 @@ void Wippersnapper_V2::provision() { set_ssid_pass(); #ifdef USE_DISPLAY - WsV2._ui_helper->set_label_status(""); - WsV2._ui_helper->set_load_bar_icon_complete(loadBarIconFile); + WsV2._ui_helperV2->set_label_status(""); + WsV2._ui_helperV2->set_load_bar_icon_complete(loadBarIconFile); #endif } -/**************************************************************************/ /*! @brief Disconnects from Adafruit IO+ Wippersnapper_V2. */ -/**************************************************************************/ void Wippersnapper_V2::disconnect() { _disconnect(); } // Concrete class definition for abstract classes -/****************************************************************************/ /*! @brief Connects to wireless network. */ -/****************************************************************************/ void Wippersnapper_V2::_connect() { WS_DEBUG_PRINTLN("Wippersnapper_V2::_connect()"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Disconnect Wippersnapper MQTT session and network. */ -/****************************************************************************/ void Wippersnapper_V2::_disconnect() { WS_DEBUG_PRINTLN("WIppersnapper_V2::_disconnect"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Sets the network interface's unique identifer, typically the MAC address. */ -/****************************************************************************/ void Wippersnapper_V2::getMacAddr() { WS_DEBUG_PRINTLN("Wippersnapper_V2::getMacAddr"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Gets the network's RSSI. @return int32_t RSSI value, 0 to 255, in dB */ -/****************************************************************************/ int32_t Wippersnapper_V2::getRSSI() { WS_DEBUG_PRINTLN("Wiippersnapper_V2::getRSSI"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); return 0; } -/****************************************************************************/ /*! @brief Sets up the MQTT client session. @param clientID A unique client identifier string. */ -/****************************************************************************/ void Wippersnapper_V2::setupMQTTClient(const char * /*clientID*/) { WS_DEBUG_PRINTLN("Wippersnapper_V2::setupMQTTClient"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Returns the network's connection status @returns Network status as ws_status_t. */ -/****************************************************************************/ ws_status_t Wippersnapper_V2::networkStatus() { WS_DEBUG_PRINTLN("Wippersnapper_V2::networkStatus"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); return WS_IDLE; } -/****************************************************************************/ /*! @brief Sets the device's wireless network credentials. @param ssid @@ -226,49 +213,41 @@ ws_status_t Wippersnapper_V2::networkStatus() { @param ssidPassword Your wireless network's password. */ -/****************************************************************************/ void Wippersnapper_V2::set_ssid_pass(const char * /*ssid*/, const char * /*ssidPassword*/) { WS_DEBUG_PRINTLN("Wippersnapper_V2::set_ssid_pass"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Sets the device's wireless network credentials from the secrets.json configuration file. */ -/****************************************************************************/ void Wippersnapper_V2::set_ssid_pass() { WS_DEBUG_PRINTLN("Wippersnapper_V2::set_ssid_pass"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ -/***********************************************************/ bool Wippersnapper_V2::check_valid_ssid() { WS_DEBUG_PRINTLN("Wippersnapper_V2::check_valid_ssid"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); return false; } -/****************************************************************************/ /*! @brief Configures the device's Adafruit IO credentials. This method should be used only if filesystem-backed provisioning is not avaliable. */ -/****************************************************************************/ void Wippersnapper_V2::set_user_key() { WS_DEBUG_PRINTLN("Wippersnapper_V2::set_user_key"); WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } -/****************************************************************************/ /*! @brief Handles a Checkin Response message and initializes the device's GPIO classes. @@ -277,7 +256,6 @@ void Wippersnapper_V2::set_user_key() { @returns True if Checkin Response decoded and parsed successfully, False otherwise. */ -/****************************************************************************/ bool handleCheckinResponse(pb_istream_t *stream) { // Decode the Checkin Response message if (!WsV2.CheckInModel->DecodeCheckinResponse(stream)) { @@ -311,7 +289,6 @@ bool handleCheckinResponse(pb_istream_t *stream) { // Decoders // -/******************************************************************************************/ /*! @brief Decodes a BrokerToDevice message and executes the asscoiated callback. @@ -323,7 +300,6 @@ bool handleCheckinResponse(pb_istream_t *stream) { Optional arguments from decoder calling function. @returns True if decoded and executed successfully, False otherwise. */ -/******************************************************************************************/ bool cbDecodeBrokerToDevice(pb_istream_t *stream, const pb_field_t *field, void **arg) { (void)arg; // marking unused parameters to avoid compiler warning @@ -333,7 +309,6 @@ bool cbDecodeBrokerToDevice(pb_istream_t *stream, const pb_field_t *field, WS_DEBUG_PRINTLN("-> Checkin Response Message Type"); WS_DEBUG_PRINT("Handling Checkin Response..."); if (!handleCheckinResponse(stream)) { - WS_DEBUG_PRINTLN("Failure handling Checkin Response!"); return false; } WS_DEBUG_PRINTLN("Handled!"); @@ -341,76 +316,156 @@ bool cbDecodeBrokerToDevice(pb_istream_t *stream, const pb_field_t *field, case wippersnapper_signal_BrokerToDevice_digitalio_add_tag: WS_DEBUG_PRINTLN("-> DigitalIO Add Message Type"); if (!WsV2.digital_io_controller->Handle_DigitalIO_Add(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to add digitalio pin!"); return false; } break; case wippersnapper_signal_BrokerToDevice_digitalio_remove_tag: WS_DEBUG_PRINTLN("-> DigitalIO Remove Message Type"); if (!WsV2.digital_io_controller->Handle_DigitalIO_Remove(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to remove digitalio pin!"); return false; } break; case wippersnapper_signal_BrokerToDevice_digitalio_write_tag: WS_DEBUG_PRINTLN("-> DigitalIO Write Message Type"); if (!WsV2.digital_io_controller->Handle_DigitalIO_Write(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to write to digitalio pin!"); return false; } break; case wippersnapper_signal_BrokerToDevice_analogio_add_tag: WS_DEBUG_PRINTLN("-> AnalogIO Add Message Type"); if (!WsV2.analogio_controller->Handle_AnalogIOAdd(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to add analogio pin!"); return false; } break; case wippersnapper_signal_BrokerToDevice_analogio_remove_tag: WS_DEBUG_PRINTLN("-> AnalogIO Remove Message Type"); if (!WsV2.analogio_controller->Handle_AnalogIORemove(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to remove analogio pin!"); return false; } break; case wippersnapper_signal_BrokerToDevice_ds18x20_add_tag: WS_DEBUG_PRINTLN("-> DS18X20 Add Message Type"); if (!WsV2._ds18x20_controller->Handle_Ds18x20Add(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to add DS18X20 sensor!"); return false; } break; case wippersnapper_signal_BrokerToDevice_ds18x20_remove_tag: WS_DEBUG_PRINTLN("-> DS18X20 Remove Message Type"); if (!WsV2._ds18x20_controller->Handle_Ds18x20Remove(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to remove DS18X20 sensor!"); return false; } break; case wippersnapper_signal_BrokerToDevice_i2c_device_add_replace_tag: WS_DEBUG_PRINTLN("-> I2C Device Add/Replace Message Type"); if (!WsV2._i2c_controller->Handle_I2cDeviceAddOrReplace(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to add/replace I2C device!"); + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_i2c_bus_scan_tag: + WS_DEBUG_PRINTLN("-> I2C Bus Scan Message Type"); + if (!WsV2._i2c_controller->Handle_I2cBusScan(stream)) { return false; } break; case wippersnapper_signal_BrokerToDevice_i2c_device_remove_tag: WS_DEBUG_PRINTLN("-> I2C Device Remove Message Type"); if (!WsV2._i2c_controller->Handle_I2cDeviceRemove(stream)) { - WS_DEBUG_PRINTLN("ERROR: Unable to remove I2C device!"); return false; } + break; + case wippersnapper_signal_BrokerToDevice_i2c_device_output_write_tag: + WS_DEBUG_PRINTLN("-> I2C Device Output Write Message Type"); + if (!WsV2._i2c_controller->Handle_I2cDeviceOutputWrite(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pixels_add_tag: + WS_DEBUG_PRINTLN("-> Pixels Add Message Type"); + if (!WsV2._pixels_controller->Handle_Pixels_Add(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pixels_remove_tag: + WS_DEBUG_PRINTLN("-> Pixels Remove Message Type"); + if (!WsV2._pixels_controller->Handle_Pixels_Remove(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pixels_write_tag: + WS_DEBUG_PRINTLN("-> Pixels Write Message Type"); + if (!WsV2._pixels_controller->Handle_Pixels_Write(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pwm_add_tag: + WS_DEBUG_PRINTLN("-> PWM Add Message Type"); + if (!WsV2._pwm_controller->Handle_PWM_Add(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pwm_write_duty_tag: + WS_DEBUG_PRINTLN("-> PWM Write Duty Cycle Message Type"); + if (!WsV2._pwm_controller->Handle_PWM_Write_DutyCycle(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pwm_write_freq_tag: + WS_DEBUG_PRINTLN("-> PWM Write Frequency Message Type"); + if (!WsV2._pwm_controller->Handle_PWM_Write_Frequency(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_pwm_remove_tag: + WS_DEBUG_PRINTLN("-> PWM Remove Message Type"); + if (!WsV2._pwm_controller->Handle_PWM_Remove(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_servo_add_tag: + WS_DEBUG_PRINTLN("-> Servo Add Message Type"); + if (!WsV2._servo_controller->Handle_Servo_Add(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_servo_write_tag: + WS_DEBUG_PRINTLN("-> Servo Write Message Type"); + if (!WsV2._servo_controller->Handle_Servo_Write(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_servo_remove_tag: + WS_DEBUG_PRINTLN("-> Servo Remove Message Type"); + if (!WsV2._servo_controller->Handle_Servo_Remove(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_uart_add_tag: + WS_DEBUG_PRINTLN("-> UART Add Message Type"); + if (!WsV2._uart_controller->Handle_UartAdd(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_uart_remove_tag: + WS_DEBUG_PRINTLN("-> UART Remove Message Type"); + if (!WsV2._uart_controller->Handle_UartRemove(stream)) { + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_uart_write_tag: + WS_DEBUG_PRINTLN("-> UART Write Message Type"); + if (!WsV2._uart_controller->Handle_UartWrite(stream)) { + return false; + } + break; default: WS_DEBUG_PRINTLN("ERROR: BrokerToDevice message type not found!"); return false; } - // once this is returned, pb_dec_submessage() // decodes the submessage contents. return true; } -/**************************************************************************/ /*! @brief Called when client receives a message published across the Adafruit IO MQTT /ws-b2d/ "signal topic". @@ -419,7 +474,6 @@ bool cbDecodeBrokerToDevice(pb_istream_t *stream, const pb_field_t *field, @param len Length of data received from MQTT broker. */ -/**************************************************************************/ void cbBrokerToDevice(char *data, uint16_t len) { WS_DEBUG_PRINTLN("=> New B2D message!"); wippersnapper_signal_BrokerToDevice msg_signal = @@ -440,12 +494,10 @@ void cbBrokerToDevice(char *data, uint16_t len) { WS_DEBUG_PRINTLN("Decoded BrokerToDevice message!"); } -/**************************************************************************/ /*! @brief Decodes and parses a buffer containing configuration messages from the SD card. */ -/**************************************************************************/ void callDecodeB2D() { for (size_t i = 0; i < WsV2._sharedConfigBuffers.size(); i++) { wippersnapper_signal_BrokerToDevice msg_signal = @@ -463,7 +515,6 @@ void callDecodeB2D() { } } -/**************************************************************************/ /*! @brief Called when client receives a message published across the Adafruit IO MQTT /error special topic. @@ -472,7 +523,6 @@ void callDecodeB2D() { @param len Length of data received from MQTT broker. */ -/**************************************************************************/ void cbErrorTopicV2(char *errorData, uint16_t len) { (void)len; // marking unused parameter to avoid compiler warning WS_DEBUG_PRINT("IO Ban Error: "); @@ -484,14 +534,13 @@ void cbErrorTopicV2(char *errorData, uint16_t len) { } #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error("IO Ban Error", errorData); + WsV2._ui_helperV2->show_scr_error("IO Ban Error", errorData); #endif // WDT reset WsV2.haltErrorV2("IO MQTT Ban Error"); } -/**************************************************************************/ /*! @brief Called when client receives a message published across the Adafruit IO MQTT /throttle special topic. Delays until @@ -501,7 +550,6 @@ void cbErrorTopicV2(char *errorData, uint16_t len) { @param len Length of data received from MQTT broker. */ -/**************************************************************************/ void cbThrottleTopicV2(char *throttleData, uint16_t len) { (void)len; // marking unused parameter to avoid compiler warning WS_DEBUG_PRINT("IO Throttle Error: "); @@ -523,7 +571,7 @@ void cbThrottleTopicV2(char *throttleData, uint16_t len) { buffer, 100, "[IO ERROR] Device is throttled for %d mS and blocking execution..\n.", throttleDuration); - WsV2._ui_helper->add_text_to_terminal(buffer); + WsV2._ui_helperV2->add_text_to_terminal(buffer); #endif // If throttle duration is less than the keepalive interval, delay for the @@ -543,18 +591,16 @@ void cbThrottleTopicV2(char *throttleData, uint16_t len) { } WS_DEBUG_PRINTLN("Device is un-throttled, resumed command execution"); #ifdef USE_DISPLAY - WsV2._ui_helper->add_text_to_terminal( + WsV2._ui_helperV2->add_text_to_terminal( "[IO] Device is un-throttled, resuming...\n"); #endif } -/**************************************************************************/ /*! @brief Attempts to generate unique device identifier. @returns True if device identifier generated successfully, False otherwise. */ -/**************************************************************************/ bool Wippersnapper_V2::generateDeviceUID() { // Generate device unique identifier // Set machine_name @@ -598,14 +644,12 @@ bool Wippersnapper_V2::generateDeviceUID() { return true; } -/**************************************************************************/ /*! @brief Generates device-specific Wippersnapper control topics and subscribes to them. @returns True if memory for control topics allocated successfully, False otherwise. */ -/**************************************************************************/ bool Wippersnapper_V2::generateWSTopics() { WS_DEBUG_PRINTLN("Pre-calculating topic lengths..."); // Calculate length of strings that are are dynamic within the secrets file @@ -701,38 +745,34 @@ bool Wippersnapper_V2::generateWSTopics() { return true; } -/**************************************************************************/ /*! @brief Writes an error message to the serial and the filesystem, blinks WS_LED_STATUS_ERROR_RUNTIME pattern and hangs. @param error The error message to write to the serial and filesystem. */ -/**************************************************************************/ -void Wippersnapper_V2::errorWriteHangV2(String error) { +void Wippersnapper_V2::errorWriteHangV2(const char *error) { // Print error WS_DEBUG_PRINTLN(error); #ifdef USE_TINYUSB - WsV2._fileSystemV2->WriteFileBoot(error.c_str()); + _fileSystemV2->writeToBootOut(error); TinyUSBDevice.attach(); delay(500); #endif // Signal and hang forever while (1) { WS_DEBUG_PRINTLN("ERROR: Halted execution"); - WS_DEBUG_PRINTLN(error.c_str()); + WS_DEBUG_PRINTLN(error); WsV2.feedWDTV2(); statusLEDBlink(WS_LED_STATUS_ERROR_RUNTIME); delay(1000); } } -/**************************************************************************/ /*! @brief Checks network and MQTT connectivity. Handles network re-connection and mqtt re-establishment. */ -/**************************************************************************/ void Wippersnapper_V2::runNetFSMV2() { WsV2.feedWDTV2(); // Initial state @@ -753,8 +793,8 @@ void Wippersnapper_V2::runNetFSMV2() { if (networkStatus() == WS_NET_CONNECTED) { WS_DEBUG_PRINTLN("Connected to WiFi!"); #ifdef USE_DISPLAY - if (WsV2._ui_helper->getLoadingState()) - WsV2._ui_helper->set_load_bar_icon_complete(loadBarIconWifi); + if (WsV2._ui_helperV2->getLoadingState()) + WsV2._ui_helperV2->set_load_bar_icon_complete(loadBarIconWifi); #endif fsmNetwork = FSM_NET_ESTABLISH_MQTT; break; @@ -765,17 +805,17 @@ void Wippersnapper_V2::runNetFSMV2() { WS_DEBUG_PRINTLN("Establishing network connection..."); WS_PRINTER.flush(); #ifdef USE_DISPLAY - if (WsV2._ui_helper->getLoadingState()) - WsV2._ui_helper->set_label_status("Connecting to WiFi..."); + if (WsV2._ui_helperV2->getLoadingState()) + WsV2._ui_helperV2->set_label_status("Connecting to WiFi..."); #endif // Perform a WiFi scan and check if SSID within // secrets.json is within the scanned SSIDs WS_DEBUG_PRINT("Performing a WiFi scan for SSID..."); if (!check_valid_ssid()) { #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error("ERROR", - "Unable to find WiFi network listed in " - "the secrets file. Rebooting soon..."); + WsV2._ui_helperV2->show_scr_error( + "ERROR", "Unable to find WiFi network listed in " + "the secrets file. Rebooting soon..."); #endif haltErrorV2("ERROR: Unable to find WiFi network, rebooting soon...", WS_LED_STATUS_WIFI_CONNECTING); @@ -803,7 +843,7 @@ void Wippersnapper_V2::runNetFSMV2() { if (networkStatus() != WS_NET_CONNECTED) { WS_DEBUG_PRINTLN("ERROR: Unable to connect to WiFi!"); #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error( + WsV2._ui_helperV2->show_scr_error( "CONNECTION ERROR", "Unable to connect to WiFi Network. Please check that you entered " "the WiFi credentials correctly. Rebooting in 5 seconds..."); @@ -816,8 +856,8 @@ void Wippersnapper_V2::runNetFSMV2() { break; case FSM_NET_ESTABLISH_MQTT: #ifdef USE_DISPLAY - if (WsV2._ui_helper->getLoadingState()) - WsV2._ui_helper->set_label_status("Connecting to IO..."); + if (WsV2._ui_helperV2->getLoadingState()) + WsV2._ui_helperV2->set_label_status("Connecting to IO..."); #endif WsV2._mqttV2->setKeepAliveInterval(WS_KEEPALIVE_INTERVAL_MS / 1000); // Attempt to connect @@ -849,7 +889,7 @@ void Wippersnapper_V2::runNetFSMV2() { } if (fsmNetwork != FSM_NET_CHECK_MQTT) { #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error( + WsV2._ui_helperV2->show_scr_error( "CONNECTION ERROR", "Unable to connect to Adafruit.io. If you are repeatedly having " "this issue, please check that your IO Username and IO Key are set " @@ -867,7 +907,6 @@ void Wippersnapper_V2::runNetFSMV2() { } } -/**************************************************************************/ /*! @brief Prints an error to the serial and halts the hardware until the WDT bites. @@ -880,15 +919,9 @@ void Wippersnapper_V2::runNetFSMV2() { If false, the device will not allow the WDT to bite and instead hang indefinitely, holding the WIPPER drive open */ -/**************************************************************************/ -void Wippersnapper_V2::haltErrorV2(String error, ws_led_status_t ledStatusColor, - bool reboot, bool reattach_usb_filesystem) { -#ifdef USE_TINYUSB - if (reattach_usb_filesystem) { - WsV2._fileSystemV2->InitUsbMsc(); - delay(1500); - } -#endif +void Wippersnapper_V2::haltErrorV2(const char *error, + ws_led_status_t ledStatusColor, + bool reboot) { WS_DEBUG_PRINT("ERROR "); if (reboot) { WS_DEBUG_PRINT("[RESET]: "); @@ -914,7 +947,6 @@ void Wippersnapper_V2::haltErrorV2(String error, ws_led_status_t ledStatusColor, } } -/**************************************************************************/ /*! @brief Publishes a signal message to the broker. @param which_payload @@ -924,7 +956,6 @@ void Wippersnapper_V2::haltErrorV2(String error, ws_led_status_t ledStatusColor, @returns True if the signal message published successfully, False otherwise. */ -/**************************************************************************/ bool Wippersnapper_V2::PublishSignal(pb_size_t which_payload, void *payload) { #ifdef DEBUG_PROFILE @@ -973,6 +1004,42 @@ bool Wippersnapper_V2::PublishSignal(pb_size_t which_payload, void *payload) { MsgSignal.payload.ds18x20_event = *(wippersnapper_ds18x20_Ds18x20Event *)payload; break; + case wippersnapper_signal_DeviceToBroker_pixels_added_tag: + WS_DEBUG_PRINTLN("PixelsAdded"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_pixels_added_tag; + MsgSignal.payload.pixels_added = + *(wippersnapper_pixels_PixelsAdded *)payload; + break; + case wippersnapper_signal_DeviceToBroker_pwm_added_tag: + WS_DEBUG_PRINTLN("PWMAdded"); + MsgSignal.which_payload = wippersnapper_signal_DeviceToBroker_pwm_added_tag; + MsgSignal.payload.pwm_added = *(wippersnapper_pwm_PWMAdded *)payload; + break; + case wippersnapper_signal_DeviceToBroker_servo_added_tag: + WS_DEBUG_PRINTLN("ServoAdded"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_servo_added_tag; + MsgSignal.payload.servo_added = *(wippersnapper_servo_ServoAdded *)payload; + break; + case wippersnapper_signal_DeviceToBroker_uart_added_tag: + WS_DEBUG_PRINTLN("UARTAdded"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_uart_added_tag; + MsgSignal.payload.uart_added = *(wippersnapper_uart_UartAdded *)payload; + break; + case wippersnapper_signal_DeviceToBroker_uart_input_event_tag: + WS_DEBUG_PRINTLN("UARTInputEvent"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_uart_input_event_tag; + MsgSignal.payload.uart_input_event = + *(wippersnapper_uart_UartInputEvent *)payload; + break; + case wippersnapper_signal_DeviceToBroker_gps_event_tag: + WS_DEBUG_PRINTLN("GPSEvent"); + MsgSignal.which_payload = wippersnapper_signal_DeviceToBroker_gps_event_tag; + MsgSignal.payload.gps_event = *(wippersnapper_gps_GPSEvent *)payload; + break; default: WS_DEBUG_PRINTLN("ERROR: Invalid signal payload type, bailing out!"); return false; @@ -1033,14 +1100,12 @@ bool Wippersnapper_V2::PublishSignal(pb_size_t which_payload, void *payload) { return true; } -/**************************************************************************/ /*! @brief Creates, fills, encodes and publishes a checkin request message to the broker. @returns True if the Checkin request message published successfully, False otherwise. */ -/**************************************************************************/ bool Wippersnapper_V2::CreateCheckinRequest() { WS_DEBUG_PRINT("Creating the CheckinRequest message..."); WsV2.CheckInModel = new CheckinModel(); @@ -1061,12 +1126,10 @@ bool Wippersnapper_V2::CreateCheckinRequest() { return true; } -/**************************************************************************/ /*! @brief Polls for and handles the checkin response message from the broker. */ -/**************************************************************************/ void Wippersnapper_V2::PollCheckinResponse() { WsV2.got_checkin_response = false; WS_DEBUG_PRINTLN("Waiting for checkin response..."); @@ -1079,13 +1142,11 @@ void Wippersnapper_V2::PollCheckinResponse() { WS_DEBUG_PRINTLN("Completed checkin process!"); } -/**************************************************************************/ /*! @brief Pings the MQTT broker within the keepalive interval to keep the connection alive. Blinks the keepalive LED every STATUS_LED_KAT_BLINK_TIME milliseconds. */ -/**************************************************************************/ void Wippersnapper_V2::pingBrokerV2() { // ping within keepalive-10% to keep connection open if (millis() > (_prv_pingV2 + (WS_KEEPALIVE_INTERVAL_MS - @@ -1094,12 +1155,12 @@ void Wippersnapper_V2::pingBrokerV2() { if (WsV2._mqttV2->ping()) { WS_DEBUG_PRINTLN("SUCCESS!"); #ifdef USE_DISPLAY - WsV2._ui_helper->add_text_to_terminal("[NET] Sent KeepAlive ping!\n"); + WsV2._ui_helperV2->add_text_to_terminal("[NET] Sent KeepAlive ping!\n"); #endif } else { WS_DEBUG_PRINTLN("FAILURE! Running network FSM..."); #ifdef USE_DISPLAY - WsV2._ui_helper->add_text_to_terminal( + WsV2._ui_helperV2->add_text_to_terminal( "[NET] EROR: Failed to send KeepAlive ping!\n"); #endif WsV2._mqttV2->disconnect(); @@ -1113,12 +1174,10 @@ void Wippersnapper_V2::pingBrokerV2() { BlinkKATStatus(); } -/**************************************************************************/ /*! @brief Blinks the status LED every STATUS_LED_KAT_BLINK_TIME milliseconds to indicate that the device is still alive. */ -/**************************************************************************/ void Wippersnapper_V2::BlinkKATStatus() { if (millis() > (_prvKATBlinkV2 + STATUS_LED_KAT_BLINK_TIME)) { statusLEDBlink(WS_LED_STATUS_KAT); @@ -1126,11 +1185,9 @@ void Wippersnapper_V2::BlinkKATStatus() { } } -/********************************************************/ /*! @brief Feeds the WDT to prevent hardware reset. */ -/*******************************************************/ void Wippersnapper_V2::feedWDTV2() { #ifndef OFFLINE_MODE_WOKWI // TODO: This is a temporary fix for watchdog.reset() not firing @@ -1139,14 +1196,12 @@ void Wippersnapper_V2::feedWDTV2() { #endif } -/********************************************************/ /*! @brief Enables the watchdog timer. @param timeoutMS The desired amount of time to elapse before the WDT executes. */ -/*******************************************************/ void Wippersnapper_V2::enableWDTV2(int timeoutMS) { #ifndef ARDUINO_ARCH_RP2040 Watchdog.disable(); @@ -1156,13 +1211,11 @@ void Wippersnapper_V2::enableWDTV2(int timeoutMS) { } } -/********************************************************/ /*! @brief Process all incoming packets from the Adafruit IO MQTT broker. Handles network connectivity. */ -/*******************************************************/ void Wippersnapper_V2::processPacketsV2() { // runNetFSMV2(); // NOTE: Removed for now, causes error with virtual // _connect() method when caused with WsV2 object in another file. @@ -1171,11 +1224,9 @@ void Wippersnapper_V2::processPacketsV2() { WsV2._mqttV2->processPackets(10); } -/**************************************************************************/ /*! @brief Prints information about the WsV2 device to the serial monitor. */ -/**************************************************************************/ void printDeviceInfoV2() { WS_DEBUG_PRINTLN("-------Device Information-------"); WS_DEBUG_PRINT("Firmware Version: "); @@ -1204,11 +1255,9 @@ void printDeviceInfoV2() { #endif } -/**************************************************************************/ /*! @brief Connects to Adafruit IO+ Wippersnapper_V2 broker. */ -/**************************************************************************/ void Wippersnapper_V2::connect() { WS_DEBUG_PRINTLN("Adafruit.io WipperSnapper"); // Dump device info to the serial monitor @@ -1277,8 +1326,8 @@ void Wippersnapper_V2::connect() { WsV2.feedWDTV2(); #ifdef USE_DISPLAY - WsV2._ui_helper->set_load_bar_icon_complete(loadBarIconCloud); - WsV2._ui_helper->set_label_status("Sending device info..."); + WsV2._ui_helperV2->set_load_bar_icon_complete(loadBarIconCloud); + WsV2._ui_helperV2->set_label_status("Sending device info..."); #endif WS_DEBUG_PRINTLN("Performing checkin handshake..."); @@ -1298,19 +1347,17 @@ void Wippersnapper_V2::connect() { // switch to monitor screen #ifdef USE_DISPLAY WS_DEBUG_PRINTLN("Clearing loading screen..."); - WsV2._ui_helper->clear_scr_load(); + WsV2._ui_helperV2->clear_scr_load(); WS_DEBUG_PRINTLN("building monitor screen..."); - WsV2._ui_helper->build_scr_monitor(); + WsV2._ui_helperV2->build_scr_monitor(); #endif WS_DEBUG_PRINTLN("Running app loop..."); } -/**************************************************************************/ /*! @brief Processes incoming commands and handles network connection. @returns Network status, as ws_status_t. */ -/**************************************************************************/ ws_status_t Wippersnapper_V2::run() { WsV2.feedWDTV2(); if (!WsV2._sdCardV2->isModeOffline()) { @@ -1332,10 +1379,14 @@ ws_status_t Wippersnapper_V2::run() { // Process all DS18x20 sensor events WsV2._ds18x20_controller->update(); - // TODO: Process I2C sensor events + // Process I2C driver events WsV2._i2c_controller->update(); - // TODO: Process UART sensor events + // Process UART driver events + WsV2._uart_controller->update(); + + // Process GPS controller events + WsV2._gps_controller->update(); return WS_NET_CONNECTED; // TODO: Make this funcn void! } diff --git a/src/Wippersnapper_V2.h b/src/Wippersnapper_V2.h index 5e5820fcc..97187cb0b 100644 --- a/src/Wippersnapper_V2.h +++ b/src/Wippersnapper_V2.h @@ -1,315 +1,317 @@ -/*! - * @file Wippersnapper_V2.h - * - * This is the documentation for Adafruit's Wippersnapper firmware for the - * Arduino platform. It is designed specifically to work with - * Adafruit IO Wippersnapper IoT platform. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * @copyright Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - */ - -#ifndef WIPPERSNAPPER_V2_H -#define WIPPERSNAPPER_V2_H - -// Debug Flags -// #DEBUG_PROFILE 1 ///< Enable debug output for function profiling -// Debug Flags -#define WS_DEBUG /**< Define to enable debugging to serial terminal */ -#define WS_PRINTER Serial /**< Where debug messages will be printed */ - -/**************************************************************************/ -/*! - @brief Debug print macros for WipperSnapper debugging output - @details These macros provide debug output functionality when WS_DEBUG is - defined -*/ -/**************************************************************************/ -#ifdef WS_DEBUG -#define WS_DEBUG_PRINT(...) \ - { WS_PRINTER.print(__VA_ARGS__); } /**< Print debug message to serial */ -#define WS_DEBUG_PRINTLN(...) \ - { \ - WS_PRINTER.println(__VA_ARGS__); \ - } /**< Print debug message with newline \ - */ -#define WS_DEBUG_PRINTHEX(...) \ - { \ - WS_PRINTER.print(__VA_ARGS__, HEX); \ - } /**< Print debug message in hexadecimal */ -#else -#define WS_DEBUG_PRINT(...) \ - {} /**< Debug print */ -#define WS_DEBUG_PRINTLN(...) \ - {} /**< Debug println */ -#endif - -/**************************************************************************/ -/*! - @brief delay() function for use with a watchdog timer - @param timeout - Delay duration in milliseconds -*/ -/**************************************************************************/ -#define WS_DELAY_WITH_WDT(timeout) \ - { \ - unsigned long start = millis(); \ - while (millis() - start < timeout) { \ - delay(10); \ - yield(); \ - feedWDT(); \ - if (millis() < start) { \ - start = millis(); \ - } \ - } \ - } - -// Cpp STD -#include -#include -#include -#include -#include - -// Nanopb dependencies -#include -#include -#include -#include -#include - -// Include Signal Proto -#include "protos/checkin.pb.h" -#include "protos/digitalio.pb.h" -#include "protos/ds18x20.pb.h" -#include "protos/signal.pb.h" - -// External libraries -#include "Adafruit_MQTT.h" // MQTT Client -#include "Adafruit_SleepyDog.h" // Watchdog -#include "Arduino.h" // Wiring -#include // SPI -#include // I2C - -// Wippersnapper API Helpers -#include "Wippersnapper_Boards.h" -#include "components/statusLED/Wippersnapper_StatusLED.h" -#include "helpers/ws_helper_status.h" -#ifdef ARDUINO_ARCH_ESP32 -#include "helpers/ws_helper_esp.h" -#endif - -// Components (API v2) -#include "components/analogIO/controller.h" -#include "components/checkin/model.h" -#include "components/digitalIO/controller.h" -#include "components/ds18x20/controller.h" -#include "components/i2c/controller.h" -#include "components/sensor/model.h" - -// Display -#ifdef USE_DISPLAY -#include "display/ws_display_driver.h" -#include "display/ws_display_ui_helper.h" -#endif - -#include "provisioning/ConfigJson.h" -#include "provisioning/sdcard/ws_sdcard.h" -#if defined(USE_TINYUSB) -#include "provisioning/tinyusb/Wippersnapper_FS.h" -#endif -#if defined(USE_LITTLEFS) -#include "provisioning/littlefs/WipperSnapper_LittleFS.h" -#endif - -#define WS_VERSION \ - "1.0.0-offline-beta.4" ///< WipperSnapper app. version - ///< (semver-formatted) - -#define WS_WDT_TIMEOUT 60000 ///< WDT timeout -#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks -/* MQTT Configuration */ -#define WS_KEEPALIVE_INTERVAL_MS \ - 5000 ///< Session keepalive interval time, in milliseconds - -// Forward declarations -class Wippersnapper_FS; -class WipperSnapper_LittleFS; -class ws_sdcard; -#ifdef USE_DISPLAY -class ws_display_driver; -class ws_display_ui_helper; -#endif -class CheckinModel; -class SensorModel; -class DigitalIOController; -class AnalogIOController; -class DS18X20Controller; -class I2cController; - -/**************************************************************************/ -/*! - @brief Class that provides storage and functions for the Adafruit IO - Wippersnapper interface. -*/ -/**************************************************************************/ -class Wippersnapper_V2 { -public: - Wippersnapper_V2(); - virtual ~Wippersnapper_V2(); - - void provision(); - - // Global flags for the status led - bool - lockStatusNeoPixelV2; ///< True if status LED is using the status neopixel - bool lockStatusDotStarV2; ///< True if status LED is using the status dotstar - bool lockStatusLEDV2; ///< True if status LED is using the built-in LED - float status_pixel_brightnessV2 = - STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness - ///< (from 0.0 to 1.0) - - virtual void set_user_key(); - virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); - virtual void set_ssid_pass(); - virtual bool check_valid_ssid(); - - virtual void _connect(); - virtual void _disconnect(); - void connect(); - void disconnect(); - - virtual void getMacAddr(); - virtual int32_t getRSSI(); - virtual void setupMQTTClient(const char *clientID); - - virtual ws_status_t networkStatus(); - - // Generators for device UID and MQTT topics - bool generateDeviceUID(); - bool generateWSTopics(); - - // High-level MQTT Publish - bool PublishSignal(pb_size_t which_payload, void *payload); - - // Checkin API - bool CreateCheckinRequest(); - void PollCheckinResponse(); - - // run() loop - ws_status_t run(); - void processPacketsV2(); - - // Networking helpers - void pingBrokerV2(); - void runNetFSMV2(); - - // WDT helpers - void enableWDTV2(int timeoutMS = 0); - void feedWDTV2(); - void BlinkKATStatus(); - - // Error handling helpers - void haltErrorV2(String error, - ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME, - bool reboot = true, bool reattach_usb_filesystem = true); - void errorWriteHangV2(String error); - - bool _is_offline_mode; ///< Global flag for if the device is in offline mode - - // TODO: Do we need this? - ws_board_status_t _boardStatusV2 = - WS_BOARD_DEF_IDLE; ///< Hardware's registration status - - // TODO: We really should look at making these static definitions, not dynamic - // to free up space on the heap - Wippersnapper_FS *_fileSystemV2; ///< Instance of Filesystem (native USB) - WipperSnapper_LittleFS - *_littleFSV2; ///< Instance of LittleFS Filesystem (non-native USB) - ws_sdcard *_sdCardV2; ///< Instance of SD card class -#ifdef USE_DISPLAY - ws_display_driver *_displayV2 = nullptr; ///< Instance of display driver class - ws_display_ui_helper *_ui_helperV2 = - nullptr; ///< Instance of display UI helper class -#endif - // ws_pixels *_ws_pixelsComponentV2; ///< ptr to instance of ws_pixels class - // ws_pwm *_pwmComponentV2; ///< Instance of pwm class - // ws_servo *_servoComponentV2; ///< Instance of servo class - // ws_uart *_uartComponentV2; ///< Instance of UART class - - // API v2 Components - CheckinModel *CheckInModel = nullptr; ///< Instance of CheckinModel class - SensorModel *sensorModel = nullptr; ///< Instance of SensorModel class - DigitalIOController *digital_io_controller = - nullptr; ///< Instance of DigitalIO controller class - AnalogIOController *analogio_controller = - nullptr; ///< Instance of AnalogIO controller - DS18X20Controller *_ds18x20_controller = - nullptr; ///< Instance of DS18X20 controller - I2cController *_i2c_controller = nullptr; ///< Instance of I2C controller - - // TODO: does this really need to be global? - uint8_t _macAddrV2[6]; /*!< Unique network iface identifier */ - char sUIDV2[13]; /*!< Unique hardware identifier */ - const char *_boardIdV2; /*!< Adafruit IO+ board string */ - Adafruit_MQTT *_mqttV2; /*!< Reference to Adafruit_MQTT, _mqtt. */ - - // TODO: Audit this, does it need to be here? - secretsConfig _configV2; /*!< Wippersnapper secrets.json as a struct. */ - networkConfig _multiNetworksV2[3]; /*!< Wippersnapper networks as structs. */ - bool _isWiFiMultiV2 = false; /*!< True if multiple networks are defined. */ - - // TODO: Does this need to be within this class? - int32_t totalDigitalPinsV2; /*!< Total number of digital-input capable pins */ - - // TODO: Do these need to be here or can they sit within their function? - char *throttleMessageV2; /*!< Pointer to throttle message data. */ - int throttleTimeV2; /*!< Total amount of time to throttle the device, in - milliseconds. */ - - // enable LEDC if esp32 - // #ifdef ARDUINO_ARCH_ESP32 - // ws_ledc *_ledcV2 = nullptr; ///< Pointer to LEDC object - // #endif - bool got_checkin_response; ///< True if a checkin response was received, False - ///< otherwise. - std::vector> - _sharedConfigBuffers; ///< Shared JSON config buffers for offline mode - JsonDocument _config_doc; ///< Storage for the config.json file - uint8_t pin_sd_cs; ///< SD card chip select pin -private: - void _initV2(); - - // MQTT topics - char *_topicB2d; - char *_topicD2b; - char *_topicError; - char *_topicThrottle; - - // Adafruit_MQTT Subscription objects - Adafruit_MQTT_Subscribe *_subscribeB2d; - Adafruit_MQTT_Subscribe *_subscribeError; - Adafruit_MQTT_Subscribe *_subscribeThrottle; - -protected: - ws_status_t _statusV2 = WS_IDLE; ///< Wippersnapper status - - uint32_t _last_mqtt_connectV2 = 0; /*!< Previous time when client connected to - Adafruit IO, in milliseconds. */ - uint32_t _prv_pingV2 = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - uint32_t _prvKATBlinkV2 = 0; /*!< Previous time when client pinged Adafruit - IO's MQTT broker, in milliseconds. */ - - // Device information - const char *_deviceIdV2; /*!< Adafruit IO+ device identifier string */ - char *_device_uidV2; /*!< Unique device identifier */ -}; -extern Wippersnapper_V2 WsV2; ///< Global member variable for callbacks - -#endif // WIPPERSNAPPER_V2_H +/*! + * @file Wippersnapper_V2.h + * + * This is the documentation for Adafruit's Wippersnapper firmware for the + * Arduino platform. It is designed specifically to work with + * Adafruit IO Wippersnapper IoT platform. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @copyright Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + */ + +#ifndef WIPPERSNAPPER_V2_H +#define WIPPERSNAPPER_V2_H + +// Debug Flags +// #DEBUG_PROFILE 1 ///< Enable debug output for function profiling +// Debug Flags +#define WS_DEBUG /**< Define to enable debugging to serial terminal */ +#define WS_PRINTER Serial /**< Where debug messages will be printed */ + +/*! + @brief Debug print macros for WipperSnapper debugging output + @details These macros provide debug output functionality when WS_DEBUG is + defined +*/ +#ifdef WS_DEBUG +#define WS_DEBUG_PRINT(...) \ + { WS_PRINTER.print(__VA_ARGS__); } /**< Print debug message to serial */ +#define WS_DEBUG_PRINTLN(...) \ + { \ + WS_PRINTER.println(__VA_ARGS__); \ + } /**< Print debug message with newline \ + */ +#define WS_DEBUG_PRINTHEX(...) \ + { \ + WS_PRINTER.print(__VA_ARGS__, HEX); \ + } /**< Print debug message in hexadecimal */ +#else +#define WS_DEBUG_PRINT(...) \ + {} /**< Debug print */ +#define WS_DEBUG_PRINTLN(...) \ + {} /**< Debug println */ +#endif + +/*! + @brief delay() function for use with a watchdog timer + @param timeout + Delay duration in milliseconds +*/ +#define WS_DELAY_WITH_WDT(timeout) \ + { \ + unsigned long start = millis(); \ + while (millis() - start < timeout) { \ + delay(10); \ + yield(); \ + WsV2.feedWDTV2(); \ + if (millis() < start) { \ + start = millis(); \ + } \ + } \ + } + +// Cpp STD +#include +#include +#include +#include +#include + +// Nanopb messages and dependencies +#include "protos/signal.pb.h" +#include +#include +#include +#include + +// External libraries +#include "Adafruit_MQTT.h" // MQTT Client +#include "Adafruit_SleepyDog.h" // Watchdog +#include "Arduino.h" // Wiring +#include "RTClib.h" // RTC +#include // SPI +#include // I2C + +// Wippersnapper API Helpers +#include "Wippersnapper_Boards.h" +#include "components/statusLED/Wippersnapper_StatusLED.h" +#include "helpers/ws_helper_status.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "helpers/ws_helper_esp.h" +#endif + +// Components (API v2) +#include "components/analogIO/controller.h" +#include "components/checkin/model.h" +#include "components/digitalIO/controller.h" +#include "components/ds18x20/controller.h" +#include "components/gps/controller.h" +#include "components/i2c/controller.h" +#include "components/pixels/controller.h" +#include "components/pwm/controller.h" +#include "components/sensor/model.h" +#include "components/servo/controller.h" +#include "components/uart/controller.h" + +// Display +#ifdef USE_DISPLAY +#include "display/ws_display_driver.h" +#include "display/ws_display_ui_helper.h" +#endif + +#include "provisioning/ConfigJson.h" +#include "provisioning/sdcard/ws_sdcard.h" +#if defined(USE_TINYUSB) +#include "provisioning/tinyusb/Wippersnapper_FS.h" +#endif +#if defined(USE_LITTLEFS) +#include "provisioning/littlefs/WipperSnapper_LittleFS.h" +#endif + +#define WS_VERSION \ + "1.0.0-offline-beta.4" ///< WipperSnapper app. version + ///< (semver-formatted) + +#define WS_WDT_TIMEOUT 60000 ///< WDT timeout +#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks +/* MQTT Configuration */ +#define WS_KEEPALIVE_INTERVAL_MS \ + 5000 ///< Session keepalive interval time, in milliseconds + +// Forward declarations +class Wippersnapper_FS; +class WipperSnapper_LittleFS; +class ws_sdcard; +#ifdef USE_DISPLAY +class ws_display_driver; +class ws_display_ui_helper; +#endif +class CheckinModel; +class SensorModel; +class DigitalIOController; +class AnalogIOController; +class DS18X20Controller; +class GPSController; +class I2cController; +class PixelsController; +class PWMController; +class ServoController; +class UARTController; + +/*! + @brief Class that provides storage and functions for the Adafruit IO + Wippersnapper interface. +*/ +class Wippersnapper_V2 { +public: + Wippersnapper_V2(); + virtual ~Wippersnapper_V2(); + + void provision(); + + // Global flags for the status led + bool + lockStatusNeoPixelV2; ///< True if status LED is using the status neopixel + bool lockStatusDotStarV2; ///< True if status LED is using the status dotstar + bool lockStatusLEDV2; ///< True if status LED is using the built-in LED + float status_pixel_brightnessV2 = + STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness + ///< (from 0.0 to 1.0) + + virtual void set_user_key(); + virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); + virtual void set_ssid_pass(); + virtual bool check_valid_ssid(); + + virtual void _connect(); + virtual void _disconnect(); + void connect(); + void disconnect(); + + virtual void getMacAddr(); + virtual int32_t getRSSI(); + virtual void setupMQTTClient(const char *clientID); + + virtual ws_status_t networkStatus(); + + // Generators for device UID and MQTT topics + bool generateDeviceUID(); + bool generateWSTopics(); + + // High-level MQTT Publish + bool PublishSignal(pb_size_t which_payload, void *payload); + + // Checkin API + bool CreateCheckinRequest(); + void PollCheckinResponse(); + + // run() loop + ws_status_t run(); + void processPacketsV2(); + + // Networking helpers + void pingBrokerV2(); + void runNetFSMV2(); + + // WDT helpers + void enableWDTV2(int timeoutMS = 0); + void feedWDTV2(); + void BlinkKATStatus(); + + // Error handling helpers + void haltErrorV2(const char *error, + ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME, + bool reboot = true); + void errorWriteHangV2(const char *error); + + bool _is_offline_mode; ///< Global flag for if the device is in offline mode + + // TODO: Do we need this? + ws_board_status_t _boardStatusV2 = + WS_BOARD_DEF_IDLE; ///< Hardware's registration status + + // TODO: We really should look at making these static definitions, not dynamic + // to free up space on the heap + Wippersnapper_FS *_fileSystemV2; ///< Instance of Filesystem (native USB) + WipperSnapper_LittleFS + *_littleFSV2; ///< Instance of LittleFS Filesystem (non-native USB) + ws_sdcard *_sdCardV2; ///< Instance of SD card class +#ifdef USE_DISPLAY + ws_display_driver *_displayV2 = nullptr; ///< Instance of display driver class + ws_display_ui_helper *_ui_helperV2 = + nullptr; ///< Instance of display UI helper class +#endif + + // API v2 Components + CheckinModel *CheckInModel = nullptr; ///< Instance of CheckinModel class + SensorModel *sensorModel = nullptr; ///< Instance of SensorModel class + DigitalIOController *digital_io_controller = + nullptr; ///< Instance of DigitalIO controller class + AnalogIOController *analogio_controller = + nullptr; ///< Instance of AnalogIO controller + DS18X20Controller *_ds18x20_controller = + nullptr; ///< Instance of DS18X20 controller + GPSController *_gps_controller = nullptr; ///< Instance of GPS controller + I2cController *_i2c_controller = nullptr; ///< Instance of I2C controller + PixelsController *_pixels_controller = + nullptr; ///< Instance of Pixels controller + PWMController *_pwm_controller = nullptr; ///< Instance of PWM controller + ServoController *_servo_controller = + nullptr; ///< Instance of Servo controller + UARTController *_uart_controller = nullptr; ///< Instance of UART controller + + // TODO: does this really need to be global? + uint8_t _macAddrV2[6]; /*!< Unique network iface identifier */ + char sUIDV2[13]; /*!< Unique hardware identifier */ + const char *_boardIdV2; /*!< Adafruit IO+ board string */ + Adafruit_MQTT *_mqttV2; /*!< Reference to Adafruit_MQTT, _mqtt. */ + + // TODO: Audit this, does it need to be here? + secretsConfig _configV2; /*!< Wippersnapper secrets.json as a struct. */ + networkConfig _multiNetworksV2[3]; /*!< Wippersnapper networks as structs. */ + bool _isWiFiMultiV2 = false; /*!< True if multiple networks are defined. */ + + // TODO: Does this need to be within this class? + int32_t totalDigitalPinsV2; /*!< Total number of digital-input capable pins */ + + // TODO: Do these need to be here or can they sit within their function? + char *throttleMessageV2; /*!< Pointer to throttle message data. */ + int throttleTimeV2; /*!< Total amount of time to throttle the device, in + milliseconds. */ + + // enable LEDC if esp32 + // #ifdef ARDUINO_ARCH_ESP32 + // ws_ledc *_ledcV2 = nullptr; ///< Pointer to LEDC object + // #endif + bool got_checkin_response; ///< True if a checkin response was received, False + ///< otherwise. + std::vector> + _sharedConfigBuffers; ///< Shared JSON config buffers for offline mode + JsonDocument _config_doc; ///< Storage for the config.json file + uint8_t pin_sd_cs; ///< SD card chip select pin +private: + void _initV2(); + + // MQTT topics + char *_topicB2d; + char *_topicD2b; + char *_topicError; + char *_topicThrottle; + + // Adafruit_MQTT Subscription objects + Adafruit_MQTT_Subscribe *_subscribeB2d; + Adafruit_MQTT_Subscribe *_subscribeError; + Adafruit_MQTT_Subscribe *_subscribeThrottle; + +protected: + ws_status_t _statusV2 = WS_IDLE; ///< Wippersnapper status + + uint32_t _last_mqtt_connectV2 = 0; /*!< Previous time when client connected to + Adafruit IO, in milliseconds. */ + uint32_t _prv_pingV2 = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + uint32_t _prvKATBlinkV2 = 0; /*!< Previous time when client pinged Adafruit + IO's MQTT broker, in milliseconds. */ + + // Device information + const char *_deviceIdV2; /*!< Adafruit IO+ device identifier string */ + char *_device_uidV2; /*!< Unique device identifier */ +}; +extern Wippersnapper_V2 WsV2; ///< Global member variable for callbacks + +#endif // WIPPERSNAPPER_V2_H \ No newline at end of file diff --git a/src/Wippersnapper_demo.ino b/src/Wippersnapper_demo.ino index 5fec95439..2f4af4902 100644 --- a/src/Wippersnapper_demo.ino +++ b/src/Wippersnapper_demo.ino @@ -1,27 +1,21 @@ // Adafruit IO WipperSnapper +// USE ONLY WITH DEVICES WITHOUT A NETWORK ADAPTER LIKE RP2040 PICO // // Adafruit invests time and resources providing this open source code. // Please support Adafruit and open source hardware by purchasing // products from Adafruit! // -// Brent Rubell for Adafruit Industries, 2020-2025 +// Brent Rubell for Adafruit Industries, 2025 // // All text above must be included in any redistribution. #include "ws_adapters.h" -#if defined(ARDUINO_RASPBERRY_PI_PICO_2) || \ - defined(ARDUINO_RASPBERRY_PI_PICO) || \ - defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) || \ - defined(ARDUINO_ADAFRUIT_METRO_RP2350) -ws_adapter_offline wipper; -#else ws_adapter_wifi wipper; -#endif - #define WS_DEBUG // Enable debug output! void setup() { Serial.begin(115200); + while(!Serial); wipper.provision(); wipper.connect(); } diff --git a/src/adapters/offline/ws_offline_pico.h b/src/adapters/offline/ws_offline_pico.h index d3817b3c1..247ea5f68 100644 --- a/src/adapters/offline/ws_offline_pico.h +++ b/src/adapters/offline/ws_offline_pico.h @@ -29,33 +29,26 @@ extern Wippersnapper_V2 WsV2; -/****************************************************************************/ /*! @brief Class for using the Raspberry Pi Pico network interface. */ -/****************************************************************************/ class ws_offline_pico : public Wippersnapper_V2 { public: - /**************************************************************************/ /*! @brief Initializes the WipperSnapper class for RPi Pico. */ - /**************************************************************************/ ws_offline_pico() : Wippersnapper_V2() { // Do-nothing } - /**************************************************************************/ /*! @brief Destructor */ - /**************************************************************************/ ~ws_offline_pico() { // Do-nothing - this class has no resources to release } - /********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -63,74 +56,61 @@ class ws_offline_pico : public Wippersnapper_V2 { @param ssidPassword WiFi network's password. */ - /********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: set_ssid_pass() is not " "supported in this implementation!"); } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. */ - /**********************************************************/ void set_ssid_pass() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: set_ssid_pass() is not " "supported in this implementation!"); } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: check_valid_ssid() is not " "supported in this implementation!"); return false; // return an invalid value } - /********************************************************/ /*! @brief Sets the RPi Pico's unique client identifier @note On RPi Pico, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { // Do Nothing } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: getRSSI() is not supported in " "this implementation!"); return -9999; // return an invalid value } - /********************************************************/ /*! @brief Initializes the MQTT client @param clientID MQTT client identifier */ - /********************************************************/ void setupMQTTClient(const char *clientID) { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: setupMQTTClient() is not " "supported in this implementation!"); } - /********************************************************/ /*! @brief Returns the network status of an RPi Pico. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: networkStatus() is not " "supported in this implementation!"); @@ -138,12 +118,10 @@ class ws_offline_pico : public Wippersnapper_V2 { // a network } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper @return Pico */ - /*******************************************************************/ const char *connectionType() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: connectionType() is not " "supported in this implementation!"); @@ -151,21 +129,17 @@ class ws_offline_pico : public Wippersnapper_V2 { } protected: - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ void _connect() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: _connect() is not supported in " "this implementation!"); } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WS_DEBUG_PRINTLN("[ws_offline_pico] Error: _disconnect() is not supported " "in this implementation!"); diff --git a/src/adapters/wifi/ws_wifi_airlift.h b/src/adapters/wifi/ws_wifi_airlift.h index d9904c329..19a94903e 100644 --- a/src/adapters/wifi/ws_wifi_airlift.h +++ b/src/adapters/wifi/ws_wifi_airlift.h @@ -11,7 +11,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * Copyright (c) Brent Rubell 2020-2025 for Adafruit Industries. * * MIT license, all text here must be included in any redistribution. * @@ -20,35 +20,33 @@ #ifndef WS_WIFI_AIRLIFT_H #define WS_WIFI_AIRLIFT_H +#include "../../helpers/ws_helper_macros.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Arduino.h" #include "SPI.h" #include "WiFiNINA.h" -#include "Wippersnapper.h" +#include "Wippersnapper_V2.h" #define NINAFWVER \ - "1.7.7" /*!< min. nina-fw version compatible with this library. */ + "2.0.0-rc.0+adafruit" /*!< min. nina-fw version compatible with this \ + library. */ #define AIRLIFT_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ #define AIRLIFT_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ #define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ -extern Wippersnapper WS; ///< Global Wippersnapper instance -/****************************************************************************/ +extern Wippersnapper_V2 WsV2; ///< Wippersnapper client instance /*! @brief Class for using the AirLift Co-Processor network iface. */ -/****************************************************************************/ -class ws_wifi_airlift : public Wippersnapper { +class ws_wifi_airlift : public Wippersnapper_V2 { public: - /**************************************************************************/ /*! @brief Initializes the Adafruit IO class for AirLift devices. */ - /**************************************************************************/ - ws_wifi_airlift() : Wippersnapper() { + ws_wifi_airlift() : Wippersnapper_V2() { _ssPin = SPIWIFI_SS; // 10; _ackPin = SPIWIFI_ACK; // 7; _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals @@ -66,17 +64,14 @@ class ws_wifi_airlift : public Wippersnapper { WiFi.setPins(_ssPin, _ackPin, _rstPin, _gpio0Pin, _wifi); } - /**************************************************************************/ /*! @brief Destructor for the Adafruit IO AirLift class. */ - /**************************************************************************/ ~ws_wifi_airlift() { - if (_mqtt) - delete _mqtt; + if (_mqttV2) + delete _mqttV2; } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -84,29 +79,24 @@ class ws_wifi_airlift : public Wippersnapper { @param ssidPassword Wireless network's password. */ - /**********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { _ssid = ssid; _pass = ssidPassword; } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password from the secrets.json provisioning file. */ - /**********************************************************/ void set_ssid_pass() { - _ssid = WS._config.network.ssid; - _pass = WS._config.network.pass; + _ssid = WsV2._configV2.network.ssid; + _pass = WsV2._configV2.network.pass; } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { // Disconnect WiFi from an AP if it was previously connected WiFi.disconnect(); @@ -141,19 +131,16 @@ class ws_wifi_airlift : public Wippersnapper { return false; } - /********************************************************/ /*! @brief Sets the WiFi client. @param wifi Instance of SPIClass. */ - /********************************************************/ void set_wifi(SPIClass *wifi) { _wifi = wifi; _mqtt_client = new WiFiSSLClient; } - /********************************************************/ /*! @brief Configures ESP32 "AirLift" pins. @param ssPin @@ -165,7 +152,6 @@ class ws_wifi_airlift : public Wippersnapper { @param gpio0Pin ESP32 GPIO0 pin. */ - /********************************************************/ void set_airlift_pins(int ssPin, int ackPin, int rstPin, int gpio0Pin) { _ssPin = ssPin; _ackPin = ackPin; @@ -173,20 +159,17 @@ class ws_wifi_airlift : public Wippersnapper { _gpio0Pin = gpio0Pin; } - /********************************************************/ /*! @brief Checks the version of an ESP32 module running nina-fw. @returns True if matches min. required to run WipperSnapper, False otherwise. */ - /********************************************************/ bool firmwareCheck() { _fv = WiFi.firmwareVersion(); return compareVersions(_fv, NINAFWVER); } - /********************************************************/ /*! @brief Compares two version strings. @param currentVersion @@ -196,7 +179,6 @@ class ws_wifi_airlift : public Wippersnapper { @returns True if the current version is greater than or equal to the required version, False otherwise. */ - /********************************************************/ bool compareVersions(const char *currentVersion, const char *requiredVersion) { int curMajor = 0, curMinor = 0, curPatch = 0; @@ -222,45 +204,37 @@ class ws_wifi_airlift : public Wippersnapper { return curPatch >= reqPatch; } - /********************************************************/ /*! @brief Gets the ESP32's unique client identifier. @note For the ESP32, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; WiFi.macAddress(mac); - memcpy(WS._macAddr, mac, sizeof(mac)); + memcpy(WsV2._macAddrV2, mac, sizeof(mac)); } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { return WiFi.RSSI(); } - /********************************************************/ /*! @brief Initializes the MQTT client. @param clientID MQTT client identifier */ - /********************************************************/ void setupMQTTClient(const char *clientID) { - WS._mqtt = new Adafruit_MQTT_Client( - _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID, - WS._config.aio_user, WS._config.aio_key); + WsV2._mqttV2 = new Adafruit_MQTT_Client( + _mqtt_client, WsV2._configV2.aio_url, WsV2._configV2.io_port, clientID, + WsV2._configV2.aio_user, WsV2._configV2.aio_key); } - /********************************************************/ /*! @brief Returns the network status of an ESP32 module. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { switch (WiFi.status()) { case WL_CONNECTED: @@ -274,12 +248,10 @@ class ws_wifi_airlift : public Wippersnapper { } } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper @return AIRLIFT */ - /*******************************************************************/ const char *connectionType() { return "AIRLIFT"; } protected: @@ -293,15 +265,13 @@ class ws_wifi_airlift : public Wippersnapper { WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ SPIClass *_wifi; /*!< Instance of the SPI bus used by the AirLift. */ - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ void _connect() { if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; // possibly unneccesary as already checking - // elsewhere + _statusV2 = WS_SSID_INVALID; // possibly unneccesary as already checking + // elsewhere } else { // disconnect from possible previous connection _disconnect(); @@ -310,16 +280,16 @@ class ws_wifi_airlift : public Wippersnapper { _wifi->end(); delay(100); _wifi->begin(); - feedWDT(); + WsV2.feedWDTV2(); // reset the esp32 if possible resetAirLift(); - feedWDT(); + WsV2.feedWDTV2(); WS_DEBUG_PRINT("ESP32 booted, version: "); WS_PRINTER.flush(); WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); WS_PRINTER.flush(); - feedWDT(); + WsV2.feedWDTV2(); // validate co-processor's firmware version if (!firmwareCheck()) { @@ -330,35 +300,32 @@ class ws_wifi_airlift : public Wippersnapper { WS_DEBUG_PRINT("Connecting to "); WS_DEBUG_PRINTLN(_ssid); WS_PRINTER.flush(); - feedWDT(); + WsV2.feedWDTV2(); WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; + _statusV2 = WS_NET_DISCONNECTED; // Use the macro to retry the status check until connected / timed out int lastResult = -1; RETRY_FUNCTION_UNTIL_TIMEOUT( []() -> int { return WiFi.status(); }, // Function call each cycle - int, // return type lastResult, // return variable [](int status) { return status == WL_CONNECTED; }, // check AIRLIFT_CONNECT_TIMEOUT_MS, // timeout interval (ms) AIRLIFT_CONNECT_RETRY_DELAY_MS); // interval between retries if (lastResult == WL_CONNECTED) { - _status = WS_NET_CONNECTED; + _statusV2 = WS_NET_CONNECTED; // wait 2seconds for connection to stabilize WS_DELAY_WITH_WDT(2000); } else { - _status = WS_NET_DISCONNECTED; // maybe connect failed instead? + _statusV2 = WS_NET_DISCONNECTED; // maybe connect failed instead? } } } - /**************************************************************************/ /*! @brief Resets the ESP32 module. */ - /**************************************************************************/ void resetAirLift() { if (_rstPin != -1) { WS_DEBUG_PRINTLN("Resetting ESP32..."); @@ -383,11 +350,9 @@ class ws_wifi_airlift : public Wippersnapper { } } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WiFi.disconnect(); delay(500); diff --git a/src/adapters/wifi/ws_wifi_esp32.h b/src/adapters/wifi/ws_wifi_esp32.h index 2de415682..db0ada932 100644 --- a/src/adapters/wifi/ws_wifi_esp32.h +++ b/src/adapters/wifi/ws_wifi_esp32.h @@ -29,25 +29,20 @@ #include extern Wippersnapper_V2 WsV2; ///< Wippersnapper client instance -/****************************************************************************/ /*! @brief Class for using the ESP32 network interface. */ -/****************************************************************************/ class ws_wifi_esp32 : public Wippersnapper_V2 { public: - /**************************************************************************/ /*! @brief Initializes the Adafruit IO class for ESP32 devices. */ - /**************************************************************************/ ws_wifi_esp32() : Wippersnapper_V2() { _ssid = 0; _pass = 0; } - /**************************************************************************/ /*! @brief Overload for ESP32 devices without filesystem-backed provisioning. @param aioUsername @@ -63,7 +58,6 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { @param brokerPort Adafruit IO broker port. */ - /**************************************************************************/ ws_wifi_esp32(const char *aioUsername, const char *aioKey, const char *netSSID, const char *netPass, const char *brokerURL, uint16_t brokerPort) @@ -83,11 +77,9 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { WsV2._configV2.io_port = brokerPort; } - /**************************************************************************/ /*! @brief Destructor for the Adafruit IO AirLift class. */ - /**************************************************************************/ ~ws_wifi_esp32() { if (_mqtt_client_secure) delete _mqtt_client_secure; @@ -95,7 +87,6 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { delete _mqtt_client_insecure; } - /********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -103,7 +94,6 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { @param ssidPassword WiFi network's password. */ - /********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { _ssid = ssid; @@ -116,22 +106,18 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { } } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. */ - /**********************************************************/ void set_ssid_pass() { _ssid = WsV2._configV2.network.ssid; _pass = WsV2._configV2.network.pass; } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { // Set WiFi to station mode and disconnect from an AP if it was previously // connected @@ -183,33 +169,27 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { return false; } - /********************************************************/ /*! @brief Sets the ESP32's unique client identifier @note On ESP32, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0}; Network.macAddress(mac); memcpy(WsV2._macAddrV2, mac, sizeof(mac)); } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { return WiFi.RSSI(); } - /********************************************************/ /*! @brief Initializes the MQTT client @param clientID MQTT client identifier */ - /********************************************************/ void setupMQTTClient(const char *clientID) { if (strcmp(WsV2._configV2.aio_url, "io.adafruit.com") == 0 || strcmp(WsV2._configV2.aio_url, "io.adafruit.us") == 0) { @@ -231,12 +211,10 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { } } - /********************************************************/ /*! @brief Returns the network status of an ESP32 module. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { switch (WiFi.status()) { case WL_CONNECTED: @@ -250,12 +228,10 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { } } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper @return ESP32 */ - /*******************************************************************/ const char *connectionType() { return "ESP32"; } protected: @@ -324,11 +300,9 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { "7h4SeM6Y8l/7MBRpPCz6l8Y=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.com - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ void _connect() { if (WiFi.status() == WL_CONNECTED) @@ -369,11 +343,9 @@ class ws_wifi_esp32 : public Wippersnapper_V2 { } } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WiFi.disconnect(); delay(500); diff --git a/src/adapters/wifi/ws_wifi_esp8266.h b/src/adapters/wifi/ws_wifi_esp8266.h index 73e25250d..f801fbbef 100644 --- a/src/adapters/wifi/ws_wifi_esp8266.h +++ b/src/adapters/wifi/ws_wifi_esp8266.h @@ -8,7 +8,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * Copyright (c) Brent Rubell 2020-2025 for Adafruit Industries. * * MIT license, all text here must be included in any redistribution. * @@ -39,16 +39,13 @@ extern Wippersnapper_V2 WsV2; -/******************************************************************************/ /*! @brief Class for interacting with the Espressif ESP8266's network interface. */ -/******************************************************************************/ class ws_wifi_esp8266 : public Wippersnapper_V2 { public: - /**************************************************************************/ /*! @brief Initializes the Adafruit IO class for ESP8266 devices. @param aioUsername @@ -60,7 +57,6 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { @param netPass Wireless Network password */ - /**************************************************************************/ ws_wifi_esp8266() : Wippersnapper_V2() { _ssid = 0; _pass = 0; @@ -69,19 +65,16 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { WiFi.mode(WIFI_STA); } - /**************************************************************************/ /*! @brief Destructor for the ESP8266's network iface. */ - /**************************************************************************/ ~ws_wifi_esp8266() { if (_wifi_client) delete _wifi_client; - if (_mqtt) - delete _mqtt; + if (_mqttV2) + delete _mqttV2; } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -89,7 +82,6 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { @param ssidPassword Wireless network's password. */ - /**********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { _ssid = ssid; @@ -102,23 +94,19 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { } } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password from the ESP8266's LittleFS. */ - /**********************************************************/ void set_ssid_pass() { - _ssid = WS._config.network.ssid; - _pass = WS._config.network.pass; + _ssid = WsV2._configV2.network.ssid; + _pass = WsV2._configV2.network.pass; } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { // Set WiFi to station mode and disconnect from an AP if it was previously // connected @@ -142,7 +130,7 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; } - if (WS._isWiFiMultiV2) { + if (WsV2._isWiFiMultiV2) { // multi network mode for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { if (strcmp(WsV2._multiNetworksV2[j].ssid, WiFi.SSID(i).c_str()) == @@ -170,38 +158,33 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { return false; } - /********************************************************/ /*! @brief Gets the ESP8266's unique client identifier. @note For the ESP8266, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; WiFi.macAddress(mac); memcpy(WsV2._macAddrV2, mac, sizeof(mac)); } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { return WiFi.RSSI(); } - /*******************************************************************/ /*! @brief Sets up an Adafruit_MQTT_Client @param clientID MQTT client identifier */ - /*******************************************************************/ void setupMQTTClient(const char *clientID) { // Uncomment the following lines to use MQTT/SSL. You will need to - // re-compile after. _wifi_client->setFingerprint(fingerprint); WS._mqtt = - // new Adafruit_MQTT_Client(_wifi_client, WS._config.aio_url, - // WS._config.io_port, clientID, WS._config.aio_user, WS._config.aio_key); + // re-compile after. _wifi_client->setFingerprint(fingerprint); WsV2._mqttV2 + // = new Adafruit_MQTT_Client(_wifi_client, WsV2._configV2.aio_url, + // WsV2._configV2.io_port, clientID, WsV2._configV2.aio_user, + // WsV2._configV2.aio_key); if (WsV2._configV2.io_port == 8883) WsV2._configV2.io_port = 1883; WsV2._mqttV2 = new Adafruit_MQTT_Client( @@ -209,12 +192,10 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { WsV2._configV2.aio_user, WsV2._configV2.aio_key); } - /********************************************************/ /*! @brief Returns the network status of an ESP8266 module. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { switch (WiFi.status()) { case WL_CONNECTED: @@ -228,12 +209,10 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { } } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper_V2 @return "ESP8266" */ - /*******************************************************************/ const char *connectionType() { return "ESP8266"; } protected: @@ -242,12 +221,10 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { WiFiClient *_wifi_client; ESP8266WiFiMulti _wifiMulti; - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ - void _connect()() { + void _connect() { if (WiFi.status() == WL_CONNECTED) return; @@ -265,7 +242,7 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { _statusV2 = WS_NET_DISCONNECTED; delay(100); - if (WS._isWiFiMultiV2) { + if (WsV2._isWiFiMultiV2) { // multi network mode for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) { if (strlen(WsV2._multiNetworksV2[i].ssid) > 0 && @@ -307,15 +284,13 @@ class ws_wifi_esp8266 : public Wippersnapper_V2 { _statusV2 = WS_NET_DISCONNECTED; } } - WsV2.feedWDT(); + WsV2.feedWDTV2(); } } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WiFi.disconnect(); delay(500); diff --git a/src/adapters/wifi/ws_wifi_ninafw.h b/src/adapters/wifi/ws_wifi_ninafw.h index 1db1ff53e..627c78823 100644 --- a/src/adapters/wifi/ws_wifi_ninafw.h +++ b/src/adapters/wifi/ws_wifi_ninafw.h @@ -28,15 +28,12 @@ SPI /*!< Instance of SPI interface used by an external uBlox module. */ extern Wippersnapper WS; ///< Global Wippersnapper instance -/****************************************************************************/ /*! @brief Class for using the AirLift Co-Processor network iface. */ -/****************************************************************************/ class ws_wifi_ninafw : public Wippersnapper { public: - /**************************************************************************/ /*! @brief Initializes the Adafruit IO class for ublox devices. @param aioUsername @@ -48,7 +45,6 @@ class ws_wifi_ninafw : public Wippersnapper { @param netPass Wireless Network password */ - /**************************************************************************/ ws_wifi_ninafw(const char *aioUsername, const char *aioKey, const char *netSSID, const char *netPass) : Wippersnapper() { @@ -61,29 +57,24 @@ class ws_wifi_ninafw : public Wippersnapper { _mqtt_client = new WiFiSSLClient; } - /**************************************************************************/ /*! @brief Destructor for the Adafruit IO ublox class. */ - /**************************************************************************/ ~ws_wifi_ninafw() { if (_mqtt) delete _mqtt; } - /****************************************************************************/ /*! @brief Configures the device's Adafruit IO credentials. This method should be used only if filesystem-backed provisioning is not avaliable. */ - /****************************************************************************/ void set_user_key() { strlcpy(WS._config.aio_user, _username, sizeof(WS._config.aio_user)); strlcpy(WS._config.aio_key, _key, sizeof(WS._config.aio_key)); } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -91,30 +82,25 @@ class ws_wifi_ninafw : public Wippersnapper { @param ssidPassword Wireless network's password. */ - /**********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { strlcpy(WS._config.network.ssid, ssid, sizeof(WS._config.network.ssid)); strlcpy(WS._config.network.pass, ssidPassword, sizeof(WS._config.network.pass)); } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password from the header file's credentials. */ - /**********************************************************/ void set_ssid_pass() { strlcpy(WS._config.network.ssid, _ssid, sizeof(WS._config.network.ssid)); strlcpy(WS._config.network.pass, _pass, sizeof(WS._config.network.pass)); } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { // Set WiFi to station mode and disconnect from an AP if it was previously // connected @@ -150,25 +136,21 @@ class ws_wifi_ninafw : public Wippersnapper { return false; } - /********************************************************/ /*! @brief Sets the WiFi client. @param wifi Instance of SPIClass. */ - /********************************************************/ void set_wifi(SPIClass *wifi) { _wifi = wifi; _mqtt_client = new WiFiSSLClient; } - /***********************************************************/ /*! @brief Checks the nina-fw version on the module. @return True if firmware on the ublox module matches the latest version of the library, False otherwise. */ - /***********************************************************/ bool firmwareCheck() { String fv = WiFi.firmwareVersion(); if (fv < WIFI_FIRMWARE_LATEST_VERSION) @@ -176,45 +158,37 @@ class ws_wifi_ninafw : public Wippersnapper { return true; } - /********************************************************/ /*! @brief Gets the ESP32's unique client identifier. @note For the ESP32, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; WiFi.macAddress(mac); memcpy(WS._macAddr, mac, sizeof(mac)); } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { return WiFi.RSSI(); } - /********************************************************/ /*! @brief Initializes the MQTT client. @param clientID MQTT client identifier */ - /********************************************************/ void setupMQTTClient(const char *clientID) { WS._mqtt = new Adafruit_MQTT_Client( _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID, WS._config.aio_user, WS._config.aio_key); } - /********************************************************/ /*! @brief Returns the network status of an ESP32 module. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { switch (WiFi.status()) { case WL_CONNECTED: @@ -228,12 +202,10 @@ class ws_wifi_ninafw : public Wippersnapper { } } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper @return AIRLIFT */ - /*******************************************************************/ const char *connectionType() { return "AIRLIFT"; } protected: @@ -245,11 +217,9 @@ class ws_wifi_ninafw : public Wippersnapper { WiFiSSLClient *_mqtt_client; /*!< Instance of a secure WiFi client. */ SPIClass *_wifi; /*!< Instance of the SPI bus used by the ublox. */ - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ void _connect() { // check if co-processor connected first @@ -272,11 +242,9 @@ class ws_wifi_ninafw : public Wippersnapper { } } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WiFi.disconnect(); delay(500); diff --git a/src/adapters/wifi/ws_wifi_pico.h b/src/adapters/wifi/ws_wifi_pico.h index 07ba5066a..a79729797 100644 --- a/src/adapters/wifi/ws_wifi_pico.h +++ b/src/adapters/wifi/ws_wifi_pico.h @@ -17,7 +17,8 @@ #ifndef WS_WIFI_PICO_H #define WS_WIFI_PICO_H -#ifdef ARDUINO_RASPBERRY_PI_PICO_W +#if defined(ARDUINO_RASPBERRY_PI_PICO_W) || \ + defined(ARDUINO_RASPBERRY_PI_PICO_2W) #define PICO_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ #define PICO_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ @@ -31,29 +32,23 @@ #include extern Wippersnapper_V2 WsV2; -/****************************************************************************/ /*! @brief Class for using the Raspberry Pi Pico network interface. */ -/****************************************************************************/ class ws_wifi_pico : public Wippersnapper_V2 { public: - /**************************************************************************/ /*! @brief Initializes the WipperSnapper class for RPi Pico. */ - /**************************************************************************/ ws_wifi_pico() : Wippersnapper_V2() { _ssid = 0; _pass = 0; } - /**************************************************************************/ /*! @brief Destructor */ - /**************************************************************************/ ~ws_wifi_pico() { if (_mqtt_client_secure) delete _mqtt_client_secure; @@ -61,7 +56,6 @@ class ws_wifi_pico : public Wippersnapper_V2 { delete _mqtt_client_secure; } - /********************************************************/ /*! @brief Sets the WiFi client's ssid and password. @param ssid @@ -69,7 +63,6 @@ class ws_wifi_pico : public Wippersnapper_V2 { @param ssidPassword WiFi network's password. */ - /********************************************************/ void set_ssid_pass(const char *ssid, const char *ssidPassword) { _ssid = ssid; @@ -82,22 +75,18 @@ class ws_wifi_pico : public Wippersnapper_V2 { } } - /**********************************************************/ /*! @brief Sets the WiFi client's ssid and password. */ - /**********************************************************/ void set_ssid_pass() { _ssid = WsV2._configV2.network.ssid; _pass = WsV2._configV2.network.pass; } - /***********************************************************/ /*! @brief Performs a scan of local WiFi networks. @returns True if `_network_ssid` is found, False otherwise. */ - /***********************************************************/ bool check_valid_ssid() { // Set WiFi to station mode and disconnect from an AP if it was previously // connected @@ -148,33 +137,27 @@ class ws_wifi_pico : public Wippersnapper_V2 { return false; } - /********************************************************/ /*! @brief Sets the RPi Pico's unique client identifier @note On RPi Pico, the UID is the MAC address. */ - /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0}; WiFi.macAddress(mac); memcpy(WsV2._macAddrV2, mac, sizeof(mac)); } - /********************************************************/ /*! @brief Gets the current network RSSI value @return int32_t RSSI value */ - /********************************************************/ int32_t getRSSI() { return WiFi.RSSI(); } - /********************************************************/ /*! @brief Initializes the MQTT client @param clientID MQTT client identifier */ - /********************************************************/ void setupMQTTClient(const char *clientID) { if (strcmp(WsV2._configV2.aio_url, "io.adafruit.com") == 0 || strcmp(WsV2._configV2.aio_url, "io.adafruit.us") == 0) { @@ -194,12 +177,10 @@ class ws_wifi_pico : public Wippersnapper_V2 { } } - /********************************************************/ /*! @brief Returns the network status of an RPi Pico. @return ws_status_t */ - /********************************************************/ ws_status_t networkStatus() { switch (WiFi.status()) { case WL_CONNECTED: @@ -213,12 +194,10 @@ class ws_wifi_pico : public Wippersnapper_V2 { } } - /*******************************************************************/ /*! @brief Returns the type of network connection used by Wippersnapper @return Pico */ - /*******************************************************************/ const char *connectionType() { return "Pico"; } protected: @@ -287,11 +266,9 @@ class ws_wifi_pico : public Wippersnapper_V2 { "7h4SeM6Y8l/7MBRpPCz6l8Y=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.com - /**************************************************************************/ /*! @brief Establishes a connection with the wireless network. */ - /**************************************************************************/ void _connect() { if (WiFi.status() == WL_CONNECTED) @@ -349,11 +326,9 @@ class ws_wifi_pico : public Wippersnapper_V2 { } } - /**************************************************************************/ /*! @brief Disconnects from the wireless network. */ - /**************************************************************************/ void _disconnect() { WsV2.feedWDTV2(); WiFi.disconnect(); diff --git a/src/components/analogIO/controller.cpp b/src/components/analogIO/controller.cpp index 7fae11b54..b33978192 100644 --- a/src/components/analogIO/controller.cpp +++ b/src/components/analogIO/controller.cpp @@ -1,5 +1,5 @@ /*! - * @file controller.cpp + * @file src/components/analogIO/controller.cpp * * Controller for the analogio.proto API * @@ -14,11 +14,9 @@ */ #include "controller.h" -/***********************************************************************/ /*! @brief AnalogIO controller constructor */ -/***********************************************************************/ AnalogIOController::AnalogIOController() { _analogio_hardware = new AnalogIOHardware(); _analogio_model = new AnalogIOModel(); @@ -26,40 +24,33 @@ AnalogIOController::AnalogIOController() { SetRefVoltage(3.3); // Default to 3.3V } -/***********************************************************************/ /*! @brief AnalogIO controller destructor */ -/***********************************************************************/ AnalogIOController::~AnalogIOController() { delete _analogio_hardware; delete _analogio_model; } -/***********************************************************************/ /*! @brief Set the reference voltage for the analog pins @param voltage The reference voltage. */ -/***********************************************************************/ void AnalogIOController::SetRefVoltage(float voltage) { // To set the reference voltage, we call into the hardware _analogio_hardware->SetReferenceVoltage(voltage); } -/***********************************************************************/ /*! @brief Set the total number of analog pins present on the hardware. @param total_pins The hardware's total number of analog pins. */ -/***********************************************************************/ void AnalogIOController::SetTotalAnalogPins(uint8_t total_pins) { _total_analogio_pins = total_pins; } -/***********************************************************************/ /*! @brief Handles an AnalogIOAdd message from the broker and adds a new analog pin to the controller. @@ -67,7 +58,6 @@ void AnalogIOController::SetTotalAnalogPins(uint8_t total_pins) { The nanopb input stream. @return True if the pin was successfully added, False otherwise. */ -/***********************************************************************/ bool AnalogIOController::Handle_AnalogIOAdd(pb_istream_t *stream) { // Attempt to decode the incoming message into an AnalogIOAdd object if (!_analogio_model->DecodeAnalogIOAdd(stream)) { @@ -105,7 +95,6 @@ bool AnalogIOController::Handle_AnalogIOAdd(pb_istream_t *stream) { return true; } -/***************************************************************************/ /*! @brief Handles an AnalogIORemove message from the broker and removes the requested analog pin from the controller. @@ -113,7 +102,6 @@ bool AnalogIOController::Handle_AnalogIOAdd(pb_istream_t *stream) { The nanopb input stream. @return True if the pin was successfully removed, False otherwise. */ -/***************************************************************************/ bool AnalogIOController::Handle_AnalogIORemove(pb_istream_t *stream) { // Attempt to decode the incoming message into an AnalogIORemove object if (!_analogio_model->DecodeAnalogIORemove(stream)) { @@ -138,7 +126,6 @@ bool AnalogIOController::Handle_AnalogIORemove(pb_istream_t *stream) { return true; } -/***************************************************************************/ /*! @brief Checks if a pin's periodic timer has expired. @param pin @@ -147,12 +134,10 @@ bool AnalogIOController::Handle_AnalogIORemove(pb_istream_t *stream) { The current time (called from millis()). @return True if the pin's period has expired, False otherwise. */ -/***************************************************************************/ bool AnalogIOController::IsPinTimerExpired(analogioPin *pin, ulong cur_time) { return cur_time - pin->prv_period > pin->period; } -/***************************************************************************/ /*! @brief Encodes and publishes an AnalogIOEvent message to the broker. @param pin @@ -163,7 +148,6 @@ bool AnalogIOController::IsPinTimerExpired(analogioPin *pin, ulong cur_time) { The type of read to perform on the pin. @return True if the message was successfully encoded and published. */ -/***************************************************************************/ bool AnalogIOController::EncodePublishPinEvent( uint8_t pin, float value, wippersnapper_sensor_SensorType read_type) { char c_pin_name[12]; @@ -198,7 +182,6 @@ bool AnalogIOController::EncodePublishPinEvent( return true; } -/***************************************************************************/ /*! @brief Encodes and publishes an AnalogIOEvent message to the broker. @param pin @@ -208,10 +191,9 @@ bool AnalogIOController::EncodePublishPinEvent( @return True if the message was successfully encoded and published, False othewise. */ -/***************************************************************************/ bool AnalogIOController::EncodePublishPinValue(uint8_t pin, uint16_t value) { if (WsV2._sdCardV2->isModeOffline()) { - return WsV2._sdCardV2->LogGPIOSensorEventToSD( + return WsV2._sdCardV2->LogEventGpio( pin, value, wippersnapper_sensor_SensorType_SENSOR_TYPE_RAW); } else { return EncodePublishPinEvent( @@ -219,7 +201,6 @@ bool AnalogIOController::EncodePublishPinValue(uint8_t pin, uint16_t value) { } } -/***************************************************************************/ /*! @brief Encodes and publishes an AnalogIOEvent message to the broker. @param pin @@ -229,10 +210,9 @@ bool AnalogIOController::EncodePublishPinValue(uint8_t pin, uint16_t value) { @return True if the message was successfully encoded and published, False othewise. */ -/***************************************************************************/ bool AnalogIOController::EncodePublishPinVoltage(uint8_t pin, float value) { if (WsV2._sdCardV2->isModeOffline()) { - return WsV2._sdCardV2->LogGPIOSensorEventToSD( + return WsV2._sdCardV2->LogEventGpio( pin, value, wippersnapper_sensor_SensorType_SENSOR_TYPE_VOLTAGE); } else { return EncodePublishPinEvent( @@ -240,11 +220,9 @@ bool AnalogIOController::EncodePublishPinVoltage(uint8_t pin, float value) { } } -/***************************************************************************/ /*! @brief Update/polling loop for the AnalogIO controller. */ -/***************************************************************************/ void AnalogIOController::update() { // Bail-out if the vector is empty if (_analogio_pins.empty()) { diff --git a/src/components/analogIO/controller.h b/src/components/analogIO/controller.h index d046578c8..3c076936c 100644 --- a/src/components/analogIO/controller.h +++ b/src/components/analogIO/controller.h @@ -1,5 +1,5 @@ /*! - * @file controller.h + * @file src/components/analogIO/controller.h * * Controller for the AnalogIO API * @@ -33,13 +33,11 @@ struct analogioPin { wippersnapper_sensor_SensorType read_mode; ///< Type of analog read to perform }; -/**************************************************************************/ /*! @brief Routes messages using the analogio.proto API to the appropriate hardware and model classes, controls and tracks the state of the hardware's digital I/O pins. */ -/**************************************************************************/ class AnalogIOController { public: AnalogIOController(); diff --git a/src/components/analogIO/hardware.cpp b/src/components/analogIO/hardware.cpp index 038143fff..f84dac389 100644 --- a/src/components/analogIO/hardware.cpp +++ b/src/components/analogIO/hardware.cpp @@ -1,5 +1,5 @@ /*! - * @file hardware.cpp + * @file src/components/analogIO/hardware.cpp * * Hardware interface for the analogio.proto API * @@ -14,60 +14,48 @@ */ #include "hardware.h" -/***********************************************************************/ /*! @brief AnalogIO hardware constructor */ -/***********************************************************************/ AnalogIOHardware::AnalogIOHardware() { SetNativeADCResolution(); // Configure the device's native ADC resolution SetResolution(16); // Wippersnapper's default resolution is 16-bit } -/***********************************************************************/ /*! @brief AnalogIO hardware destructor */ -/***********************************************************************/ AnalogIOHardware::~AnalogIOHardware() {} -/***********************************************************************/ /*! @brief Initializes an analog input pin. @param pin The requested pin. */ -/***********************************************************************/ void AnalogIOHardware::InitPin(uint8_t pin) { pinMode(pin, INPUT); } -/***********************************************************************/ /*! @brief Deinitializes an analog input pin and frees it for other uses. @param pin The requested pin. */ -/***********************************************************************/ void AnalogIOHardware::DeinitPin(uint8_t pin) { pinMode(pin, INPUT); // set to a hi-z floating pin } -/*************************************************************************/ /*! @brief Sets the hardware's reference voltage for reading analog pins @param voltage The reference voltage. */ -/*************************************************************************/ void AnalogIOHardware::SetReferenceVoltage(float voltage) { _ref_voltage = voltage; } -/*************************************************************************/ /*! @brief Configures the hardware's native ADC resolution. */ -/*************************************************************************/ void AnalogIOHardware::SetNativeADCResolution() { #if defined(ARDUINO_ARCH_SAMD) _native_adc_resolution = 12; @@ -84,17 +72,17 @@ void AnalogIOHardware::SetNativeADCResolution() { #else _native_adc_resolution = 10; #endif +#ifndef ARDUINO_ARCH_ESP8266 // Set the resolution (in bits) of the hardware's ADC analogReadResolution(_native_adc_resolution); +#endif // ARDUINO_ARCH_ESP8266 } -/*************************************************************************/ /*! @brief Sets the hardware ADC's resolution. @param resolution The requested resolution, in bits. */ -/*************************************************************************/ void AnalogIOHardware::SetResolution(uint8_t resolution) { _desired_adc_resolution = resolution; // Calculate (or re-calculate) the scale factor whenever we set the desired @@ -102,12 +90,10 @@ void AnalogIOHardware::SetResolution(uint8_t resolution) { CalculateScaleFactor(); } -/*************************************************************************/ /*! @brief Calculates a factor used by the hardware for scaling the ADC's resolution. */ -/*************************************************************************/ void AnalogIOHardware::CalculateScaleFactor() { _max_scale_resolution_desired = pow(2, 16); // TODO: Change to _desired_adc_resolution @@ -115,27 +101,23 @@ void AnalogIOHardware::CalculateScaleFactor() { pow(2, 13); // TODO: Change to _native_adc_resolution } -/*************************************************************************/ /*! @brief Gets the "raw" value of an analog pin from the ADC. @param pin The requested pin. @return The pin's value. */ -/*************************************************************************/ uint16_t AnalogIOHardware::GetPinValue(uint8_t pin) { return (analogRead(pin) * _max_scale_resolution_desired) / _max_scale_resolution_native; } -/*************************************************************************/ /*! @brief Gets the voltage of an analog pin. @param pin The requested pin. @return The pin's voltage. */ -/*************************************************************************/ float AnalogIOHardware::GetPinVoltage(uint8_t pin) { #ifdef ARDUINO_ARCH_ESP32 return analogReadMilliVolts(pin) / 1000.0; diff --git a/src/components/analogIO/hardware.h b/src/components/analogIO/hardware.h index 447495034..9e8c4b820 100644 --- a/src/components/analogIO/hardware.h +++ b/src/components/analogIO/hardware.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/analogIO/hardware.h * * Hardware implementation for the analogio.proto message. * @@ -16,11 +16,9 @@ #define WS_ANALOGIO_HARDWARE_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Interface for interacting with hardware's analog i/o pin API. */ -/**************************************************************************/ class AnalogIOHardware { public: AnalogIOHardware(); diff --git a/src/components/analogIO/model.cpp b/src/components/analogIO/model.cpp index b5c9f9854..7089d8207 100644 --- a/src/components/analogIO/model.cpp +++ b/src/components/analogIO/model.cpp @@ -1,5 +1,5 @@ /*! - * @file model.cpp + * @file src/components/analogIO/model.cpp * * Interfaces for the analogio.proto API * @@ -14,23 +14,25 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief AnalogIOModel constructor */ -/***********************************************************************/ AnalogIOModel::AnalogIOModel() { - _msg_AnalogioAdd = wippersnapper_analogio_AnalogIOAdd_init_default; + memset(&_msg_AnalogioAdd, 0, sizeof(_msg_AnalogioAdd)); + memset(&_msg_AnalogioRemove, 0, sizeof(_msg_AnalogioRemove)); + memset(&_msg_AnalogioEvent, 0, sizeof(_msg_AnalogioEvent)); + // no-op } -/***********************************************************************/ /*! @brief AnalogIOModel destructor */ -/***********************************************************************/ -AnalogIOModel::~AnalogIOModel() {} +AnalogIOModel::~AnalogIOModel() { + memset(&_msg_AnalogioAdd, 0, sizeof(_msg_AnalogioAdd)); + memset(&_msg_AnalogioRemove, 0, sizeof(_msg_AnalogioRemove)); + memset(&_msg_AnalogioEvent, 0, sizeof(_msg_AnalogioEvent)); +} -/***********************************************************************/ /*! @brief Decodes an AnalogIOAdd message from a stream into an AnalogIOAdd message struct. @@ -38,27 +40,23 @@ AnalogIOModel::~AnalogIOModel() {} The pb_istream_t stream to decode. @return True if successful, False otherwise. */ -/***********************************************************************/ bool AnalogIOModel::DecodeAnalogIOAdd(pb_istream_t *stream) { // Zero-out the AnalogIOAdd message struct. to ensure we don't have any old // data - _msg_AnalogioAdd = wippersnapper_analogio_AnalogIOAdd_init_default; + memset(&_msg_AnalogioAdd, 0, sizeof(_msg_AnalogioAdd)); // Decode the stream into a AnalogIOAdd message return pb_decode(stream, wippersnapper_analogio_AnalogIOAdd_fields, &_msg_AnalogioAdd); } -/***********************************************************************/ /*! @brief Gets an AnalogIOAdd message struct. @return Pointer to an AnalogIOAdd message struct. */ -/***********************************************************************/ wippersnapper_analogio_AnalogIOAdd *AnalogIOModel::GetAnalogIOAddMsg() { return &_msg_AnalogioAdd; } -/***********************************************************************/ /*! @brief Decodes an AnalogIORemove message from a stream into an AnalogIORemove message struct. @@ -66,37 +64,31 @@ wippersnapper_analogio_AnalogIOAdd *AnalogIOModel::GetAnalogIOAddMsg() { The pb_istream_t stream to decode. @return True if successful, False otherwise. */ -/***********************************************************************/ bool AnalogIOModel::DecodeAnalogIORemove(pb_istream_t *stream) { // Zero-out the AnalogIORemove message struct. to ensure we don't have any old // data - _msg_AnalogioRemove = wippersnapper_analogio_AnalogIORemove_init_default; + memset(&_msg_AnalogioRemove, 0, sizeof(_msg_AnalogioRemove)); // Decode the stream into a AnalogIORemove message return pb_decode(stream, wippersnapper_analogio_AnalogIORemove_fields, &_msg_AnalogioRemove); } -/*****************************************************************************/ /*! @brief Gets an AnalogIORemove message struct. @return Pointer to an AnalogIORemove message struct. */ -/*****************************************************************************/ wippersnapper_analogio_AnalogIORemove *AnalogIOModel::GetAnalogIORemoveMsg() { return &_msg_AnalogioRemove; } -/*****************************************************************************/ /*! @brief Gets an AnalogIOEvent message struct. @return Pointer to an AnalogIOEvent message struct. */ -/*****************************************************************************/ wippersnapper_analogio_AnalogIOEvent *AnalogIOModel::GetAnalogIOEvent() { return &_msg_AnalogioEvent; } -/*****************************************************************************/ /*! @brief Encodes an AnalogIOEvent message. @param pin_name @@ -107,12 +99,11 @@ wippersnapper_analogio_AnalogIOEvent *AnalogIOModel::GetAnalogIOEvent() { The type of sensor event to encode. @return True if successful, False otherwise. */ -/*****************************************************************************/ bool AnalogIOModel::EncodeAnalogIOEvent( char *pin_name, float pin_value, wippersnapper_sensor_SensorType read_type) { // Initialize the AnalogIOEvent message to default values - _msg_AnalogioEvent = wippersnapper_analogio_AnalogIOEvent_init_zero; + memset(&_msg_AnalogioEvent, 0, sizeof(_msg_AnalogioEvent)); // Fill the AnalogIOEvent message's fields strncpy(_msg_AnalogioEvent.pin_name, pin_name, sizeof(_msg_AnalogioEvent.pin_name)); @@ -136,7 +127,6 @@ bool AnalogIOModel::EncodeAnalogIOEvent( &_msg_AnalogioEvent); } -/********************************************************************************/ /*! @brief Encodes an AnalogIOEvent message with a raw pin value. @param pin_name @@ -145,13 +135,11 @@ bool AnalogIOModel::EncodeAnalogIOEvent( The value of the pin. @return True if successful, False otherwise. */ -/********************************************************************************/ bool AnalogIOModel::EncodeAnalogIOEventRaw(char *pin_name, float pin_value) { return EncodeAnalogIOEvent(pin_name, pin_value, wippersnapper_sensor_SensorType_SENSOR_TYPE_RAW); } -/********************************************************************************/ /*! @brief Encodes an AnalogIOEvent message with a voltage pin value. @param pin_name @@ -160,7 +148,6 @@ bool AnalogIOModel::EncodeAnalogIOEventRaw(char *pin_name, float pin_value) { The value of the pin. @return True if successful, False otherwise. */ -/********************************************************************************/ bool AnalogIOModel::EncodeAnalogIOEventVoltage(char *pin_name, float pin_value) { return EncodeAnalogIOEvent( diff --git a/src/components/analogIO/model.h b/src/components/analogIO/model.h index f20fd4c46..cd9a46d8d 100644 --- a/src/components/analogIO/model.h +++ b/src/components/analogIO/model.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/analogIO/model.h * * Model interface for the analogio.proto message. * @@ -16,12 +16,10 @@ #define WS_ANALOGIO_MODEL_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from analogio.proto. */ -/**************************************************************************/ class AnalogIOModel { public: AnalogIOModel(); diff --git a/src/components/checkin/model.cpp b/src/components/checkin/model.cpp index 3ddf0d38b..95b121a06 100644 --- a/src/components/checkin/model.cpp +++ b/src/components/checkin/model.cpp @@ -1,5 +1,5 @@ /*! - * @file model.cpp + * @file src/components/checkin/model.cpp * * Model for the Wippersnapper checkin proto API. * @@ -14,27 +14,23 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief CheckinModel constructor */ -/***********************************************************************/ CheckinModel::CheckinModel() { - _CheckinRequest = wippersnapper_checkin_CheckinRequest_init_default; - _CheckinResponse = wippersnapper_checkin_CheckinResponse_init_default; + memset(&_CheckinRequest, 0, sizeof(_CheckinRequest)); + memset(&_CheckinResponse, 0, sizeof(_CheckinResponse)); + // no-op } -/***********************************************************************/ /*! @brief CheckinModel destructor */ -/***********************************************************************/ CheckinModel::~CheckinModel() { - _CheckinRequest = wippersnapper_checkin_CheckinRequest_init_default; - _CheckinResponse = wippersnapper_checkin_CheckinResponse_init_default; + memset(&_CheckinRequest, 0, sizeof(_CheckinRequest)); + memset(&_CheckinResponse, 0, sizeof(_CheckinResponse)); } -/***********************************************************************/ /*! @brief Fills and creates a CheckinRequest message @param hardware_uid @@ -42,20 +38,17 @@ CheckinModel::~CheckinModel() { @param firmware_version WipperSnapper firmware version. */ -/***********************************************************************/ void CheckinModel::CreateCheckinRequest(const char *hardware_uid, const char *firmware_version) { strcpy(_CheckinRequest.hardware_uid, hardware_uid); strcpy(_CheckinRequest.firmware_version, firmware_version); } -/***********************************************************************/ /*! @brief Encodes a CheckinRequest message @returns True if the message was successfully encoded, False otherwise. */ -/***********************************************************************/ bool CheckinModel::EncodeCheckinRequest() { // Obtain size of the CheckinRequest message size_t CheckinRequestSz; @@ -73,7 +66,6 @@ bool CheckinModel::EncodeCheckinRequest() { &_CheckinRequest); } -/***********************************************************************/ /*! @brief Decodes a CheckinResponse message @param stream @@ -81,17 +73,15 @@ bool CheckinModel::EncodeCheckinRequest() { @returns True if the message was successfully decoded, False otherwise. */ -/***********************************************************************/ bool CheckinModel::DecodeCheckinResponse(pb_istream_t *stream) { + memset(&_CheckinResponse, 0, sizeof(_CheckinResponse)); return pb_decode(stream, wippersnapper_checkin_CheckinResponse_fields, &_CheckinResponse); } -/***********************************************************************/ /*! @brief Parses the fields of a CheckinResponse message. */ -/***********************************************************************/ void CheckinModel::ParseCheckinResponse() { setCheckinResponse(_CheckinResponse.response); setTotalGPIOPins(_CheckinResponse.total_gpio_pins); @@ -99,92 +89,74 @@ void CheckinModel::ParseCheckinResponse() { setReferenceVoltage(_CheckinResponse.reference_voltage); } -/***********************************************************************/ /*! @brief Sets the CheckinResponse message's Response field @param response CheckinResponse message. */ -/***********************************************************************/ void CheckinModel::setCheckinResponse( wippersnapper_checkin_CheckinResponse_Response response) { _response = _CheckinResponse.response; } -/***********************************************************************/ /*! @brief Gets the CheckinResponse message's Response field @returns CheckinResponse message. */ -/***********************************************************************/ wippersnapper_checkin_CheckinResponse_Response CheckinModel::getCheckinResponse() { return _response; }; -/***********************************************************************/ /*! @brief Gets the CheckinRequest message @returns CheckinRequest message. */ -/***********************************************************************/ wippersnapper_checkin_CheckinRequest *CheckinModel::getCheckinRequest() { return &_CheckinRequest; } -/***********************************************************************/ /*! @brief Sets the CheckinResponse message's total GPIO pins field @param total_gpio_pins Total number of GPIO pins. */ -/***********************************************************************/ void CheckinModel::setTotalGPIOPins(int32_t total_gpio_pins) { _total_gpio_pins = total_gpio_pins; } -/***********************************************************************/ /*! @brief Gets the CheckinResponse message's total GPIO pins field @returns Total number of GPIO pins. */ -/***********************************************************************/ int32_t CheckinModel::getTotalGPIOPins() { return _total_gpio_pins; } -/***********************************************************************/ /*! @brief Sets the CheckinResponse message's total analog pins field @param total_analog_pins Total number of analog pins. */ -/***********************************************************************/ void CheckinModel::setTotalAnalogPins(int32_t total_analog_pins) { _total_analog_pins = total_analog_pins; } -/***********************************************************************/ /*! @brief Gets the CheckinResponse message's total analog pins field @returns Total number of analog pins. */ -/***********************************************************************/ int32_t CheckinModel::getTotalAnalogPins() { return _total_analog_pins; } -/***********************************************************************/ /*! @brief Sets the CheckinResponse message's reference voltage field @param reference_voltage Reference voltage. */ -/***********************************************************************/ void CheckinModel::setReferenceVoltage(float reference_voltage) { _reference_voltage = reference_voltage; } -/***********************************************************************/ /*! @brief Gets the CheckinResponse message's reference voltage field @returns Reference voltage. */ -/***********************************************************************/ float CheckinModel::getReferenceVoltage() { return _reference_voltage; } \ No newline at end of file diff --git a/src/components/checkin/model.h b/src/components/checkin/model.h index 280d62a6c..2c17a82eb 100644 --- a/src/components/checkin/model.h +++ b/src/components/checkin/model.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/checkin/model.h * * Model for the Wippersnapper checkin proto API. * @@ -16,12 +16,10 @@ #define WS_CHECKIN_MODEL_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from checkin.proto. */ -/**************************************************************************/ class CheckinModel { public: CheckinModel(); diff --git a/src/components/digitalIO/controller.cpp b/src/components/digitalIO/controller.cpp index 1af117aaa..8e21e75bd 100644 --- a/src/components/digitalIO/controller.cpp +++ b/src/components/digitalIO/controller.cpp @@ -1,5 +1,5 @@ /*! - * @file controller.cpp + * @file src/components/digitalIO/controller.cpp * * Controller for the digitalio.proto API * @@ -14,11 +14,9 @@ */ #include "controller.h" -/***********************************************************************/ /*! @brief DigitalIOController constructor */ -/***********************************************************************/ DigitalIOController::DigitalIOController() { _dio_model = new DigitalIOModel(); _dio_hardware = new DigitalIOHardware(); @@ -27,35 +25,29 @@ DigitalIOController::DigitalIOController() { SetMaxDigitalPins(0); } -/***********************************************************************/ /*! @brief DigitalIOController destructor */ -/***********************************************************************/ DigitalIOController::~DigitalIOController() { delete _dio_model; delete _dio_hardware; } -/***********************************************************************/ /*! @brief Set the maximum number of digital pins @param max_digital_pins The maximum number of digital pins */ -/***********************************************************************/ void DigitalIOController::SetMaxDigitalPins(uint8_t max_digital_pins) { _max_digitalio_pins = max_digital_pins; } -/***********************************************************************/ /*! @brief Add a new digital pin to the controller @param stream The nanopb input stream. @return True if the digital pin was successfully added. */ -/***********************************************************************/ bool DigitalIOController::Handle_DigitalIO_Add(pb_istream_t *stream) { // Early-out if we have reached the maximum number of digital pins if (_digitalio_pins.size() >= _max_digitalio_pins) { @@ -76,7 +68,7 @@ bool DigitalIOController::Handle_DigitalIO_Add(pb_istream_t *stream) { // Check if the provided pin is also the status LED pin if (_dio_hardware->IsStatusLEDPin(pin_name)) - releaseStatusLED(); + ReleaseStatusPixel(); // Deinit the pin if it's already in use if (GetPinIdx(pin_name) != -1) @@ -115,14 +107,12 @@ bool DigitalIOController::Handle_DigitalIO_Add(pb_istream_t *stream) { return true; } -/***********************************************************************/ /*! @brief Removes a digital pin from the controller, if it exists @param stream The nanopb input stream. @return True if the digital pin was successfully removed. */ -/***********************************************************************/ bool DigitalIOController::Handle_DigitalIO_Remove(pb_istream_t *stream) { // Attempt to decode the DigitalIORemove message if (!_dio_model->DecodeDigitalIORemove(stream)) { @@ -146,14 +136,12 @@ bool DigitalIOController::Handle_DigitalIO_Remove(pb_istream_t *stream) { return true; } -/***********************************************************************/ /*! @brief Get the index of a digital output pin @param pin_name The pin's name. @return The index of the digital output pin. */ -/***********************************************************************/ int DigitalIOController::GetPinIdx(uint8_t pin_name) { for (int i = 0; i < _digitalio_pins.size(); i++) { if (_digitalio_pins[i].pin_name == pin_name) { @@ -163,14 +151,12 @@ int DigitalIOController::GetPinIdx(uint8_t pin_name) { return -1; // Pin not found } -/***********************************************************************/ /*! @brief Write a digital pin @param stream The nanopb input stream. @return True if the digital pin was successfully written. */ -/***********************************************************************/ bool DigitalIOController::Handle_DigitalIO_Write(pb_istream_t *stream) { // Attempt to decode the DigitalIOWrite message if (!_dio_model->DecodeDigitalIOWrite(stream)) { @@ -218,7 +204,6 @@ bool DigitalIOController::Handle_DigitalIO_Write(pb_istream_t *stream) { return true; } -/***********************************************************************/ /*! @brief Check if a pin's timer has expired @param pin @@ -227,18 +212,15 @@ bool DigitalIOController::Handle_DigitalIO_Write(pb_istream_t *stream) { The current time. @return True if the pin's timer has expired. */ -/***********************************************************************/ bool DigitalIOController::IsPinTimerExpired(DigitalIOPin *pin, ulong cur_time) { return cur_time - pin->prv_pin_time > pin->pin_period; } -/***********************************************************************/ /*! @brief Print a pin's ID and value @param pin The specified pin. */ -/***********************************************************************/ void DigitalIOController::PrintPinValue(DigitalIOPin *pin) { if (WsV2._sdCardV2->isModeOffline()) return; @@ -248,14 +230,12 @@ void DigitalIOController::PrintPinValue(DigitalIOPin *pin) { WS_DEBUG_PRINTLN(pin->prv_pin_value); } -/***********************************************************************/ /*! @brief Check if a pin's timer has expired @param pin The pin to check. @return True if the pin's timer has expired. */ -/***********************************************************************/ bool DigitalIOController::CheckTimerPin(DigitalIOPin *pin) { ulong cur_time = millis(); // Bail out if the pin's timer has not expired @@ -270,14 +250,12 @@ bool DigitalIOController::CheckTimerPin(DigitalIOPin *pin) { return true; } -/***********************************************************************/ /*! @brief Check if a pin's value has changed @param pin The pin to check. @return True if the pin's value has changed. */ -/***********************************************************************/ bool DigitalIOController::CheckEventPin(DigitalIOPin *pin) { // Get the pin's current value pin->pin_value = _dio_hardware->GetValue(pin->pin_name); @@ -291,7 +269,6 @@ bool DigitalIOController::CheckEventPin(DigitalIOPin *pin) { return true; } -/***********************************************************************/ /*! @brief Encode and publish a pin event @param pin_name @@ -300,7 +277,6 @@ bool DigitalIOController::CheckEventPin(DigitalIOPin *pin) { The pin's value. @return True if the pin event was successfully encoded and published. */ -/***********************************************************************/ bool DigitalIOController::EncodePublishPinEvent(uint8_t pin_name, bool pin_value) { // Prefix pin_name with "D" to match the expected pin name format @@ -329,7 +305,7 @@ bool DigitalIOController::EncodePublishPinEvent(uint8_t pin_name, WS_DEBUG_PRINTLN("[digitalio] Published DigitalIOEvent to broker!") } else { // let's log the event to the SD card - if (!WsV2._sdCardV2->LogGPIOSensorEventToSD( + if (!WsV2._sdCardV2->LogEventGpio( pin_name, pin_value, wippersnapper_sensor_SensorType_SENSOR_TYPE_BOOLEAN)) return false; @@ -338,12 +314,10 @@ bool DigitalIOController::EncodePublishPinEvent(uint8_t pin_name, return true; } -/***********************************************************************/ /*! @brief Iterates through the digital pins and updates their values (if necessary) and publishes the event to the broker. */ -/***********************************************************************/ void DigitalIOController::Update() { // Bail out if we have no digital pins to poll if (_digitalio_pins.empty()) diff --git a/src/components/digitalIO/controller.h b/src/components/digitalIO/controller.h index e1de9666e..2f2ace195 100644 --- a/src/components/digitalIO/controller.h +++ b/src/components/digitalIO/controller.h @@ -1,5 +1,5 @@ /*! - * @file controller.h + * @file src/components/digitalIO/controller.h * * Controller for the digitalio API * @@ -39,13 +39,11 @@ struct DigitalIOPin { class DigitalIOModel; // Forward declaration class DigitalIOHardware; // Forward declaration -/**************************************************************************/ /*! @brief Routes messages using the digitalio.proto API to the appropriate hardware and model classes, controls and tracks the state of the hardware's digital I/O pins. */ -/**************************************************************************/ class DigitalIOController { public: DigitalIOController(); diff --git a/src/components/digitalIO/hardware.cpp b/src/components/digitalIO/hardware.cpp index f6503e1a8..98656c31e 100644 --- a/src/components/digitalIO/hardware.cpp +++ b/src/components/digitalIO/hardware.cpp @@ -1,5 +1,5 @@ /*! - * @file hardware.cpp + * @file src/components/digitalIO/hardware.cpp * * Hardware driver for the digitalio.proto API * @@ -14,21 +14,16 @@ */ #include "hardware.h" -/***********************************************************************/ /*! @brief DigitalIOHardware constructor */ -/***********************************************************************/ DigitalIOHardware::DigitalIOHardware() {} -/***********************************************************************/ /*! @brief DigitalIOHardware destructor */ -/***********************************************************************/ DigitalIOHardware::~DigitalIOHardware() {} -/***********************************************************************/ /*! @brief Configures a digital pin. @param name @@ -37,7 +32,6 @@ DigitalIOHardware::~DigitalIOHardware() {} The pin's direction. @return True if the pin was successfully configured. False otherwise. */ -/***********************************************************************/ bool DigitalIOHardware::ConfigurePin( uint8_t name, wippersnapper_digitalio_DigitalIODirection direction) { // Configure an output pin @@ -68,13 +62,11 @@ bool DigitalIOHardware::ConfigurePin( return true; } -/***********************************************************************/ /*! @brief Deinitializes a digital pin. @param pin_name The digital pin to deinitialize. */ -/***********************************************************************/ void DigitalIOHardware::deinit(uint8_t pin_name) { // Turn off pin output and reset mode to hi-z floating state digitalWrite(pin_name, LOW); @@ -86,7 +78,6 @@ void DigitalIOHardware::deinit(uint8_t pin_name) { } } -/***********************************************************************/ /*! @brief Sets a digital pin's value. @param pin_name @@ -94,31 +85,26 @@ void DigitalIOHardware::deinit(uint8_t pin_name) { @param pin_value The pin's value. */ -/***********************************************************************/ void DigitalIOHardware::SetValue(uint8_t pin_name, bool pin_value) { digitalWrite(pin_name, pin_value ? HIGH : LOW); } -/***********************************************************************/ /*! @brief Gets a digital pin's value. @param pin_name The pin's name. @return The pin's value. */ -/***********************************************************************/ bool DigitalIOHardware::GetValue(uint8_t pin_name) { return digitalRead(pin_name); } -/***********************************************************************/ /*! @brief Checks if a pin is the status LED pin. @param pin_name The pin's name. @return True if the pin is the status LED pin. */ -/***********************************************************************/ bool DigitalIOHardware::IsStatusLEDPin(uint8_t pin_name) { #ifdef STATUS_LED_PIN return pin_name == STATUS_LED_PIN; diff --git a/src/components/digitalIO/hardware.h b/src/components/digitalIO/hardware.h index e471c6b7d..067ee32f3 100644 --- a/src/components/digitalIO/hardware.h +++ b/src/components/digitalIO/hardware.h @@ -1,7 +1,7 @@ /*! - * @file model.h + * @file src/components/digitalIO/hardware.h * - * Model for the digitalio.proto message. + * Hardware for the digitalio.proto message. * * Adafruit invests time and resources providing this open source code, * please support Adafruit and open-source hardware by purchasing @@ -16,11 +16,9 @@ #define WS_DIGITALIO_HARDWARE_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Interface for interacting with hardware's digital I/O pin API. */ -/**************************************************************************/ class DigitalIOHardware { public: DigitalIOHardware(); diff --git a/src/components/digitalIO/model.cpp b/src/components/digitalIO/model.cpp index 6643b1c49..b36404a96 100644 --- a/src/components/digitalIO/model.cpp +++ b/src/components/digitalIO/model.cpp @@ -1,5 +1,5 @@ /*! - * @file model.cpp + * @file src/components/digitalIO/model.cpp * * Model for the digitalio.proto message. * @@ -14,74 +14,67 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief DigitalIOModel constructor */ -/***********************************************************************/ DigitalIOModel::DigitalIOModel() { - _msg_dio_add = wippersnapper_digitalio_DigitalIOAdd_init_default; - _msg_dio_remove = wippersnapper_digitalio_DigitalIORemove_init_default; - _msg_dio_event = wippersnapper_digitalio_DigitalIOEvent_init_default; - _msg_dio_write = wippersnapper_digitalio_DigitalIOWrite_init_default; + memset(&_msg_dio_add, 0, sizeof(_msg_dio_add)); + memset(&_msg_dio_remove, 0, sizeof(_msg_dio_remove)); + memset(&_msg_dio_event, 0, sizeof(_msg_dio_event)); + memset(&_msg_dio_write, 0, sizeof(_msg_dio_write)); + // no-op } -/***********************************************************************/ /*! @brief DigitalIOModel destructor */ -/***********************************************************************/ -DigitalIOModel::~DigitalIOModel() {} +DigitalIOModel::~DigitalIOModel() { + memset(&_msg_dio_add, 0, sizeof(_msg_dio_add)); + memset(&_msg_dio_remove, 0, sizeof(_msg_dio_remove)); + memset(&_msg_dio_event, 0, sizeof(_msg_dio_event)); + memset(&_msg_dio_write, 0, sizeof(_msg_dio_write)); +} -/***********************************************************************/ /*! @brief Parses a DigitalIOAdd message. @return DigitalIOAdd message object. */ -/***********************************************************************/ wippersnapper_digitalio_DigitalIOAdd *DigitalIOModel::GetDigitalIOAddMsg() { return &_msg_dio_add; } -/***********************************************************************/ /*! @brief Parses a DigitalIORemove message. @param stream The nanopb input stream. @return DigitalIORemove message object. */ -/***********************************************************************/ bool DigitalIOModel::DecodeDigitalIORemove(pb_istream_t *stream) { // Zero-out the DigitalIORemove message struct. to ensure we don't have any // old data - _msg_dio_remove = wippersnapper_digitalio_DigitalIORemove_init_default; + memset(&_msg_dio_remove, 0, sizeof(_msg_dio_remove)); // Decode the stream into a DigitalIORemove message return pb_decode(stream, wippersnapper_digitalio_DigitalIORemove_fields, &_msg_dio_remove); } -/***********************************************************************/ /*! @brief Gets a DigitalIOWrite message. @return DigitalIOWrite message object. */ -/***********************************************************************/ wippersnapper_digitalio_DigitalIOWrite *DigitalIOModel::GetDigitalIOWriteMsg() { return &_msg_dio_write; } -/***********************************************************************/ /*! @brief Gets a DigitalIOEvent message. @return DigitalIOEvent message object. */ -/***********************************************************************/ wippersnapper_digitalio_DigitalIOEvent *DigitalIOModel::GetDigitalIOEventMsg() { return &_msg_dio_event; } -/***********************************************************************/ /*! @brief Decodes a DigitalIOAdd message into the _msg_dio_add object from a nanopb stream. @@ -89,18 +82,16 @@ wippersnapper_digitalio_DigitalIOEvent *DigitalIOModel::GetDigitalIOEventMsg() { The nanopb input stream. @return True if the DigitalIOAdd message was successfully decoded. */ -/***********************************************************************/ bool DigitalIOModel::DecodeDigitalIOAdd(pb_istream_t *stream) { // Zero-out the DigitalIOAdd message struct. to ensure we don't have any old // data - _msg_dio_add = wippersnapper_digitalio_DigitalIOAdd_init_default; + memset(&_msg_dio_add, 0, sizeof(_msg_dio_add)); // Decode the stream into a DigitalIOAdd message return pb_decode(stream, wippersnapper_digitalio_DigitalIOAdd_fields, &_msg_dio_add); } -/***********************************************************************/ /*! @brief Decodes a DigitalIOWrite message into the _msg_dio_write object from a nanopb stream. @@ -108,17 +99,15 @@ bool DigitalIOModel::DecodeDigitalIOAdd(pb_istream_t *stream) { The nanopb input stream. @return True if the DigitalIOWrite message was successfully decoded. */ -/***********************************************************************/ bool DigitalIOModel::DecodeDigitalIOWrite(pb_istream_t *stream) { // Zero-out the DigitalIOWrite message struct. to ensure we don't have any old // data - _msg_dio_write = wippersnapper_digitalio_DigitalIOWrite_init_default; + memset(&_msg_dio_write, 0, sizeof(_msg_dio_write)); // Decode the stream into a DigitalIOWrite message return pb_decode(stream, wippersnapper_digitalio_DigitalIOWrite_fields, &_msg_dio_write); } -/***********************************************************************/ /*! @brief Encodes a DigitalIOEvent message into the _msg_dio_event object. @@ -129,10 +118,9 @@ bool DigitalIOModel::DecodeDigitalIOWrite(pb_istream_t *stream) { @return True if the DigitalIOEvent message was successfully encoded. False if encoding resulted in a failure. */ -/***********************************************************************/ bool DigitalIOModel::EncodeDigitalIOEvent(char *pin_name, bool value) { // Initialize the DigitalIOEvent - _msg_dio_event = wippersnapper_digitalio_DigitalIOEvent_init_default; + memset(&_msg_dio_event, 0, sizeof(_msg_dio_event)); // Fill the DigitalIOEvent strncpy(_msg_dio_event.pin_name, pin_name, sizeof(_msg_dio_event.pin_name)); _msg_dio_event.has_value = true; diff --git a/src/components/digitalIO/model.h b/src/components/digitalIO/model.h index d0840efd0..27bcaa3df 100644 --- a/src/components/digitalIO/model.h +++ b/src/components/digitalIO/model.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/digitalIO/model.h * * Model for the digitalio.proto message. * @@ -16,12 +16,10 @@ #define WS_DIGITALIO_MODEL_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from digitalio.proto. */ -/**************************************************************************/ class DigitalIOModel { public: DigitalIOModel(); diff --git a/src/components/ds18x20/controller.cpp b/src/components/ds18x20/controller.cpp index 06a4a3b2c..6b21617f3 100644 --- a/src/components/ds18x20/controller.cpp +++ b/src/components/ds18x20/controller.cpp @@ -1,5 +1,5 @@ /*! - * @file controller.cpp + * @file src/components/ds18x20/controller.cpp * * Controller for the ds18x20.proto API * @@ -14,27 +14,22 @@ */ #include "controller.h" -/***********************************************************************/ /*! @brief DS18X20Controller constructor */ -/***********************************************************************/ DS18X20Controller::DS18X20Controller() { _num_drivers = 0; _DS18X20_model = new DS18X20Model(); } -/***********************************************************************/ /*! @brief DS18X20Controller destructor */ -/***********************************************************************/ DS18X20Controller::~DS18X20Controller() { _num_drivers = 0; delete _DS18X20_model; } -/***********************************************************************/ /*! @brief Handles a Ds18x20Add message from the broker. Attempts to initialize a OneWire bus on the requested pin, attempts to @@ -47,7 +42,6 @@ DS18X20Controller::~DS18X20Controller() { added to the controller, and a response was succesfully published to the broker. False otherwise. */ -/***********************************************************************/ bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { // Attempt to decode the incoming message into a Ds18x20Add message if (!_DS18X20_model->DecodeDS18x20Add(stream)) { @@ -64,9 +58,13 @@ bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { // Extract the OneWire pin from the message uint8_t pin_name = atoi(_DS18X20_model->GetDS18x20AddMsg()->onewire_pin + 1); - // Initialize the DS18X20Hardware object +#ifdef ARDUINO_ARCH_SAMD + auto new_dsx_driver = new DS18X20Hardware(pin_name, _num_drivers); + std::unique_ptr unique_driver(new_dsx_driver); +#else auto new_dsx_driver = std::make_unique(pin_name, _num_drivers); +#endif // Attempt to get the sensor's ID on the OneWire bus to show it's been init'd bool is_initialized = new_dsx_driver->GetSensor(); @@ -103,7 +101,11 @@ bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { // If the sensor was successfully initialized, add it to the controller if (is_initialized == true) { +#ifdef ARDUINO_ARCH_SAMD + _DS18X20_pins.push_back(std::move(unique_driver)); +#else _DS18X20_pins.push_back(std::move(new_dsx_driver)); +#endif _num_drivers++; } @@ -150,7 +152,6 @@ bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { return true; } -/***********************************************************************/ /*! @brief Handles a Ds18x20Remove message from the broker. Attempts to remove a DS18X20Hardware object from the controller and @@ -160,7 +161,6 @@ bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { @return True if the sensor was successfully removed from the controller, False otherwise. */ -/***********************************************************************/ bool DS18X20Controller::Handle_Ds18x20Remove(pb_istream_t *stream) { WS_DEBUG_PRINT("Removing DS18X20 sensor..."); // Attempt to decode the stream @@ -185,11 +185,9 @@ bool DS18X20Controller::Handle_Ds18x20Remove(pb_istream_t *stream) { return true; } -/***********************************************************************/ /*! @brief Update/polling loop for the DS18X20 controller. */ -/***********************************************************************/ void DS18X20Controller::update() { #ifdef DEBUG_PROFILE unsigned long total_start_time = millis(); @@ -271,7 +269,7 @@ void DS18X20Controller::update() { } WS_DEBUG_PRINTLN("Published!"); } else { - if (!WsV2._sdCardV2->LogDS18xSensorEventToSD( + if (!WsV2._sdCardV2->LogEventDs18x( _DS18X20_model->GetDS18x20EventMsg())) { WS_DEBUG_PRINTLN( "ERROR | DS18x20: Failed to log DS18x20 event to SD card"); diff --git a/src/components/ds18x20/controller.h b/src/components/ds18x20/controller.h index 037e517b7..ca51beeab 100644 --- a/src/components/ds18x20/controller.h +++ b/src/components/ds18x20/controller.h @@ -1,5 +1,5 @@ /*! - * @file controller.h + * @file src/components/ds18x20/controller.h * * Controller for the DS18X20 API * @@ -23,11 +23,9 @@ class Wippersnapper_V2; ///< Forward declaration class DS18X20Model; ///< Forward declaration class DS18X20Hardware; ///< Forward declaration -/**************************************************************************/ /*! @brief Routes messages between the ds18x20.proto API and the hardware. */ -/**************************************************************************/ class DS18X20Controller { public: DS18X20Controller(); diff --git a/src/components/ds18x20/hardware.cpp b/src/components/ds18x20/hardware.cpp index beaf37aa0..d759b9730 100644 --- a/src/components/ds18x20/hardware.cpp +++ b/src/components/ds18x20/hardware.cpp @@ -1,5 +1,5 @@ /*! - * @file hardware.cpp + * @file src/components/ds18x20/hardware.cpp * * Hardware interface for the ds18x20.proto API * @@ -14,7 +14,6 @@ */ #include "hardware.h" -/***********************************************************************/ /*! @brief DS18X20Hardware constructor @param onewire_pin @@ -22,7 +21,6 @@ @param sensor_num Unique identifier for the sensor driver. */ -/***********************************************************************/ DS18X20Hardware::DS18X20Hardware(uint8_t onewire_pin, int sensor_num) : _drv_therm(_ow) { is_read_temp_c = false; @@ -32,24 +30,20 @@ DS18X20Hardware::DS18X20Hardware(uint8_t onewire_pin, int sensor_num) _onewire_pin = onewire_pin; } -/***********************************************************************/ /*! @brief DS18X20Hardware destructor */ -/***********************************************************************/ DS18X20Hardware::~DS18X20Hardware() { pinMode(_onewire_pin, INPUT); // Set the pin to hi-z and release it for other uses } -/****************************************************************************/ /*! @brief Initializes the DS18X20 sensor driver and verifies that the sensor is present on the OneWire bus. @returns True if the sensor was successfully initialized, False otherwise. */ -/****************************************************************************/ bool DS18X20Hardware::GetSensor() { // Initialize the DS18X20 driver #ifdef ARDUINO_ARCH_RP2040 @@ -80,44 +74,36 @@ bool DS18X20Hardware::GetSensor() { return ec == OneWireNg::EC_SUCCESS; } -/***********************************************************************/ /*! @brief Gets the pin used as a OneWire bus. @returns The OneWire bus pin. */ -/***********************************************************************/ uint8_t DS18X20Hardware::GetOneWirePin() { return _onewire_pin; } -/***********************************************************************/ /*! @brief Sets the name of the OneWire bus pin. @param prettyOWPinName The name of the OneWire bus pin (non-logical pin name, includes the "D" or "A" prefix). */ -/***********************************************************************/ void DS18X20Hardware::setOneWirePinName(const char *prettyOWPinName) { strncpy(_onewire_pin_name, prettyOWPinName, sizeof(_onewire_pin_name)); _onewire_pin_name[sizeof(_onewire_pin_name) - 1] = '\0'; } -/***********************************************************************/ /*! @brief Gets the name of the OneWire bus pin. @returns The name of the OneWire bus pin (non-logical pin name, includes the "D" or "A" prefix). */ -/***********************************************************************/ const char *DS18X20Hardware::getOneWirePinName() { return _onewire_pin_name; } -/*************************************************************************/ /*! @brief Sets the DS18X20 sensor's resolution. @param resolution The desired resolution of the DS18X20 sensor, in bits (from 9 to 12). */ -/*************************************************************************/ void DS18X20Hardware::SetResolution(int resolution) { // Set the resolution of the DS18X20 sensor driver switch (resolution) { @@ -149,56 +135,46 @@ void DS18X20Hardware::SetResolution(int resolution) { _drv_therm.copyScratchpadAll(false); } -/*************************************************************************/ /*! @brief Sets the timer to read from the sensor. @param period The desired period to read the sensor, in seconds. */ -/*************************************************************************/ void DS18X20Hardware::SetPeriod(float period) { _period = period * 1000; // Convert to milliseconds _prv_period = 0; // Also reset the previous period whenever we set a // new period } -/*************************************************************************/ /*! @brief Obtains the current time in milliseconds and compares it to the last time the sensor was polled. @returns True if the timer has expired, False otherwise. */ -/*************************************************************************/ bool DS18X20Hardware::IsTimerExpired() { return millis() - _prv_period > _period; } -/*************************************************************************/ /*! @brief Gets the temperature value last read by the sensor, in Celsius. @returns The temperature in Celsius. */ -/*************************************************************************/ float DS18X20Hardware::GetTemperatureC() { return _temp_c; } -/*************************************************************************/ /*! @brief Gets the temperature value last read by the sensor, in Fahrenheit. @returns The temperature in Fahrenheit. */ -/*************************************************************************/ float DS18X20Hardware::GetTemperatureF() { _temp_f = _temp_c * 9.0 / 5.0 + 32.0; return _temp_f; } -/*************************************************************************/ /*! @brief Attempts to obtain the temperature from the sensor, in degrees Celsius. @returns True if the temperature was successfully read, False otherwise. */ -/*************************************************************************/ bool DS18X20Hardware::ReadTemperatureC() { // Start temperature conversion for the first identified sensor on the OneWire // bus diff --git a/src/components/ds18x20/hardware.h b/src/components/ds18x20/hardware.h index 95e8b6663..f6f020ff4 100644 --- a/src/components/ds18x20/hardware.h +++ b/src/components/ds18x20/hardware.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/ds18x20/hardware.h * * Hardware implementation for the ds18x20.proto message. * @@ -26,12 +26,10 @@ 1 ///< 4 drivers handled by PIO1, 4 drivers handled by PIO2 #endif -/**************************************************************************/ /*! @brief Interface for interacting with the's DallasTemp and OneWire APIs. */ -/**************************************************************************/ class DS18X20Hardware { public: DS18X20Hardware(uint8_t onewire_pin, int sensor_num); diff --git a/src/components/ds18x20/model.cpp b/src/components/ds18x20/model.cpp index 17dd039c0..3f28bc6a4 100644 --- a/src/components/ds18x20/model.cpp +++ b/src/components/ds18x20/model.cpp @@ -1,5 +1,5 @@ /*! - * @file model.cpp + * @file src/components/ds18x20/model.cpp * * Model for the ds18x20.proto message. * @@ -14,61 +14,55 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief DS18X20Model constructor */ -/***********************************************************************/ DS18X20Model::DS18X20Model() { - // Initialize the DS18X20 messages - _msg_DS18x20Add = wippersnapper_ds18x20_Ds18x20Add_init_zero; - _msg_DS18x20Added = wippersnapper_ds18x20_Ds18x20Added_init_zero; - _msg_DS18x20Remove = wippersnapper_ds18x20_Ds18x20Remove_init_zero; + memset(&_msg_DS18x20Add, 0, sizeof(_msg_DS18x20Add)); + memset(&_msg_DS18x20Added, 0, sizeof(_msg_DS18x20Added)); + memset(&_msg_DS18x20Remove, 0, sizeof(_msg_DS18x20Remove)); + memset(&_msg_DS18x20Event, 0, sizeof(_msg_DS18x20Event)); } -/***********************************************************************/ /*! @brief DS18X20Model destructor */ -/***********************************************************************/ -DS18X20Model::~DS18X20Model() {} +DS18X20Model::~DS18X20Model() { + memset(&_msg_DS18x20Add, 0, sizeof(_msg_DS18x20Add)); + memset(&_msg_DS18x20Added, 0, sizeof(_msg_DS18x20Added)); + memset(&_msg_DS18x20Remove, 0, sizeof(_msg_DS18x20Remove)); + memset(&_msg_DS18x20Event, 0, sizeof(_msg_DS18x20Event)); +} -/***********************************************************************/ /*! @brief Attempts to decode a Ds18x20Add message from the broker. @param stream The nanopb input stream. @return True if the message was successfully decoded, False otherwise. */ -/***********************************************************************/ bool DS18X20Model::DecodeDS18x20Add(pb_istream_t *stream) { - _msg_DS18x20Add = wippersnapper_ds18x20_Ds18x20Add_init_zero; + memset(&_msg_DS18x20Add, 0, sizeof(_msg_DS18x20Add)); // Attempt to decode the stream into a Ds18x20Add message return pb_decode(stream, wippersnapper_ds18x20_Ds18x20Add_fields, &_msg_DS18x20Add); } -/***********************************************************************/ /*! @brief Gets a pointer to the Ds18x20Add message. @return Pointer to the Ds18x20Add message. */ -/***********************************************************************/ wippersnapper_ds18x20_Ds18x20Add *DS18X20Model::GetDS18x20AddMsg() { return &_msg_DS18x20Add; } -/***********************************************************************/ /*! @brief Returns a pointer to the Ds18x20Added message. @return Pointer to the Ds18x20Added message. */ -/***********************************************************************/ wippersnapper_ds18x20_Ds18x20Added *DS18X20Model::GetDS18x20AddedMsg() { return &_msg_DS18x20Added; } -/***********************************************************************/ /*! @brief Encodes a Ds18x20Added message. @param onewire_pin @@ -79,10 +73,9 @@ wippersnapper_ds18x20_Ds18x20Added *DS18X20Model::GetDS18x20AddedMsg() { @return True if the message was successfully encoded, False otherwise. */ -/***********************************************************************/ bool DS18X20Model::EncodeDS18x20Added(char *onewire_pin, bool is_init) { // Fill the Ds18x20Added message - _msg_DS18x20Added = wippersnapper_ds18x20_Ds18x20Added_init_zero; + memset(&_msg_DS18x20Added, 0, sizeof(_msg_DS18x20Added)); _msg_DS18x20Added.is_initialized = is_init; strcpy(_msg_DS18x20Added.onewire_pin, onewire_pin); @@ -98,46 +91,38 @@ bool DS18X20Model::EncodeDS18x20Added(char *onewire_pin, bool is_init) { &_msg_DS18x20Added); } -/*************************************************************************/ /*! @brief Attempts to decode a Ds18x20Remove message from the broker. @param stream The nanopb input stream. @return True if the message was successfully decoded, False otherwise. */ -/*************************************************************************/ bool DS18X20Model::DecodeDS18x20Remove(pb_istream_t *stream) { - _msg_DS18x20Remove = wippersnapper_ds18x20_Ds18x20Remove_init_zero; + memset(&_msg_DS18x20Remove, 0, sizeof(_msg_DS18x20Remove)); return pb_decode(stream, wippersnapper_ds18x20_Ds18x20Remove_fields, &_msg_DS18x20Remove); } -/*************************************************************************/ /*! @brief Gets a pointer to the Ds18x20Remove message. @return Pointer to the Ds18x20Remove message. */ -/*************************************************************************/ wippersnapper_ds18x20_Ds18x20Remove *DS18X20Model::GetDS18x20RemoveMsg() { return &_msg_DS18x20Remove; } -/*************************************************************************/ /*! @brief Gets a pointer to the Ds18x20Event message. @return Pointer to the Ds18x20Event message. */ -/*************************************************************************/ wippersnapper_ds18x20_Ds18x20Event *DS18X20Model::GetDS18x20EventMsg() { return &_msg_DS18x20Event; } -/*************************************************************************/ /*! @brief Encodes a Ds18x20Event message. @return True if the message was successfully encoded, False otherwise. */ -/*************************************************************************/ bool DS18X20Model::EncodeDs18x20Event() { // take the filled _msg_DS18x20Event we built in the controller and encode it size_t sz_msg; @@ -151,20 +136,17 @@ bool DS18X20Model::EncodeDs18x20Event() { &_msg_DS18x20Event); } -/*************************************************************************/ /*! @brief Initializes the Ds18x20Event message. @param ow_pin_name The OneWire bus pin name. */ -/*************************************************************************/ void DS18X20Model::InitDS18x20EventMsg(const char *ow_pin_name) { - _msg_DS18x20Event = wippersnapper_ds18x20_Ds18x20Event_init_zero; + memset(&_msg_DS18x20Event, 0, sizeof(_msg_DS18x20Event)); _msg_DS18x20Event.sensor_events_count = 0; strcpy(_msg_DS18x20Event.onewire_pin, ow_pin_name); } -/*************************************************************************/ /*! @brief Adds a "sensor event" to the Ds18x20Event message. @param sensor_type @@ -172,7 +154,6 @@ void DS18X20Model::InitDS18x20EventMsg(const char *ow_pin_name) { @param sensor_value The event's value. */ -/*************************************************************************/ void DS18X20Model::addSensorEvent(wippersnapper_sensor_SensorType sensor_type, float sensor_value) { _msg_DS18x20Event.sensor_events[_msg_DS18x20Event.sensor_events_count].type = diff --git a/src/components/ds18x20/model.h b/src/components/ds18x20/model.h index c7efaac4b..57cd60c8b 100644 --- a/src/components/ds18x20/model.h +++ b/src/components/ds18x20/model.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/ds18x20/model.h * * Model interface for the DS18X20.proto message. * @@ -16,12 +16,10 @@ #define WS_DS18X20_MODEL_H #include "Wippersnapper_V2.h" -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from DS18X20.proto. */ -/**************************************************************************/ class DS18X20Model { public: DS18X20Model(); diff --git a/src/components/gps/controller.cpp b/src/components/gps/controller.cpp new file mode 100644 index 000000000..275a39598 --- /dev/null +++ b/src/components/gps/controller.cpp @@ -0,0 +1,224 @@ +/*! + * @file src/components/gps/controller.cpp + * + * Controller for WipperSnapper's GPS component, bridges between the GPS.proto + * API, the model, and the hardware layer. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/*! + * @brief Constructor for GPSController. + */ +GPSController::GPSController() { + _gps_model = new GPSModel(); + has_gps = false; +} + +/*! + * @brief Destructor for GPSController. + */ +GPSController::~GPSController() { + // Clean up model + if (_gps_model) { + delete _gps_model; + _gps_model = nullptr; + } + has_gps = false; +} + +/*! + * @brief Adds an I2C GPS hardware instance to the controller. + * @param wire Pointer to the TwoWire instance for I2C communication. + * @param i2c_addr I2C address of the GPS device. + * @param gps_config Pointer to the GPS configuration message. + * @return True if the GPS was added successfully, false otherwise. + */ +bool GPSController::AddGPS(TwoWire *wire, uint32_t i2c_addr, + wippersnapper_gps_GPSConfig *gps_config) { + GPSHardware *gps_hw = new GPSHardware(); + + if (!gps_hw->SetInterface(wire)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to set module interface!"); + delete gps_hw; + return false; + } + + gps_hw->SetI2CAddress(i2c_addr); + if (!gps_hw->begin()) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to initialize module!"); + delete gps_hw; + return false; + } + + if (!gps_hw->Handle_GPSConfig(gps_config)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Configuration failed!"); + delete gps_hw; + return false; + } + + _gps_drivers.push_back(gps_hw); + WS_DEBUG_PRINTLN("[gps] GPS hardware added successfully!"); + has_gps = true; + return true; +} + +/*! + * @brief Adds a GPS hardware serial instance to the controller. + * @param serial Pointer to the HardwareSerial instance for GPS communication. + * @param gps_config Pointer to the GPS configuration message. + * @return True if the GPS was added successfully, false otherwise. + */ +bool GPSController::AddGPS(HardwareSerial *serial, uint32_t baudrate, + wippersnapper_gps_GPSConfig *gps_config) { + GPSHardware *gps_hw = new GPSHardware(); + + if (!gps_hw->SetInterface(serial, baudrate)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to set GPS UART interface!"); + delete gps_hw; + return false; + } + + if (!gps_hw->begin()) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to initialize GPS hardware!"); + delete gps_hw; + return false; + } + // Required - let the GPS spit out its initial data + delay(1000); + + if (!gps_hw->Handle_GPSConfig(gps_config)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to configure GPS!"); + delete gps_hw; + return false; + } + + _gps_drivers.push_back(gps_hw); + WS_DEBUG_PRINTLN("[gps] GPS hardware added successfully!"); + has_gps = true; + return true; +} + +#if HAS_SW_SERIAL +/*! + * @brief Adds a GPS software serial instance to the controller. + * @param serial Pointer to the SoftwareSerial instance for GPS communication. + * @param gps_config Pointer to the GPS configuration message. + * @return True if the GPS was added successfully, false otherwise. + */ +bool GPSController::AddGPS(SoftwareSerial *serial_sw, uint32_t baudrate, + wippersnapper_gps_GPSConfig *gps_config) { + GPSHardware *gps_hw = new GPSHardware(); + + if (!gps_hw->SetInterface(serial_sw, baudrate)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to set GPS UART interface!"); + delete gps_hw; + return false; + } + + if (!gps_hw->begin()) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to initialize GPS hardware!"); + delete gps_hw; + return false; + } + // Required - let the GPS spit out its initial data + delay(1000); + + if (!gps_hw->Handle_GPSConfig(gps_config)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to configure GPS!"); + delete gps_hw; + return false; + } + + _gps_drivers.push_back(gps_hw); + WS_DEBUG_PRINTLN("[gps] GPS hardware added successfully!"); + has_gps = true; + return true; +} +#endif // HAS_SW_SERIAL + +/*! + * @brief Gets the current GPS datetime. + * @return The current GPS datetime. + */ +DateTime GPSController::GetGPSDateTime() { + return _gps_model->GetPrvGPSDateTime(); +} + +/*! + * @brief Updates the GPSController, polling the GPS hardware for data. + * This function checks if the read period has elapsed and processes the GPS + * data accordingly. + */ +void GPSController::update() { + if (_gps_drivers.empty()) + return; // bail-out! + + for (GPSHardware *drv : _gps_drivers) { + // Did read period elapse? + ulong cur_time = millis(); + if (cur_time - drv->GetPollPeriodPrv() < drv->GetPollPeriod()) + continue; // Not yet elapsed, skip this driver + + // Discard the GPS buffer before we attempt to do a fresh read + drv->ReadDiscardBuffer(); + + // Poll the GPS hardware for update_rate ms + // and store NMEA sentences in a ring buffer + drv->PollStoreSentences(); + + // Parse each NMEA sentence in the buffer + _gps_model->CreateGPSEvent(); + char nmea_sentence[MAX_LEN_NMEA_SENTENCE]; + bool has_gps_event = false; + while (drv->NmeaBufPop(nmea_sentence) != -1) { + // Let the driver parse the NMEA sentence + if (!drv->ParseNMEASentence(nmea_sentence)) { + continue; // Skip this sentence if parsing failed + } else { + has_gps_event = true; // We have a valid NMEA sentence + } + + // Using the Model, process the NMEA sentence into a GPSEvent + _gps_model->ProcessNMEASentence(nmea_sentence, drv); + + // We did not create a GPSEvent because the NMEA sentences were not + // GGA/RMC or parsed correctly + if (!has_gps_event) + continue; + + // Encode and process the GPSEvent + bool did_encode = _gps_model->EncodeGPSEvent(); + if (did_encode) { + if (!WsV2._sdCardV2->isModeOffline()) { + // Publish the GPSEvent to IO + WS_DEBUG_PRINT("[gps] Encoding and publishing GPSEvent to IO..."); + if (!WsV2.PublishSignal( + wippersnapper_signal_DeviceToBroker_gps_event_tag, + _gps_model->GetGPSEvent())) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to publish GPSEvent!"); + } else { + WS_DEBUG_PRINTLN("...ok!"); + } + } else { + // Set the Datetime for the GPSEvent + // Log the GPSEvent to SD card + if (!WsV2._sdCardV2->LogEventGps(_gps_model->GetGPSEvent())) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to log GPSEvent!"); + } + } + } else { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to encode GPSEvent!"); + } + drv->SetPollPeriodPrv(cur_time); + } + } +} \ No newline at end of file diff --git a/src/components/gps/controller.h b/src/components/gps/controller.h new file mode 100644 index 000000000..d173c9bb7 --- /dev/null +++ b/src/components/gps/controller.h @@ -0,0 +1,52 @@ +/*! + * @file src/components/gps/controller.h + * + * Controller for WipperSnapper's GPS component, bridges between the GPS.proto + * API, the model, and the hardware layer. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_GPS_CONTROLLER_H +#define WS_GPS_CONTROLLER_H +#include "../uart/serial_config.h" +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" +#include + +class Wippersnapper_V2; ///< Forward declaration +class GPSModel; ///< Forward declaration +class GPSHardware; ///< Forward declaration + +/*! + @brief Routes messages between the GPS.proto API and the hardware. +*/ +class GPSController { +public: + GPSController(); + ~GPSController(); + bool AddGPS(HardwareSerial *serial, uint32_t baudrate, + wippersnapper_gps_GPSConfig *gps_config); +#if HAS_SW_SERIAL + bool AddGPS(SoftwareSerial *serial, uint32_t baudrate, + wippersnapper_gps_GPSConfig *gps_config); +#endif // HAS_SW_SERIAL + bool AddGPS(TwoWire *wire, uint32_t i2c_addr, + wippersnapper_gps_GPSConfig *gps_config); + void update(); + bool has_gps; + DateTime GetGPSDateTime(); + +private: + GPSModel *_gps_model; ///< GPS model instance + std::vector _gps_drivers; ///< GPS hardware instances +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_GPS_CONTROLLER_H \ No newline at end of file diff --git a/src/components/gps/hardware.cpp b/src/components/gps/hardware.cpp new file mode 100644 index 000000000..58cbacb83 --- /dev/null +++ b/src/components/gps/hardware.cpp @@ -0,0 +1,744 @@ +/*! + * @file src/components/gps/controller.cpp + * + * Interface for WipperSnapper's GPS component hardware. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" + +/*! + * @brief Constructor + */ +GPSHardware::GPSHardware() { + _iface_type = GPS_IFACE_NONE; + _driver_type = GPS_DRV_NONE; + _nmea_baud_rate = DEFAULT_MTK_NMEA_BAUD_RATE; + _nmea_update_rate = DEFAULT_MTK_NMEA_UPDATE_RATE; + + // Initialize NMEA buffer + _nmea_buff.head = 0; + _nmea_buff.tail = 0; + _nmea_buff.maxlen = MAX_NMEA_SENTENCES; +} + +/*! + * @brief Destructor + */ +GPSHardware::~GPSHardware() { + _iface_type = GPS_IFACE_NONE; + _driver_type = GPS_DRV_NONE; + if (_ada_gps) { + delete _ada_gps; + _ada_gps = nullptr; + } +} + +/*! + * @brief Helper function that reads and discards data requested by the I2C bus. + */ +void GPSHardware::I2cReadDiscard() { + // TODO: Let's hope this works for UBX too! Remove if its ok + _wire->flush(); + uint8_t bytes_requested = 32; + _wire->requestFrom(_addr, bytes_requested); + while (_wire->available()) { + _wire->read(); + } +} + +/*! + * @brief Handles a GPSConfig message from the protobuf stream. + * @param stream + * Pointer to a pb_istream_t object. + * @returns True if the message was handled successfully, False otherwise. + */ +bool GPSHardware::Handle_GPSConfig(wippersnapper_gps_GPSConfig *gps_config) { + WS_DEBUG_PRINTLN("[gps] Handling GPSConfig message..."); + if (gps_config == nullptr) + return false; + // Set the module's polling period for GPS data + SetPollPeriod(gps_config->period); + + // Attempt to decode the GPSConfig message + if (_driver_type == GPS_DRV_MTK) { + WS_DEBUG_PRINTLN("[gps] Handling GPSConfig for MediaTek driver..."); + // Iterate through the command sentences and send them to the GPS module + for (size_t i = 0; i < gps_config->commands_pmtks_count; i++) { + // Build the PMTK ACK response for the command + char msg_resp[MAX_NEMA_SENTENCE_LEN]; + if (!BuildPmtkAck(gps_config->commands_pmtks[i], msg_resp)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to build PMTK ACK response!"); + return false; + } + WS_DEBUG_PRINT("[gps] Expected response: "); + WS_DEBUG_PRINTLN(msg_resp); + if (_iface_type == GPS_IFACE_UART_HW || + _iface_type == GPS_IFACE_UART_SW) { +// Flush the RX/TX buffers before sending +#if HAS_SW_SERIAL + _sw_serial->flush(); + while (_sw_serial->available() > 0) { + _sw_serial->read(); + } +#else + _hw_serial->flush(); + while (_hw_serial->available() > 0) { + _hw_serial->read(); + } +#endif // HAS_SW_SERIAL + } else if (_iface_type == GPS_IFACE_I2C) { + WS_DEBUG_PRINT("[gps] Flushing I2C buffers..."); + I2cReadDiscard(); + WS_DEBUG_PRINTLN(" done!"); + } + WS_DEBUG_PRINT("[gps] TX, CMD: "); + WS_DEBUG_PRINTLN(gps_config->commands_pmtks[i]); + // Send the command to the GPS module + _ada_gps->sendCommand(gps_config->commands_pmtks[i]); + WS_DEBUG_PRINTLN("[gps] Waiting for RX..."); + // and wait for the corresponding response from the GPS module + if (!_ada_gps->waitForSentence(msg_resp, 255)) { + WS_DEBUG_PRINT("[gps] ERROR: Failed to get response | cmd:"); + WS_DEBUG_PRINTLN(gps_config->commands_pmtks[i]); + return false; + } + } + } else if (_driver_type == GPS_DRV_UBLOX) { + I2cReadDiscard(); + } else { + WS_DEBUG_PRINTLN("[gps] ERROR: Unsupported GPS driver type!"); + return false; + } + return true; +} + +/*! + * @brief Sets a UART hardware interface for the GPS controller. + * @param serial + * Pointer to a HardwareSerial instance. + * @returns True if the interface was set successfully, False otherwise. + */ +bool GPSHardware::SetInterface(HardwareSerial *serial, uint32_t baudrate) { + if (serial == nullptr) + return false; + // Configure the hardware serial interface + _hw_serial = serial; + _iface_type = GPS_IFACE_UART_HW; + _baudrate = baudrate; + return true; +} + +#if HAS_SW_SERIAL +/*! + * @brief Sets a UART software serial interface for the GPS controller. + * @param serial + * Pointer to a SoftwareSerial instance. + * @returns True if the interface was set successfully, False otherwise. + */ +bool GPSHardware::SetInterface(SoftwareSerial *serial, uint32_t baudrate) { + if (serial == nullptr) + return false; + // Configure the hardware serial interface + _sw_serial = serial; + _iface_type = GPS_IFACE_UART_SW; + _baudrate = baudrate; + return true; +} +#endif // HAS_SW_SERIAL + +/*! + * @brief Sets a TwoWire (I2C) interface for the GPS controller. + * @param wire + * Points to a TwoWire instance. + * @returns True if the interface was set successfully, False otherwise. + */ +bool GPSHardware::SetInterface(TwoWire *wire) { + if (wire == nullptr) + return false; + // Configure the I2C interface + _wire = wire; + _iface_type = GPS_IFACE_I2C; + return true; +} + +/*! + * @brief Attempts to initialize the GPS device based on the configured + * interface type. + * @returns True if the GPS device was initialized successfully, False + * otherwise. + */ +bool GPSHardware::begin() { + if (_iface_type == GPS_IFACE_NONE) { + WS_DEBUG_PRINTLN("[gps] ERROR: No interface configured for GPS!"); + return false; + } + + // Attempt to set the GPS interface type based on the hardware + if (!QueryModuleType()) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to query GPS module type!"); + return false; + } + WS_DEBUG_PRINTLN("[gps] Module detected and ready!"); + return true; +} + +/*! + * @brief Attempts to detect and initialize a MediaTek GPS module over I2C + * @returns True if a MediaTek GPS module was detected and initialized, False + * otherwise. + */ +bool GPSHardware::DetectMtkI2C(uint32_t addr) { + if (_addr != PA1010D_I2C_ADDRESS) { + WS_DEBUG_PRINTLN( + "[gps] ERROR: Only PA1010D i2c module is supported at this time!"); + return false; + } + _ada_gps = new Adafruit_GPS(_wire); + if (!_ada_gps->begin(_addr)) + return false; + _driver_type = GPS_DRV_MTK; + return true; +} + +/*! + * @brief Attempts to detect and initialize a U-Blox GPS module over I2C + * @param addr + * The I2C address of the GPS module. + * @returns True if a u-blox GPS module was detected and initialized, False + * otherwise. + */ +bool GPSHardware::DetectUbxI2C(uint32_t addr) { + _ubx_gps_ddc = new Adafruit_UBloxDDC(addr, _wire); + if (!_ubx_gps_ddc->begin()) + return false; + _ubx_gps = new Adafruit_UBX(*_ubx_gps_ddc); + if (!_ubx_gps->begin()) + return false; + _ubx_gps->verbose_debug = 3; // TODO: Set this to 1 in production + // Configure Adafruit_GPS instance for parsing NMEA sentences only + _ada_gps = new Adafruit_GPS(); + _driver_type = GPS_DRV_UBLOX; + return true; +} + +/*! + * @brief Queries the GPS driver type by attempting to detect MediaTek or u-blox + * GPS modules. + * @returns True if the driver type was detected successfully, False otherwise. + */ +bool GPSHardware::QueryModuleType() { + WS_DEBUG_PRINTLN("[gps] Detecting module type..."); + if (_iface_type == GPS_IFACE_UART_HW) { + // Try to detect MediaTek GPS module + if (DetectMtkUart()) { + WS_DEBUG_PRINTLN("[gps] Using MediaTek GPS driver!"); + return true; + } + + WS_DEBUG_PRINTLN("[gps] Failed to detect MTK GPS module!"); + // Future TODO: Implement u-blox detection here for UART + // Future TODO: Implement generic NMEA GPS driver detection fallback + WS_DEBUG_PRINTLN("[gps] ERROR: No GPS driver type detected!"); + } else if (_iface_type == GPS_IFACE_I2C) { + if (_addr == PA1010D_I2C_ADDRESS) { + WS_DEBUG_PRINT("[gps] Attempting to use PA1010D driver..."); + if (!DetectMtkI2C(_addr)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to init PA1010D module!"); + return false; + } + WS_DEBUG_PRINTLN("ok!"); + return true; + } else if (_addr == UBX_I2C_ADDRESS) { + WS_DEBUG_PRINT("[gps] Attempting to use u-blox driver..."); + if (!DetectUbxI2C(_addr)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to init u-blox module!"); + return false; + } + WS_DEBUG_PRINTLN("ok!"); + return true; + } else { + WS_DEBUG_PRINTLN("[gps] ERROR: Uknown I2C address provided!"); + return false; + } + } + + return false; +} + +/*! + * @brief Detects if the GPS module is a MediaTek GPS module by querying its + * firmware version. + * @returns True if a MediaTek GPS module was detected, False otherwise. + */ +bool GPSHardware::DetectMtkUart() { + if (_iface_type != GPS_IFACE_UART_HW) { + WS_DEBUG_PRINTLN("[gps] ERROR: MediaTek GPS module only supports Hardware " + "Serial interface!"); + return false; + } + +// Clear the tx and rx buffers before sending the command +#if HAS_SW_SERIAL + _sw_serial->flush(); + while (_sw_serial->available() > 0) { + _sw_serial->read(); + } +#else + _hw_serial->flush(); + while (_hw_serial->available() > 0) { + _hw_serial->read(); + } +#endif + + // Query MediaTek firmware version + uint16_t timeout = MTK_QUERY_FW_TIMEOUT; + +#if HAS_SW_SERIAL + while (_sw_serial->available() < MAX_NEMA_SENTENCE_LEN && timeout--) { + delay(1); + } +#else + while (_hw_serial->available() < MAX_NEMA_SENTENCE_LEN && timeout--) { + delay(1); + } +#endif // HAS_SW_SERIAL + + if (timeout == 0) + return false; + + // We found a response, let's verify that it's the expected PMTK_DK_RELEASE + // command by reading out the NMEA sentence string into a buffer + size_t buf_len = MAX_NEMA_SENTENCE_LEN * 4; // +3 for \r\n and null terminator + char buffer[buf_len]; + size_t available; +#if HAS_SW_SERIAL + available = _sw_serial->available(); +#else + available = _hw_serial->available(); +#endif // HAS_SW_SERIAL + size_t bytes_to_read = min(available, buf_len - 1); + // Print the two out + WS_DEBUG_PRINT("[gps] Reading MediaTek GPS response: "); + WS_DEBUG_PRINT(available); + WS_DEBUG_PRINT(" bytes, reading "); + WS_DEBUG_PRINTLN(bytes_to_read); + for (size_t i = 0; i < bytes_to_read; i++) { +#if HAS_SW_SERIAL + buffer[i] = _sw_serial->read(); +#else + buffer[i] = _hw_serial->read(); +#endif // HAS_SW_SERIAL + } + buffer[bytes_to_read] = '\0'; + WS_DEBUG_PRINT("[gps] MediaTek GPS response: "); + WS_DEBUG_PRINTLN(buffer); + // did we get the expected PMTK705 string? + if (strncmp(buffer, CMD_MTK_QUERY_FW_RESP, 8) != 0) { + return false; + } + +// Attempt to use Adafruit_GPS +#if HAS_SW_SERIAL + _ada_gps = new Adafruit_GPS(_sw_serial); +#else + _ada_gps = new Adafruit_GPS(_hw_serial); +#endif // HAS_SW_SERIAL + if (!_ada_gps->begin(_baudrate)) { + WS_DEBUG_PRINTLN("[gps] ERROR: Failed to initialize Mediatek!"); + return false; + } + _driver_type = GPS_DRV_MTK; + return true; +} + +/*! + * @brief Builds a PMTK acknowledgment message for the provided command. + * @param msg_cmd + * Pointer to a command string. + * @param msg_resp + * Pointer to a response string. + * @returns True if the acknowledgment was built successfully, False otherwise. + */ +bool GPSHardware::BuildPmtkAck(char *msg_cmd, char *msg_resp) { + int cmd_num = 0; + if (sscanf(msg_cmd, "$PMTK%d", &cmd_num) != 1) + return false; + snprintf(msg_resp, MAX_NEMA_SENTENCE_LEN, "$PMTK001,%d,3", cmd_num); + return true; +} + +/*! + * @brief Sets the polling period for GPS data. + * @param poll_period + * The polling period in milliseconds. + */ +void GPSHardware::SetPollPeriod(ulong poll_period) { + if (poll_period < 0) { + _period = 0; + return; + } + _period = (unsigned long)(poll_period * 1000.0f); +} + +/*! + * @brief Sets the previous polling period for GPS data. + * @param poll_period_prv + * The previous polling period in milliseconds. + */ +void GPSHardware::SetPollPeriodPrv(ulong poll_period_prv) { + _period_prv = poll_period_prv; +} + +/*! + * @brief Gets the current polling period for GPS data. + * @returns The polling period in milliseconds. + */ +ulong GPSHardware::GetPollPeriod() { return _period; } + +/*! + * @brief Gets the previous polling period for GPS data. + * @returns The previous polling period in milliseconds. + */ +ulong GPSHardware::GetPollPeriodPrv() { return _period_prv; } + +/*! + * @brief Returns the Adafruit_GPS instance. + * @returns Pointer to the Adafruit_GPS instance. + */ +Adafruit_GPS *GPSHardware::GetAdaGps() { return _ada_gps; } + +/*! + * @brief Sets the NMEA update rate for GPS data. + * @param nmea_update_rate + * The NMEA update rate, in Hz. + */ +void GPSHardware::SetNmeaUpdateRate(int nmea_update_rate) { + _nmea_update_rate = nmea_update_rate; +} + +/*! + * @brief Returns the NMEA port update rate for GPS data. + * @returns The NMEA update rate, in Hz. + */ +int GPSHardware::GetNmeaUpdateRate() { return _nmea_update_rate; } + +/*! + * @brief Sets the NMEA baud rate for GPS data. + * @param nmea_baud_rate + * The NMEA baud rate, in bits per second. + */ +void GPSHardware::SetNmeaBaudRate(int nmea_baud_rate) { + _nmea_baud_rate = nmea_baud_rate; +} + +/*! + * @brief Returns the NMEA port baud rate for GPS data. + * @returns The NMEA baud rate, in bits per second. + */ +int GPSHardware::GetNmeaBaudRate() { return _nmea_baud_rate; } + +/*! + * @brief Sets the I2C address for a GPS. + * @param i2c_address + * The I2C address to set for the GPS device. + */ +void GPSHardware::SetI2CAddress(uint32_t i2c_address) { _addr = i2c_address; } + +/*! + * @brief Sets the last time the GPS hardware was polled. + * @param kat_prv + * The last time the GPS hardware was polled, in milliseconds. + */ +void GPSHardware::SetPrvKat(ulong kat_prv) { + if (kat_prv < 0) { + _kat_prv = 0; + return; + } + _kat_prv = kat_prv; +} + +/*! + * @brief Gets the last time the GPS hardware was polled. + * @returns The last time the GPS hardware was polled, in milliseconds. + */ +ulong GPSHardware::GetPrvKat() { return _kat_prv; } + +/*! + * @brief Returns the driver type of the GPS hardware. + * @returns The driver type of the GPS hardware. + */ +GpsDriverType GPSHardware::GetDriverType() { return _driver_type; } + +/*! + * @brief Returns the interface type of the GPS hardware. + * @returns The interface type of the GPS hardware. + */ +GpsInterfaceType GPSHardware::GetIfaceType() { return _iface_type; } + +/*! + * @brief Discards any data in the UART RX buffer. + */ +void GPSHardware::UartReadDiscard() { + if (_driver_type == GPS_DRV_MTK) { + size_t bytes_avail = _ada_gps->available(); + if (bytes_avail > 0) { + for (size_t i = 0; i < bytes_avail; i++) { + _ada_gps->read(); + } + } + } + // TODO: Support UBX's UART iface here +} + +/*! + * @brief Discards any data in the UART or I2C RX buffer. + */ +void GPSHardware::ReadDiscardBuffer() { + if (_iface_type == GPS_IFACE_UART_HW) { + UartReadDiscard(); + } else if (_iface_type == GPS_IFACE_I2C) { + I2cReadDiscard(); + } else { + WS_DEBUG_PRINTLN("[gps] ERROR: Unsupported GPS interface type!"); + } +} + +/*! + * @brief Polls the GPS hardware for new NMEA sentences and stores them in a + * circular buffer. + */ +void GPSHardware::PollStoreSentences() { + if (_driver_type == GPS_DRV_MTK) { + // Before we poll, Unset the RX flag + if (_ada_gps->newNMEAreceived()) + _ada_gps->lastNMEA(); + + // Read from the GPS module for update_rate milliseconds + ulong update_rate = 1000 / _nmea_update_rate; + ulong start_time = millis(); + while (millis() - start_time < update_rate) { + char c = _ada_gps->read(); + // Check if we have a new NMEA sentence + if (_ada_gps->newNMEAreceived()) { + // If we have a new sentence, push it to the buffer + char *last_nmea = _ada_gps->lastNMEA(); + NmeaBufPush(_ada_gps->lastNMEA()); + } + } + } else if (_driver_type == GPS_DRV_UBLOX) { + // Read from the GPS module for update_rate milliseconds + ulong update_rate = 1000 / _nmea_update_rate; + ulong start_time = millis(); + while (millis() - start_time < update_rate) { + _ubx_gps_ddc->available(); + } + uint8_t buffer[MAX_LEN_NMEA_SENTENCE]; + String nmeaBuffer = ""; + int bytesAvailable = _ubx_gps_ddc->available(); + size_t bytesRead; + size_t bytesToRead = min(bytesAvailable, 82); + if (bytesAvailable > 0) { + bytesRead = _ubx_gps_ddc->readBytes(buffer, bytesToRead); + } + // Build NMEA sentences and parse when complete + for (size_t i = 0; i < bytesRead; i++) { + char c = buffer[i]; + nmeaBuffer += c; + // Check for end of NMEA sentence + if (c == '\n') { + // Push the NMEA sentence to the buffer + if (NmeaBufPush(nmeaBuffer.c_str()) != 0) { + WS_DEBUG_PRINTLN( + "[gps] ERROR: Unable to push NMEA sentence to buffer!"); + } else { + nmeaBuffer = ""; // Reset buffer for next sentence + } + } + } + } else { + WS_DEBUG_PRINTLN("[gps] ERROR: Unsupported GPS driver type for polling!"); + } +} + +/*! + * @brief Pushes a new NMEA sentence into the circular buffer. + * @param new_sentence Pointer to the new NMEA sentence to be added. + * @return 0 on success, -1 if the buffer is full. + */ +int GPSHardware::NmeaBufPush(const char *new_sentence) { + if (!new_sentence) + return -1; + + int next = _nmea_buff.head + 1; // points to head after the current write + if (next >= _nmea_buff.maxlen) + next = 0; // wrap around + + // If buffer is full, overwrite oldest data + if (next == _nmea_buff.tail) { + _nmea_buff.tail = (_nmea_buff.tail + 1) % _nmea_buff.maxlen; + } + + // Copy the new sentence into the buffer + strncpy(_nmea_buff.sentences[_nmea_buff.head], new_sentence, + MAX_LEN_NMEA_SENTENCE - 1); + _nmea_buff.sentences[_nmea_buff.head][MAX_LEN_NMEA_SENTENCE - 1] = '\0'; + _nmea_buff.head = next; + return 0; +} + +/*! + * @brief Pops a NMEA sentence from the circular buffer, FIFO order. + * @param sentence Pointer to the buffer where the popped sentence will be + * stored. + * @return 0 on success, -1 if the buffer is empty. + */ +int GPSHardware::NmeaBufPop(char *sentence) { + // Is the buffer empty? + if (_nmea_buff.head == _nmea_buff.tail) + return -1; + + int next = + _nmea_buff.tail + 1; // next is where tail will point to after this read. + if (next >= _nmea_buff.maxlen) + next = 0; + + // Copy sentence from tail + strcpy(sentence, _nmea_buff.sentences[_nmea_buff.tail]); + _nmea_buff.tail = next; // Advance tail + return 0; +} + +/*! + * @brief Parses a NMEA sentence and returns true if it was successfully parsed. + * @param sentence Pointer to the NMEA sentence to be parsed. + * @returns True if the sentence was parsed successfully, False otherwise. + */ +bool GPSHardware::ParseNMEASentence(char *sentence) { + if (!sentence) + return false; + return _ada_gps->parse(sentence); +} + +/*! + * @brief Gets the hours from the GPS module. + * @returns The hours (0-23), or 0 if the GPS driver type is not set. + */ +uint8_t GPSHardware::GetHour() { return _ada_gps->hour; } + +/*! + * @brief Gets the minute from the GPS module. + * @returns The minute (0-59), or 0 if the GPS driver type is not set. + */ +uint8_t GPSHardware::GetMinute() { return _ada_gps->minute; } + +/*! + * @brief Gets the seconds from the GPS module. + * @returns The seconds (0-59), or 0 if the GPS driver type is not set. + */ +uint8_t GPSHardware::GetSeconds() { return _ada_gps->seconds; } + +/*! + * @brief Gets the milliseconds from the GPS module. + * @returns The milliseconds part of the current time, or 0 if the GPS driver + * type is not set. + */ +uint16_t GPSHardware::GetMilliseconds() { return _ada_gps->milliseconds; } + +/*! + * @brief Gets the day from the GPS module. + * @returns The day of the month (1-31), or 0 if the GPS driver type is not set. + */ +uint8_t GPSHardware::GetDay() { return _ada_gps->day; } + +/*! + * @brief Gets the month from the GPS module. + * @returns The month as a number (1-12), or 0 if the GPS driver type is not + * set. + */ +uint8_t GPSHardware::GetMonth() { return _ada_gps->month; } + +/*! + * @brief Gets the year from the GPS module. + * @returns The year as a 2-digit number (e.g., 23 for 2023), or 0 if the GPS + * driver type is not set. + */ +uint8_t GPSHardware::GetYear() { return _ada_gps->year; } + +/*! + * @brief Gets the GPS fix status. + * @returns True if the GPS has a fix, False otherwise. + */ +bool GPSHardware::GetFix() { return _ada_gps->fix; } + +/*! + * @brief Gets the GPS latitude. + * @returns The latitude in degrees, or 0.0 if the GPS driver type is not set. + */ +float GPSHardware::GetLat() { return _ada_gps->latitude; } + +/*! + * @brief Gets the GPS latitude direction. + * @returns The latitude direction as a character ('N' or 'S'), or '\0' if the + * GPS driver type is not set. + */ +char GPSHardware::GetLatDir() { return _ada_gps->lat; } + +/*! + * @brief Gets the GPS longitude. + * @returns The longitude in degrees, or 0.0 if the GPS driver type is not set. + */ +float GPSHardware::GetLon() { return _ada_gps->longitude; } + +/*! + * @brief Gets the GPS longitude direction. + * @returns The longitude direction as a character ('E' or 'W'), or '\0' if the + * GPS driver type is not set. + */ +char GPSHardware::GetLonDir() { return _ada_gps->lon; } + +/*! + * @brief Gets the number of satellites in view. + * @returns The number of satellites in view, or 0 if the GPS driver type is + * not set. + */ +uint8_t GPSHardware::GetNumSats() { return _ada_gps->satellites; } + +/*! + * @brief Gets the horizontal dilution of precision (HDOP). + * @returns The HDOP value, or 0.0 if the GPS driver type is not set. + */ +float GPSHardware::GetHDOP() { return _ada_gps->HDOP; } + +/*! + * @brief Gets the altitude from the GPS module. + * @returns The altitude in meters, or 0.0 if the GPS driver type is not set. + */ +float GPSHardware::GetAltitude() { return _ada_gps->altitude; } + +/*! + * @brief Gets the speed from the GPS module. + * @returns The speed in meters per second, or 0.0 if the GPS driver type is + * not set. + */ +float GPSHardware::GetSpeed() { return _ada_gps->speed; } + +/*! + * @brief Gets the angle from the GPS module. + * @returns The angle in degrees, or 0.0 if the GPS driver type is not set. + */ +float GPSHardware::GetAngle() { return _ada_gps->angle; } + +/*! + * @brief Gets the geoid height from the GPS module. + * @returns The geoid height in meters, or 0.0 if the GPS driver type is not + * set. + */ +float GPSHardware::GetGeoidHeight() { return _ada_gps->geoidheight; } \ No newline at end of file diff --git a/src/components/gps/hardware.h b/src/components/gps/hardware.h new file mode 100644 index 000000000..b58530267 --- /dev/null +++ b/src/components/gps/hardware.h @@ -0,0 +1,153 @@ +/*! + * @file src/components/gps/hardware.h + * + * Low-level hardware interface for WipperSnapper's generic GPS component. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_GPS_HARDWARE_H +#define WS_GPS_HARDWARE_H +#include "Wippersnapper_V2.h" +#include +#include +#include + +#define CMD_MTK_QUERY_FW \ + "$PMTK605*31" ///< Request to query MediaTek firmware version +#define CMD_MTK_QUERY_FW_RESP \ + "$PMTK705" ///< Response from querying MediaTek firmware version without the + ///< ReleaseStr +#define MTK_QUERY_FW_TIMEOUT 2000 ///< Timeout for MediaTek firmware query in ms +#define CMD_MTK_CHECK_ANTENNA "$PGCMD,33,1*6C" ///< Command to check antenna +#define DEFAULT_MTK_NMEA_UPDATE_RATE 1 ///< Default NMEA update rate in Hz +#define DEFAULT_MTK_NMEA_BAUD_RATE 9600 ///< Default NMEA baud rate in bits per +#define MAX_NEMA_SENTENCE_LEN 82 ///< Maximum length of a NMEA sentence +#define PA1010D_I2C_ADDRESS 0x10 ///< I2C address for PA1010D GPS module +#define UBX_I2C_ADDRESS 0x42 ///< I2C address for all u-Blox GPS products +#define MAX_NMEA_SENTENCES 10 ///< Size of the NMEA buffer +#define MAX_LEN_NMEA_SENTENCE 82 ///< Maximum length of a NMEA sentence + +class Wippersnapper_V2; ///< Forward declaration +class UARTHardware; ///< Forward declaration + +enum GpsInterfaceType { + GPS_IFACE_NONE, ///< No interface/undefined + GPS_IFACE_UART_HW, ///< UART hardware interface + GPS_IFACE_UART_SW, ///< UART software interface + GPS_IFACE_I2C ///< I2C interface +}; ///< Type of interface used by GPS + +enum GpsDriverType { + GPS_DRV_NONE, ///< No driver/undefined + GPS_DRV_MTK, ///< MediaTek GPS driver + GPS_DRV_UBLOX, ///< u-blox GPS driver + GPS_DRV_GENERIC_NMEA ///< Generic NMEA GPS driver +}; ///< Type of GPS driver used + +typedef struct { + char sentences[MAX_NMEA_SENTENCES][MAX_LEN_NMEA_SENTENCE]; + int head; + int tail; + int maxlen; +} nmea_buffer_t; ///< NMEA sentence ring buffer structure + +/*! + @brief Low-level hardware interface for WipperSnapper's generic GPS + component. This class handles the communication with the GPS module + over a specified interface (UART, I2C, etc.) and manages the GPS + driver type. +*/ +class GPSHardware { +public: + GPSHardware(); + ~GPSHardware(); + bool begin(); + bool SetInterface(HardwareSerial *serial, uint32_t baudrate); +#if HAS_SW_SERIAL + bool SetInterface(SoftwareSerial *serial, uint32_t baudrate); +#endif // HAS_SW_SERIAL + bool SetInterface(TwoWire *wire); + void SetPollPeriod(ulong poll_period); + void SetPollPeriodPrv(ulong poll_period_prv); + ulong GetPollPeriod(); + ulong GetPollPeriodPrv(); + ulong GetPrvKat(); + void SetPrvKat(ulong kat_prv); + void SetNmeaUpdateRate(int nmea_update_rate); + int GetNmeaUpdateRate(); + void SetNmeaBaudRate(int nmea_baud_rate); + int GetNmeaBaudRate(); + void SetI2CAddress(uint32_t i2c_address); + bool Handle_GPSConfig(wippersnapper_gps_GPSConfig *gps_config); + Adafruit_GPS *GetAdaGps(); + GpsDriverType GetDriverType(); + GpsInterfaceType GetIfaceType(); + int NmeaBufPop(char *sentence); + // "Helpers" for GPS Drivers + // Used to abstract common parsing functions from GPS driver libraries (i.e: + // Adafruit_GPS, SFE_UBLOX_GNSS, anything in the future) and intelligently + // handle the differences between them + void ReadDiscardBuffer(); + void PollStoreSentences(); + bool ParseNMEASentence(char *sentence); + void HandleNmeaChar(char incoming); + // Datetime getters + uint8_t GetHour(); + uint8_t GetMinute(); + uint8_t GetSeconds(); + uint16_t GetMilliseconds(); + uint8_t GetDay(); + uint8_t GetMonth(); + uint8_t GetYear(); + // RMC/GGA getters + bool GetFix(); + float GetLat(); + char GetLatDir(); + float GetLon(); + char GetLonDir(); + uint8_t GetNumSats(); + float GetHDOP(); + float GetAltitude(); + float GetSpeed(); + float GetAngle(); + float GetGeoidHeight(); + +private: + bool QueryModuleType(); + bool DetectMtkUart(); + bool DetectMtkI2C(uint32_t addr); + bool DetectUbxI2C(uint32_t addr); + bool BuildPmtkAck(char *msg_cmd, char *msg_resp); + void I2cReadDiscard(); + void UartReadDiscard(); + GpsInterfaceType _iface_type; ///< Type of interface used by GPS + GpsDriverType _driver_type; ///< Type of GPS driver used by GPS + HardwareSerial *_hw_serial = nullptr; ///< Optional HardwareSerial instance +#if HAS_SW_SERIAL + SoftwareSerial *_sw_serial = nullptr; ///< Optional SoftwareSerial instance +#endif // HAS_SW_SERIAL + TwoWire *_wire = nullptr; ///< Optional TwoWire instance + Adafruit_GPS *_ada_gps = nullptr; ///< Optional Adafruit GPS instance + Adafruit_UBloxDDC *_ubx_gps_ddc = + nullptr; ///< Optional Adafruit UBlox DDC instance + Adafruit_UBX *_ubx_gps = nullptr; ///< Optional Adafruit UBX instance + uint32_t _addr; ///< Optional i2c address + ulong _period; ///< Polling period for GPS data (Specified by IO), in ms + ulong _period_prv; ///< Previous period for GPS data (Specified by IO), in ms + ulong _kat_prv; ///< Last time the GPS hardware was polled, in ms + uint32_t _baudrate; ///< Baud rate of UART bus + int _nmea_update_rate; ///< NMEA update rate for GPS data, in Hz + int _nmea_baud_rate; ///< NMEA baud rate for GPS data, in bits per second + int NmeaBufPush( + const char *new_sentence); ///< Push a sentence to the NMEA ring buffer + nmea_buffer_t _nmea_buff; ///< NMEA ring buffer for storing sentences +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_GPS_HARDWARE_H \ No newline at end of file diff --git a/src/components/gps/model.cpp b/src/components/gps/model.cpp new file mode 100644 index 000000000..c37846efe --- /dev/null +++ b/src/components/gps/model.cpp @@ -0,0 +1,238 @@ +/*! + * @file src/components/gps/model.cpp + * + * Model implementation for the GPS.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" + +/*! + * @brief Constructor for a GPSModel object. + */ +GPSModel::GPSModel() { + memset(&_msg_gps_config, 0, sizeof(_msg_gps_config)); + memset(&_msg_gps_event, 0, sizeof(_msg_gps_event)); +} + +/*! + * @brief Destructor for a GPSModel object. + */ +GPSModel::~GPSModel() { + memset(&_msg_gps_config, 0, sizeof(_msg_gps_config)); + memset(&_msg_gps_event, 0, sizeof(_msg_gps_event)); +} + +/*! + * @brief Decodes a GPSConfig message from an input stream. + * @param stream A pointer to the pb_istream_t stream. + * @returns True if the GPSConfig message was decoded successfully, False + * otherwise. + */ +bool GPSModel::DecodeGPSConfig(pb_istream_t *stream) { + return pb_decode(stream, wippersnapper_gps_GPSConfig_fields, + &_msg_gps_config); +} + +/*! + * @brief Returns a pointer to the GPSConfig message. + * @returns Pointer to the GPSConfig message. + */ +wippersnapper_gps_GPSConfig *GPSModel::GetGPSConfigMsg() { + return &_msg_gps_config; +} + +/*! + * @brief Creates a new GPSEvent message and initializes it. + * @NOTE: This function will clear an existing GPSEvent message. Only call when + * you are creating a NEW gps event, not modifying an existing one. + */ +void GPSModel::CreateGPSEvent() { + // Zero-out whatever was previously in the GPSEvent message + memset(&_msg_gps_event, 0, sizeof(_msg_gps_event)); + // Create new GPSEvent message with initializer + _msg_gps_event = wippersnapper_gps_GPSEvent_init_zero; + _msg_gps_event.gga_responses_count = 0; + _msg_gps_event.rmc_responses_count = 0; +} + +/*! + * @brief Encodes the GPSEvent message into a buffer. + * @returns True if the encoding was successful, false otherwise. + */ +bool GPSModel::EncodeGPSEvent() { + // Calculate the size of the encoded message + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_gps_GPSEvent_fields, + &_msg_gps_event)) + return false; + + // Attempt to encode the message into a buffer + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_gps_GPSEvent_fields, + &_msg_gps_event); +} + +/*! + * @brief Returns a pointer to the GPSEvent message. + * @returns Pointer to the GPSEvent message. + */ +wippersnapper_gps_GPSEvent *GPSModel::GetGPSEvent() { return &_msg_gps_event; }; + +/*! + * @brief Creates a GPSDateTime message with the provided parameters. + * @param hour GMT hour of the day (0-23). + * @param minute GMT minute of the hour (0-59). + * @param seconds GMT seconds of the minute (0-59). + * @param milliseconds GMT milliseconds (0-999). + * @param day GMT day of the month (1-31). + * @param month GMT month of the year (1-12). + * @param year GMT year (e.g., 25). + * @returns A wippersnapper_gps_GPSDateTime message. + */ +wippersnapper_gps_GPSDateTime +GPSModel::CreateGpsDatetime(uint8_t hour, uint8_t minute, uint8_t seconds, + uint8_t milliseconds, uint8_t day, uint8_t month, + uint8_t year) { + wippersnapper_gps_GPSDateTime datetime; + // Fill in the datetime structure with the provided values + datetime.hour = (uint32_t)(hour); + datetime.minute = (uint32_t)(minute); + datetime.seconds = (uint32_t)(seconds); + datetime.milliseconds = (uint32_t)(milliseconds); + datetime.day = (uint32_t)(day); + datetime.month = (uint32_t)(month); + datetime.year = (uint32_t)(year); + return datetime; +} + +bool GPSModel::AddGpsEventRMC(wippersnapper_gps_GPSDateTime datetime, + uint8_t fix_status, float lat, char *lat_dir, + float lon, char *lon_dir, float speed, + float angle) { + // Validate pointers have been provided correctly + if (!lat_dir || !lon_dir) + return false; + + // Always store at index 0, overwriting any previous response + wippersnapper_gps_GPSRMCResponse rmc_response; + rmc_response = wippersnapper_gps_GPSRMCResponse_init_zero; + rmc_response.has_datetime = true; + rmc_response.datetime = datetime; + + // Determine the fix status + if (fix_status == 1 || fix_status == 2) { + rmc_response.fix_status[0] = 'A'; // Active fix + } else { + rmc_response.fix_status[0] = 'V'; // Void fix + } + rmc_response.fix_status[1] = '\0'; + + // Fill lat/lon and direction + snprintf(rmc_response.lat, sizeof(rmc_response.lat), "%.6f", lat); + snprintf(rmc_response.lon, sizeof(rmc_response.lon), "%.6f", lon); + strncpy(rmc_response.lat_dir, lat_dir, sizeof(rmc_response.lat_dir) - 1); + rmc_response.lat_dir[sizeof(rmc_response.lat_dir) - 1] = '\0'; + strncpy(rmc_response.lon_dir, lon_dir, sizeof(rmc_response.lon_dir) - 1); + rmc_response.lon_dir[sizeof(rmc_response.lon_dir) - 1] = '\0'; + + // Fill current speed over ground, in knots + snprintf(rmc_response.speed, sizeof(rmc_response.speed), "%.1f", speed); + // Fill course in degrees from true north + snprintf(rmc_response.angle, sizeof(rmc_response.angle), "%.1f", angle); + + // Always store at index 0, overwrite any previous response in the FIFO buffer + _msg_gps_event.rmc_responses[0] = rmc_response; + _msg_gps_event.rmc_responses_count = 1; + return true; +} + +bool GPSModel::AddGpsEventGGA(wippersnapper_gps_GPSDateTime datetime, + uint8_t fix_status, float lat, char *lat_dir, + float lon, char *lon_dir, uint8_t num_sats, + float hdop, float alt, float geoid_height) { + // Validate pointers have been provided correctly + if (!lat_dir || !lon_dir) + return false; + + wippersnapper_gps_GPGGAResponse gga_response; + gga_response = wippersnapper_gps_GPGGAResponse_init_zero; + gga_response.has_datetime = true; + gga_response.datetime = datetime; + + // Fill lat/lon and direction + snprintf(gga_response.lat, sizeof(gga_response.lat), "%.6f", lat); + snprintf(gga_response.lon, sizeof(gga_response.lon), "%.6f", lon); + strncpy(gga_response.lat_dir, lat_dir, sizeof(gga_response.lat_dir) - 1); + gga_response.lat_dir[sizeof(gga_response.lat_dir) - 1] = '\0'; + strncpy(gga_response.lon_dir, lon_dir, sizeof(gga_response.lon_dir) - 1); + gga_response.lon_dir[sizeof(gga_response.lon_dir) - 1] = '\0'; + + // Determine the fix quality + gga_response.fix_quality = (uint32_t)fix_status; + // Fill number of satellites in use + gga_response.num_satellites = (uint32_t)num_sats; + + // Fill horizontal dilution of precision + snprintf(gga_response.hdop, sizeof(gga_response.hdop), "%.1f", hdop); + // Fill altitude in meters above MSL + snprintf(gga_response.altitude, sizeof(gga_response.altitude), "%.1f", alt); + // Fill geoid height in meters + snprintf(gga_response.geoid_height, sizeof(gga_response.geoid_height), "%.1f", + geoid_height); + + // Always store at index 0, overwrite any previous response in the FIFO buffer + _msg_gps_event.gga_responses[0] = gga_response; + _msg_gps_event.gga_responses_count = 1; + return true; +} + +/*! + * @brief Returns the previous GPS datetime as a DateTime object. + * @returns A DateTime object representing the previous GPS datetime. + */ +DateTime GPSModel::GetPrvGPSDateTime() { + // Create a DateTime object from the previous GPS datetime + return DateTime(_prv_msg_gps_datetime.year, _prv_msg_gps_datetime.month, + _prv_msg_gps_datetime.day, _prv_msg_gps_datetime.hour, + _prv_msg_gps_datetime.minute, _prv_msg_gps_datetime.seconds); +} + +bool GPSModel::ProcessNMEASentence(char *sentence, GPSHardware *drv) { + // Check for prefix: $GP or $GN + if (strncmp(sentence, "$GP", 3) != 0 && strncmp(sentence, "$GN", 3) != 0) + return false; + + _prv_msg_gps_datetime = CreateGpsDatetime( + drv->GetHour(), drv->GetMinute(), drv->GetSeconds(), + drv->GetMilliseconds(), drv->GetDay(), drv->GetMonth(), drv->GetYear()); + char lat_dir = drv->GetLatDir(); + char lon_dir = drv->GetLonDir(); + if (sentence[3] == 'R' && sentence[4] == 'M' && sentence[5] == 'C') { + // Process RMC sentence + if (!AddGpsEventRMC(_prv_msg_gps_datetime, drv->GetFix(), drv->GetLat(), + &lat_dir, drv->GetLon(), &lon_dir, drv->GetSpeed(), + drv->GetAngle())) + return false; + } else if (sentence[3] == 'G' && sentence[4] == 'G' && sentence[5] == 'A') { + // Process GGA sentence + if (!AddGpsEventGGA(_prv_msg_gps_datetime, drv->GetFix(), drv->GetLat(), + &lat_dir, drv->GetLon(), &lon_dir, drv->GetNumSats(), + drv->GetHDOP(), drv->GetAltitude(), + drv->GetGeoidHeight())) + return false; + } else { + // Not a recognized RMC or GGA sentence + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/components/gps/model.h b/src/components/gps/model.h new file mode 100644 index 000000000..55ecd0604 --- /dev/null +++ b/src/components/gps/model.h @@ -0,0 +1,59 @@ +/*! + * @file src/components/gps/model.h + * + * Model interface for the GPS.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_GPS_MODEL_H +#define WS_GPS_MODEL_H +#include "Wippersnapper_V2.h" +#include +#define MAX_COUNT_RMC_GGA 10 ///< Maximum number of RMC or GGA responses + +class GPSHardware; ///< Forward declaration + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from GPS.proto. +*/ +class GPSModel { +public: + GPSModel(); + ~GPSModel(); + bool DecodeGPSConfig(pb_istream_t *stream); + wippersnapper_gps_GPSConfig *GetGPSConfigMsg(); + // GPSEvent API + void CreateGPSEvent(); + bool EncodeGPSEvent(); + wippersnapper_gps_GPSEvent *GetGPSEvent(); + bool ProcessNMEASentence(char *sentence, GPSHardware *drv); + wippersnapper_gps_GPSDateTime CreateGpsDatetime(uint8_t hour, uint8_t minute, + uint8_t seconds, + uint8_t milliseconds, + uint8_t day, uint8_t month, + uint8_t year); + bool AddGpsEventRMC(wippersnapper_gps_GPSDateTime datetime, + uint8_t fix_status, float lat, char *lat_dir, float lon, + char *lon_dir, float speed, float angle); + + bool AddGpsEventGGA(wippersnapper_gps_GPSDateTime datetime, + uint8_t fix_status, float lat, char *lat_dir, float lon, + char *lon_dir, uint8_t num_sats, float hdop, float alt, + float geoid_height); + DateTime GetPrvGPSDateTime(); + +private: + wippersnapper_gps_GPSConfig _msg_gps_config; ///< GPS configuration message + wippersnapper_gps_GPSEvent _msg_gps_event; ///< GPS event message + wippersnapper_gps_GPSDateTime + _prv_msg_gps_datetime; ///< Previous GPS datetime +}; +#endif // WS_GPS_MODEL_H \ No newline at end of file diff --git a/src/components/i2c/controller.cpp b/src/components/i2c/controller.cpp index 26c1fb00e..538a8c8c6 100644 --- a/src/components/i2c/controller.cpp +++ b/src/components/i2c/controller.cpp @@ -1,5 +1,5 @@ /*! - * @file controller.cpp + * @file src/components/i2c/controller.cpp * * Controller for the i2c.proto API * @@ -13,8 +13,9 @@ * */ #include "controller.h" +#include "drivers/drvBase.h" +#include "drivers/drvOutputBase.h" -/*******************************************************************************/ /*! @brief Lambda function to create a drvBase driver instance @param i2c @@ -26,13 +27,12 @@ @param driver_name The i2c driver's name. */ -/*******************************************************************************/ -using FnCreateI2CDriver = +using FnCreateI2CSensorDriver = std::function; -// Factory for creating a new I2C drivers -// NOTE: When you add a new driver, make sure to add it to the factory! -static const std::map I2cFactory = { +// Factory for creating a new I2C SENSOR drivers +// NOTE: When you add a new SENSOR driver, make sure to add it to the factory! +static const std::map I2cFactorySensor = { {"bme280", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -123,26 +123,11 @@ static const std::map I2cFactory = { const char *driver_name) -> drvBase * { return new drvHtu21d(i2c, addr, mux_channel, driver_name); }}, - {"htu31d", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvHtu31d(i2c, addr, mux_channel, driver_name); - }}, - {"hdc302x", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvHdc302x(i2c, addr, mux_channel, driver_name); - }}, {"ina219", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { return new drvIna219(i2c, addr, mux_channel, driver_name); }}, - {"ina260", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvIna260(i2c, addr, mux_channel, driver_name); - }}, {"lc709203f", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -163,11 +148,6 @@ static const std::map I2cFactory = { const char *driver_name) -> drvBase * { return new drvLps25hb(i2c, addr, mux_channel, driver_name); }}, - {"lps28dfw", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvLps28dfw(i2c, addr, mux_channel, driver_name); - }}, {"ltr329", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -243,11 +223,6 @@ static const std::map I2cFactory = { const char *driver_name) -> drvBase * { return new drvScd30(i2c, addr, mux_channel, driver_name); }}, - {"sgp30", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSgp30(i2c, addr, mux_channel, driver_name); - }}, {"sgp40", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -303,36 +278,6 @@ static const std::map I2cFactory = { const char *driver_name) -> drvBase * { return new drvSen5x(i2c, addr, mux_channel, driver_name); }}, - {"sen60", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, - {"sen63c", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, - {"sen65", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, - {"sen66", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, - {"sen68", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, - {"sen6x", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvSen6x(i2c, addr, mux_channel, driver_name); - }}, {"shtc3", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -373,11 +318,6 @@ static const std::map I2cFactory = { const char *driver_name) -> drvBase * { return new drvVncl4040(i2c, addr, mux_channel, driver_name); }}, - {"vncl4200", - [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, - const char *driver_name) -> drvBase * { - return new drvVncl4200(i2c, addr, mux_channel, driver_name); - }}, {"vl53l0x", [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, const char *driver_name) -> drvBase * { @@ -469,27 +409,72 @@ static const std::unordered_map> {"bme280", "bme680", "bmp280", "bmp388", "bmp390", "dps310", "pct2075"}}}; ///< I2C address to driver map -/***********************************************************************/ /*! - @brief Obtains possible candidate drivers for a given I2C address. - @param addr - The desired I2C address. - @returns A vector of pointers to candidate drivers. + @brief Lambda function to create a drvOutputBase instance + @param i2c + The desired I2C interface. + @param addr + The desired i2c device address. + @param mux_channel + The desired I2C multiplexer channel. + @param driver_name + The i2c output driver's name. */ -/***********************************************************************/ -std::vector GetDriversForAddress(uint16_t addr) { - std::vector candidates; - std::unordered_map>::const_iterator - candidate = map_address_to_drivers.find(addr); +using FnCreateI2cOutputDrv = + std::function; - if (candidate != map_address_to_drivers.end()) { - candidates = candidate->second; +// Factory for creating a new i2c OUTPUT driver +// NOTE: When adding a new OUTPUT driver, make sure to add it to the map below! +static const std::map I2cFactoryOutput = { + {"quadalphanum", + [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, + const char *driver_name) -> drvOutputBase * { + return new drvOutQuadAlphaNum(i2c, addr, mux_channel, driver_name); + }}, + {"7seg", + [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, + const char *driver_name) -> drvOutputBase * { + return new drvOut7Seg(i2c, addr, mux_channel, driver_name); + }}, + {"charlcd", + [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, + const char *driver_name) -> drvOutputBase * { + return new drvOutCharLcd(i2c, addr, mux_channel, driver_name); + }}, + {"ssd1306", + [](TwoWire *i2c, uint16_t addr, uint32_t mux_channel, + const char *driver_name) -> drvOutputBase * { + return new drvOutSsd1306(i2c, addr, mux_channel, driver_name); + }}}; ///< I2C output driver factory + +/*! + @brief Creates an I2C driver by name + @param driver_name + The name of the I2C driver. + @param i2c + The I2C bus. + @param addr + The I2C device address. + @param i2c_mux_channel + The I2C MUX channel. + @param status + The I2cDeviceStatus message. + @returns A pointer to the I2C driver. +*/ +drvBase *CreateI2cSensorDrv(const char *driver_name, TwoWire *i2c, + uint16_t addr, uint32_t i2c_mux_channel, + wippersnapper_i2c_I2cDeviceStatus &status) { + auto it = I2cFactorySensor.find(driver_name); + if (it == I2cFactorySensor.end()) { + status = + wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_FAIL_UNSUPPORTED_SENSOR; + return nullptr; } - return candidates; + status = wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_SUCCESS; + return it->second(i2c, addr, i2c_mux_channel, driver_name); } -/***********************************************************************/ /*! @brief Creates an I2C driver by name @param driver_name @@ -504,12 +489,11 @@ std::vector GetDriversForAddress(uint16_t addr) { The I2cDeviceStatus message. @returns A pointer to the I2C driver. */ -/***********************************************************************/ -drvBase *CreateI2CDriverByName(const char *driver_name, TwoWire *i2c, - uint16_t addr, uint32_t i2c_mux_channel, - wippersnapper_i2c_I2cDeviceStatus &status) { - auto it = I2cFactory.find(driver_name); - if (it == I2cFactory.end()) { +drvOutputBase *CreateI2cOutputDrv(const char *driver_name, TwoWire *i2c, + uint16_t addr, uint32_t i2c_mux_channel, + wippersnapper_i2c_I2cDeviceStatus &status) { + auto it = I2cFactoryOutput.find(driver_name); + if (it == I2cFactoryOutput.end()) { status = wippersnapper_i2c_I2cDeviceStatus_I2C_DEVICE_STATUS_FAIL_UNSUPPORTED_SENSOR; return nullptr; @@ -521,37 +505,103 @@ drvBase *CreateI2CDriverByName(const char *driver_name, TwoWire *i2c, /***********************************************************************/ /*! - @brief I2cController constructor + @brief Obtains possible candidate drivers for a given I2C address. + @param addr + The desired I2C address. + @returns A vector of pointers to candidate drivers. */ /***********************************************************************/ +std::vector GetDriversForAddress(uint16_t addr) { + std::vector candidates; + std::unordered_map>::const_iterator + candidate = map_address_to_drivers.find(addr); + + if (candidate != map_address_to_drivers.end()) { + candidates = candidate->second; + } + + return candidates; +} + +/*! + @brief I2cController constructor +*/ I2cController::I2cController() { - _i2c_bus_alt = nullptr; _i2c_model = new I2cModel(); - // Initialize the default I2C bus + _i2c_output_model = new I2cOutputModel(); _i2c_bus_default = new I2cHardware(); } -/***********************************************************************/ /*! @brief I2cController destructor */ -/***********************************************************************/ I2cController::~I2cController() { if (_i2c_model) delete _i2c_model; + if (_i2c_output_model) + delete _i2c_output_model; + if (_i2c_bus_default) delete _i2c_bus_default; } -/*************************************************************************/ +/*! + @brief Removes an I2C driver from the controller and frees memory + @param address + The desired I2C device's address. + @param is_output_device + True if the driver is an output device, False otherwise. + @returns True if the driver was removed, False otherwise. +*/ +bool I2cController::RemoveDriver(uint32_t address, bool is_output_device) { + if (!is_output_device) { + // Safely remove the i2c sensor driver from the vector and free memory + for (drvBase *driver : _i2c_drivers) { + if (driver == nullptr) + continue; + + if (driver->GetAddress() != address) + continue; + + auto it = std::find(_i2c_drivers.begin(), _i2c_drivers.end(), driver); + if (it != _i2c_drivers.end()) { + _i2c_drivers.erase(it); + } + delete driver; + return true; + } + } else { + // This was an output driver type, safely remove the i2c output driver from + // the vector and free memory + for (drvOutputBase *driver : _i2c_drivers_output) { + if (driver == nullptr) + continue; + + if (driver->GetAddress() != address) + continue; + + auto it = std::find(_i2c_drivers_output.begin(), + _i2c_drivers_output.end(), driver); + if (it != _i2c_drivers_output.end()) { + _i2c_drivers_output.erase(it); + } + delete driver; + return true; + } + } + + // We didn't find the driver to remove + WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to find driver to remove!"); + return false; +} + /*! @brief Returns if the I2C bus has been created successfully. @param is_alt_bus True if the alt. I2C bus is being queried, False otherwise. @returns True if the I2C bus has already been created, False otherwise. */ -/*************************************************************************/ bool I2cController::IsBusStatusOK(bool is_alt_bus) { bool is_ok = false; if (is_alt_bus) { @@ -564,7 +614,6 @@ bool I2cController::IsBusStatusOK(bool is_alt_bus) { return is_ok; } -/***********************************************************************/ /*! @brief Publishes an I2cDeviceAddedorReplaced message to the broker @param device_descriptor @@ -574,20 +623,20 @@ bool I2cController::IsBusStatusOK(bool is_alt_bus) { @returns True if the I2cDeviceAddedorReplaced message was published successfully, False otherwise. */ -/***********************************************************************/ bool I2cController::PublishI2cDeviceAddedorReplaced( const wippersnapper_i2c_I2cDeviceDescriptor &device_descriptor, const wippersnapper_i2c_I2cDeviceStatus &device_status) { + // If we're in offline mode, don't publish out to IO + if (WsV2._sdCardV2->isModeOffline()) + return true; // Back out if we're in offline mode + + // Encode the I2cDeviceAddedorReplaced message and publish it to IO if (!_i2c_model->encodeMsgI2cDeviceAddedorReplaced( device_descriptor, _i2c_bus_default->GetBusStatus(), device_status)) { WS_DEBUG_PRINTLN( "[i2c] ERROR: Unable to encode I2cDeviceAddedorReplaced message!"); return false; } - - if (WsV2._sdCardV2->isModeOffline()) - return true; // Back out if we're in offline mode - if (!WsV2.PublishSignal( wippersnapper_signal_DeviceToBroker_i2c_device_added_replaced_tag, _i2c_model->GetMsgI2cDeviceAddedOrReplaced())) { @@ -598,7 +647,6 @@ bool I2cController::PublishI2cDeviceAddedorReplaced( return true; } -/***********************************************************************/ /*! @brief Implements handling for a I2cDeviceRemove message @param stream @@ -606,7 +654,6 @@ bool I2cController::PublishI2cDeviceAddedorReplaced( @returns True if the I2cDeviceRemove message was handled, False otherwise. */ -/***********************************************************************/ bool I2cController::Handle_I2cDeviceRemove(pb_istream_t *stream) { // Attempt to decode an I2cDeviceRemove message WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceRemove message..."); @@ -616,12 +663,69 @@ bool I2cController::Handle_I2cDeviceRemove(pb_istream_t *stream) { } // TODO [Online]: Implement the rest of this function - WS_DEBUG_PRINTLN("[i2c] I2cDeviceRemove message not yet implemented!"); + // TODO: Remember - can be on either bus! (default or alt) + // TODO: Remember to handle removal of a mux device or a device on a mux + // strlen(descriptor.i2c_bus_sda) == 0 + + wippersnapper_i2c_I2cDeviceRemove *msgRemove = + _i2c_model->GetI2cDeviceRemoveMsg(); + if (!msgRemove->has_i2c_device_description) { + WS_DEBUG_PRINTLN("[i2c] ERROR: I2cDeviceRemove message missing required " + "device description!"); + return false; + } + + bool did_remove = true; + + // Check for default bus + if (strlen(msgRemove->i2c_device_description.i2c_bus_scl) == 0 && + strlen(msgRemove->i2c_device_description.i2c_bus_sda) == 0) { + WS_DEBUG_PRINTLN("[i2c] Removing device from default bus..."); + if (!_i2c_bus_default->HasMux()) { + if (!RemoveDriver(msgRemove->i2c_device_description.i2c_device_address, + msgRemove->is_output_device)) { + WS_DEBUG_PRINTLN( + "[i2c] ERROR: Failed to remove i2c device from default bus!"); + did_remove = false; + } + } else { + // Bus has a I2C MUX attached + // Case 1: Is the I2C device connected to a MUX? + if (msgRemove->i2c_device_description.i2c_mux_address != 0xFFFF && + msgRemove->i2c_device_description.i2c_mux_channel >= 0) { + _i2c_bus_default->SelectMuxChannel( + msgRemove->i2c_device_description.i2c_mux_channel); + if (!RemoveDriver(msgRemove->i2c_device_description.i2c_device_address, + msgRemove->is_output_device)) { + WS_DEBUG_PRINTLN( + "[i2c] ERROR: Failed to remove i2c device from default bus!"); + did_remove = false; + } + } + // Case 2: Is the I2C device a MUX? + if (msgRemove->i2c_device_description.i2c_device_address == + msgRemove->i2c_device_description.i2c_mux_address) { + wippersnapper_i2c_I2cBusScanned scan_results; + _i2c_bus_default->ScanMux(&scan_results); + for (int i = 0; i < scan_results.i2c_bus_found_devices_count; i++) { + // Select the channel and remove the device + _i2c_bus_default->SelectMuxChannel( + scan_results.i2c_bus_found_devices[i].i2c_mux_channel); + RemoveDriver(scan_results.i2c_bus_found_devices[i].i2c_device_address, + msgRemove->is_output_device); + } + _i2c_bus_default->RemoveMux(); + } + } + } + + // TODO: Check for Alt. I2C Bus + + // Publush with did_remove to the response return true; } -/***********************************************************************/ /*! @brief Attempts to initialize a MUX on the bus. @param name @@ -635,7 +739,6 @@ bool I2cController::Handle_I2cDeviceRemove(pb_istream_t *stream) { @returns True if the MUX was successfully initialized, False otherwise. */ -/***********************************************************************/ bool I2cController::InitMux(const char *name, uint32_t address, bool is_alt_bus) { if (is_alt_bus) { @@ -655,47 +758,183 @@ bool I2cController::InitMux(const char *name, uint32_t address, return true; } -/***********************************************************************/ /*! - @brief Checks if a driver has already been initialized with the - given device descriptor. - @param device_descriptor - The I2cDeviceDescriptor message. - @returns True if a driver has already been initialized, False - otherwise. + @brief Configures the MUX channel on the bus. + @param stream + Pointer to the pb_istream + @returns True if the I2C bus was successfully scanned and the + I2cBusScan message was published to IO, False otherwise. */ -/***********************************************************************/ -bool I2cController::IsDriverInitialized( - wippersnapper_i2c_I2cDeviceDescriptor &device_descriptor) { - // Before we do anything, check if a driver has been already initialized with - // the device_descriptor if so, we log and skip - for (auto &driver : _i2c_drivers) { - // Do they share the same address? - if (driver->GetAddress() == device_descriptor.i2c_device_address) { - // Okay - do they sit on different i2c buses? - bool is_driver_bus_alt = driver->HasAltI2CBus(); - bool is_device_bus_alt = - (strcmp(device_descriptor.i2c_bus_scl, "default") != 0) || - (strcmp(device_descriptor.i2c_bus_sda, "default") != 0); - // Bus descriptors do not match, so we haven't initialized this candidate - if (is_driver_bus_alt != is_device_bus_alt) - continue; +bool I2cController::Handle_I2cBusScan(pb_istream_t *stream) { + WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceAddOrReplace message..."); + // Attempt to decode I2cBusScan message + if (!_i2c_model->DecodeI2cBusScan(stream)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to decode I2cBusScan message!"); + return false; + } - // What about the MUX? - if (driver->HasMux() && - driver->GetMuxAddress() == device_descriptor.i2c_mux_address && - driver->GetMuxChannel() != device_descriptor.i2c_mux_channel) { - continue; + _i2c_model->ClearI2cBusScanned(); + wippersnapper_i2c_I2cBusScanned *scan_results = + _i2c_model->GetI2cBusScannedMsg(); + + bool scan_success = true; + // Case 1: Scan the default I2C bus + if (_i2c_model->GetI2cBusScanMsg()->scan_default_bus) { + // Was the default bus initialized correctly and ready to scan? + WS_DEBUG_PRINT("Bus State: "); + WS_DEBUG_PRINTLN(_i2c_bus_default->GetBusStatus()); + WS_DEBUG_PRINTLN(IsBusStatusOK()); + if (IsBusStatusOK()) { + if (!_i2c_bus_default->ScanBus(scan_results)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan default I2C bus!"); + scan_success = false; } + } else { + WS_DEBUG_PRINTLN("[i2c] ERROR: Default I2C bus state is stuck, please " + "reset the board!"); + scan_success = false; + } + } - WS_DEBUG_PRINTLN("[i2c] Descriptor already initialized..."); - return true; + // Case 2: Optionally scan the alternative I2C bus + if (_i2c_model->GetI2cBusScanMsg()->scan_alt_bus) { + // Is the alt bus initialized? + if (_i2c_bus_alt == nullptr) { + _i2c_bus_alt = new I2cHardware( + _i2c_model->GetI2cBusScanMsg()->i2c_alt_bus_descriptor.i2c_bus_sda, + _i2c_model->GetI2cBusScanMsg()->i2c_alt_bus_descriptor.i2c_bus_sda); + // Was the default bus initialized correctly and ready to scan? + if (IsBusStatusOK(true)) { + if (!_i2c_bus_alt->ScanBus(scan_results)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan alt. I2C bus!"); + scan_success = false; + } + } else { + WS_DEBUG_PRINTLN("[i2c] ERROR: alt. I2C bus state is stuck, please " + "reset the board!"); + scan_success = false; + } } } - return false; + + // Case 3: Optionally scan MUX attached to the default bus + if (_i2c_model->GetI2cBusScanMsg()->scan_default_bus_mux) { + if (_i2c_bus_default->HasMux()) { + if (!_i2c_bus_default->ScanMux(scan_results)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan I2C MUX on default bus!"); + scan_success = false; + } + } + } + + // Case 4: Optionally scan MUX attached to the alt. bus + if (_i2c_model->GetI2cBusScanMsg()->scan_alt_bus) { + if (_i2c_bus_alt->HasMux()) { + if (!_i2c_bus_alt->ScanMux(scan_results)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan I2C MUX on alt. bus!"); + scan_success = false; + } + } + } + + // Printout content of scan_results + WS_DEBUG_PRINT("[i2c] Scan found "); + WS_DEBUG_PRINT(scan_results->i2c_bus_found_devices_count); + WS_DEBUG_PRINTLN(" devices."); + for (int i = 0; i < scan_results->i2c_bus_found_devices_count; i++) { + WS_DEBUG_PRINTLN(i); + WS_DEBUG_PRINT("Address: "); + WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_device_address, + HEX); + WS_DEBUG_PRINT("SCL: "); + WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_bus_scl); + WS_DEBUG_PRINT("SDA: "); + WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_bus_sda); + WS_DEBUG_PRINT("MUX Address: "); + WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_mux_address); + WS_DEBUG_PRINT("MUX Channel: "); + WS_DEBUG_PRINTLN(scan_results->i2c_bus_found_devices[i].i2c_mux_channel); + } + + // TODO: Encode and publish out to IO! + // TODO: Take scan_success into account here + return true; +} + +/*! + @brief Handler for an I2cDeviceOutputWrite message + @param stream + A pointer to the pb_istream_t stream. + @returns True if the callback was successfully executed by the driver, + False otherwise. +*/ +bool I2cController::Handle_I2cDeviceOutputWrite(pb_istream_t *stream) { + WS_DEBUG_PRINTLN("[i2c] Decoding I2cDeviceOutputWrite message..."); + // Attempt to decode an I2cDeviceOutputWrite message + if (!_i2c_model->DecodeI2cDeviceOutputWrite(stream)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to decode I2cDeviceOutputWrite " + "message!"); + return false; + } + wippersnapper_i2c_I2cDeviceDescriptor descriptor = + _i2c_model->GetI2cDeviceOutputWriteMsg()->i2c_device_description; + + // Attempt to find the driver + drvOutputBase *driver = nullptr; + for (auto *drv : _i2c_drivers_output) { + if (drv == nullptr) + continue; + + if (drv->GetAddress() != descriptor.i2c_device_address) + continue; + + driver = drv; + break; + } + + if (driver == nullptr) { + WS_DEBUG_PRINT("[i2c] ERROR: Unable to find driver for device at addr 0x"); + WS_DEBUG_PRINTLN(descriptor.i2c_device_address, HEX); + return false; + } + + // Optionally configure the I2C MUX + uint32_t mux_channel = driver->GetMuxChannel(); + WS_DEBUG_PRINTLN(mux_channel); + if (driver->HasMux()) { + ConfigureMuxChannel(mux_channel, driver->HasAltI2CBus()); + } + + // Determine which driver cb function to use + if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg == + wippersnapper_i2c_I2cDeviceOutputWrite_write_led_backpack_tag) { + WS_DEBUG_PRINTLN("[i2c] Writing to LED backpack..."); + driver->WriteMessage(_i2c_model->GetI2cDeviceOutputWriteMsg() + ->output_msg.write_led_backpack.message); + } else if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg == + wippersnapper_i2c_I2cDeviceOutputWrite_write_char_lcd_tag) { + WS_DEBUG_PRINTLN("[i2c] Writing to char LCD..."); + if (!driver->WriteMessageCharLCD(&_i2c_model->GetI2cDeviceOutputWriteMsg() + ->output_msg.write_char_lcd)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to write to char LCD!"); + return false; + } + } else if (_i2c_model->GetI2cDeviceOutputWriteMsg()->which_output_msg == + wippersnapper_i2c_I2cDeviceOutputWrite_write_oled_tag) { + WS_DEBUG_PRINTLN("[i2c] Writing to SSD1306 OLED..."); + // Note: In the future, we can expand this to support other OLEDs by + // creating and checking a tag within the write oled msg (e.g. SSD1327, + // etc.) + driver->WriteMessageSSD1306(_i2c_model->GetI2cDeviceOutputWriteMsg() + ->output_msg.write_oled.message); + } else { + WS_DEBUG_PRINTLN("[i2c] ERROR: Unable to determine I2C Output Write type!"); + return false; + } + + return true; } -/***********************************************************************/ /*! @brief Implements handling for a I2cDeviceAddOrReplace message @param stream @@ -703,7 +942,6 @@ bool I2cController::IsDriverInitialized( @returns True if the I2cDeviceAddOrReplace message was handled (created or replaced), False otherwise. */ -/***********************************************************************/ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { bool use_alt_bus = false; bool did_set_mux_ch = false; @@ -724,11 +962,17 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { wippersnapper_i2c_I2cDeviceDescriptor device_descriptor = _i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_description; - // Did the driver initialize correctly? - if (IsDriverInitialized(device_descriptor)) { - WS_DEBUG_PRINTLN("[i2c] Driver already initialized, skipping..."); - return true; - } + // Is this an i2c output device? + bool is_output = _i2c_model->GetI2cDeviceAddOrReplaceMsg()->is_output; + + // Is this a i2c GPS? + bool is_gps = _i2c_model->GetI2cDeviceAddOrReplaceMsg()->is_gps; + WS_DEBUG_PRINT("[i2c] Device name: "); + WS_DEBUG_PRINTLN(device_name); + WS_DEBUG_PRINT("[i2c] Device address: 0x"); + WS_DEBUG_PRINTLN(device_descriptor.i2c_device_address, HEX); + WS_DEBUG_PRINT("[i2c] Is GPS? "); + WS_DEBUG_PRINTLN(is_gps ? "Yes" : "No"); // TODO [Online]: Handle Replace messages by implementing the Remove handler // first...then proceed to adding a new device @@ -738,9 +982,8 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { WS_DEBUG_PRINTLN("[i2c] Non-default I2C bus specified!"); if (_i2c_bus_alt == nullptr) { WS_DEBUG_PRINTLN("[i2c] Initializing alternative i2c bus..."); - _i2c_bus_alt = new I2cHardware(); - _i2c_bus_alt->InitBus(false, device_descriptor.i2c_bus_sda, - device_descriptor.i2c_bus_scl); + _i2c_bus_alt = new I2cHardware(device_descriptor.i2c_bus_sda, + device_descriptor.i2c_bus_scl); } use_alt_bus = true; } @@ -765,8 +1008,7 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { if ((strcmp(device_name, "pca9546") == 0) || (strcmp(device_name, "pca9548") == 0)) { WS_DEBUG_PRINT("[i2c] Initializing MUX driver..."); - if (!InitMux(device_name, device_descriptor.i2c_device_address, - use_alt_bus)) { + if (!InitMux(device_name, device_descriptor.i2c_mux_address, use_alt_bus)) { // TODO [Online]: Publish back out to IO here! WsV2.haltErrorV2("[i2c] Failed to initialize MUX driver!", WS_LED_STATUS_ERROR_RUNTIME, false); @@ -779,6 +1021,8 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { // I2cDeviceAddorReplace message if (device_descriptor.i2c_mux_address != 0x00) { if (_i2c_bus_alt->HasMux() || _i2c_bus_default->HasMux()) { + WS_DEBUG_PRINT("[i2c] Configuring MUX channel: "); + WS_DEBUG_PRINTLN(device_descriptor.i2c_mux_channel); ConfigureMuxChannel(device_descriptor.i2c_mux_channel, use_alt_bus); did_set_mux_ch = true; } else { @@ -788,6 +1032,7 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { } } + WS_DEBUG_PRINTLN("Creating a new I2C driver"); // Assign I2C bus TwoWire *bus = nullptr; if (use_alt_bus) { @@ -796,35 +1041,37 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { bus = _i2c_bus_default->GetBus(); } + // Attempt to create the driver + bool did_init = false; drvBase *drv = nullptr; - WS_DEBUG_PRINTLN("[i2c] Creating driver: "); - WS_DEBUG_PRINTLN(device_name); - if (strcmp(device_name, SCAN_DEVICE) == 0) { + drvOutputBase *drv_out = nullptr; + GPSController *drv_uart_gps = nullptr; + + if (strcmp(device_name, "UNKNOWN_SCAN") == 0) { WS_DEBUG_PRINTLN("Attempting to autoconfig device found in scan..."); - // Get all possible driver candidates for this address - WS_DEBUG_PRINT("[i2c] Obtaining driver candidates @ 0x"); - WS_DEBUG_PRINTLN(device_descriptor.i2c_device_address, HEX); if (device_descriptor.i2c_device_address == 0x68 || device_descriptor.i2c_device_address == 0x70) { WS_DEBUG_PRINTLN("[i2c] Device address is shared with RTC/MUX, can not " "auto-init, skipping!"); return true; } - - std::vector candidate_drivers = - GetDriversForAddress(device_descriptor.i2c_device_address); + // Get all possible driver candidates for this address + WS_DEBUG_PRINT("[i2c] Obtaining driver candidates @ 0x"); + WS_DEBUG_PRINTLN(device_descriptor.i2c_device_address, HEX); // Probe each candidate to see if it communicates bool did_find_driver = false; - for (const char *driverName : candidate_drivers) { + for (const char *driverName : + GetDriversForAddress(device_descriptor.i2c_device_address)) { WS_DEBUG_PRINT("[i2c] Attempting to initialize candidate: "); WS_DEBUG_PRINTLN(driverName); - drv = CreateI2CDriverByName( + drv = CreateI2cSensorDrv( driverName, bus, device_descriptor.i2c_device_address, device_descriptor.i2c_mux_channel, device_status); // Probe the driver to check if it communicates its init. sequence - // properly if (!drv->begin()) { + WS_DEBUG_PRINTLN("[i2c] Failed to initialize candidate: "); + WS_DEBUG_PRINTLN(driverName); delete drv; drv = nullptr; } else { @@ -833,70 +1080,161 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { // set device_name to driverName strcpy(device_name, driverName); // Use the "default" types from the sensor driver - drv->SetSensorTypes(true); - drv->SetPeriod(DEFAULT_SENSOR_PERIOD); + drv->EnableSensorReads(true); + drv->SetSensorPeriod(DEFAULT_SENSOR_PERIOD); #ifndef OFFLINE_MODE_WOKWI WsV2._fileSystemV2->AddI2cDeviceToFileConfig( device_descriptor.i2c_device_address, driverName, drv->GetSensorTypeStrings(), drv->GetNumSensorTypes()); #endif did_find_driver = true; - break; + _i2c_drivers.push_back(drv); + return true; } } if (!did_find_driver) { WS_DEBUG_PRINTLN("[i2c] ERROR - Candidates exhausted, driver not found!"); return true; // dont cause an error in the app } - } else { - WS_DEBUG_PRINTLN("[i2c] Device in message/cfg file."); - // Create new driver - WS_DEBUG_PRINT("[i2c] Creating driver: "); - WS_DEBUG_PRINTLN(device_name); - drv = CreateI2CDriverByName( + } + + else if (is_output) { + WS_DEBUG_PRINT("[i2c] Creating an I2C output driver..."); + drv_out = CreateI2cOutputDrv( device_name, bus, device_descriptor.i2c_device_address, device_descriptor.i2c_mux_channel, device_status); - if (drv == nullptr) { - WS_DEBUG_PRINTLN( - "[i2c] ERROR: I2C driver type not found or unsupported!"); - return false; + if (drv_out != nullptr) { + did_init = true; } + WS_DEBUG_PRINTLN("OK!"); + } else if (is_gps) { + WS_DEBUG_PRINT("[i2c] Creating a GPS driver..."); + if (!WsV2._gps_controller->AddGPS( + bus, device_descriptor.i2c_device_address, + &_i2c_model->GetI2cDeviceAddOrReplaceMsg()->gps_config)) { + did_init = false; + WS_DEBUG_PRINTLN("FAILURE!"); + } else { + did_init = true; + WS_DEBUG_PRINTLN("OK!"); + // TODO: We are doing an early-out here and should publish back to IO! + return true; + } + } else { + drv = CreateI2cSensorDrv(device_name, bus, + device_descriptor.i2c_device_address, + device_descriptor.i2c_mux_channel, device_status); + if (drv != nullptr) { + did_init = true; + } + } - // Configure MUX and bus - if (did_set_mux_ch) { - drv->SetMuxAddress(device_descriptor.i2c_mux_address); + if (!did_init) { + WS_DEBUG_PRINTLN("[i2c] ERROR: I2C driver failed to initialize!"); + if (WsV2._sdCardV2->isModeOffline()) { + WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set " + "the correct value for i2cDeviceName?\n\tDid you set " + "the correct value for" + "i2cDeviceAddress?", + WS_LED_STATUS_ERROR_RUNTIME, false); } + } - if (use_alt_bus) { + // Attempt to initialize the driver + if (did_set_mux_ch) { + if (!is_output) { + drv->SetMuxAddress(device_descriptor.i2c_mux_address); + } else { + WS_DEBUG_PRINTLN("[i2c] Setting MUX address for output driver..."); + drv_out->SetMuxAddress(device_descriptor.i2c_mux_address); + } + WS_DEBUG_PRINTLN("[i2c] Set driver to use MUX"); + } + if (use_alt_bus) { + if (!is_output) { drv->EnableAltI2CBus(_i2c_model->GetI2cDeviceAddOrReplaceMsg() ->i2c_device_description.i2c_bus_scl, _i2c_model->GetI2cDeviceAddOrReplaceMsg() ->i2c_device_description.i2c_bus_sda); + } else { + WS_DEBUG_PRINTLN("[i2c] Setting alt. I2C bus for output driver..."); + drv_out->EnableAltI2CBus(_i2c_model->GetI2cDeviceAddOrReplaceMsg() + ->i2c_device_description.i2c_bus_scl, + _i2c_model->GetI2cDeviceAddOrReplaceMsg() + ->i2c_device_description.i2c_bus_sda); } - // Configure the driver - drv->SetSensorTypes( + WS_DEBUG_PRINTLN("[i2c] Set driver to use Alt I2C bus"); + } + + // Configure the driver + if (!is_output) { + // Configure Input-driver settings + drv->EnableSensorReads( false, _i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_sensor_types, _i2c_model->GetI2cDeviceAddOrReplaceMsg() ->i2c_device_sensor_types_count); - - drv->SetPeriod( + drv->SetSensorPeriod( _i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_device_period); + } else { + WS_DEBUG_PRINTLN("[i2c] Configuring output driver..."); + // Configure Output-driver settings + pb_size_t config = + _i2c_model->GetI2cDeviceAddOrReplaceMsg()->i2c_output_add.which_config; + if (config == + wippersnapper_i2c_output_I2cOutputAdd_led_backpack_config_tag) { + WS_DEBUG_PRINTLN("[i2c] Configuring LED backpack..."); + wippersnapper_i2c_output_LedBackpackConfig cfg = + _i2c_model->GetI2cDeviceAddOrReplaceMsg() + ->i2c_output_add.config.led_backpack_config; + WS_DEBUG_PRINT("[i2c] Got cfg, calling ConfigureI2CBackpack..."); + drv_out->ConfigureI2CBackpack(cfg.brightness, cfg.alignment); + WS_DEBUG_PRINTLN("OK!"); + } else if (config == + wippersnapper_i2c_output_I2cOutputAdd_char_lcd_config_tag) { + WS_DEBUG_PRINTLN("[i2c] Configuring char LCD..."); + } else if (config == + wippersnapper_i2c_output_I2cOutputAdd_oled_config_tag) { + WS_DEBUG_PRINTLN("[i2c] Configuring OLED..."); + wippersnapper_i2c_output_OledConfig cfg = + _i2c_model->GetI2cDeviceAddOrReplaceMsg() + ->i2c_output_add.config.oled_config; + WS_DEBUG_PRINT("[i2c] Got cfg, calling ConfigureOLED..."); + drv_out->ConfigureSSD1306(cfg.width, cfg.height, cfg.font_size); + WS_DEBUG_PRINTLN("OK!"); + } else { + WS_DEBUG_PRINTLN( + "[i2c] ERROR: Unknown config specified for output driver!"); + return false; + } + } + if (!is_output) { if (!drv->begin()) { if (WsV2._sdCardV2->isModeOffline()) { - WS_DEBUG_PRINTLN("[i2c] Failed to initialize driver!\n\tDid you set " + WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set " + "the correct value for i2cDeviceName?\n\tDid you set " + "the correct value for" + "i2cDeviceAddress?", + WS_LED_STATUS_ERROR_RUNTIME, false); + } + } + _i2c_drivers.push_back(drv); + } else { + if (!drv_out->begin()) { + if (WsV2._sdCardV2->isModeOffline()) { + WsV2.haltErrorV2("[i2c] Driver failed to initialize!\n\tDid you set " "the correct value for i2cDeviceName?\n\tDid you set " "the correct value for" - "i2cDeviceAddress?"); + "i2cDeviceAddress?", + WS_LED_STATUS_ERROR_RUNTIME, false); } - return true; // don't cause an error during runtime if the device is not - // found } - WS_DEBUG_PRINTLN("[i2c] Driver successfully initialized!"); + _i2c_drivers_output.push_back(drv_out); } - // Add the initialized driver - _i2c_drivers.push_back(drv); + + WS_DEBUG_PRINTLN("[i2c] Driver initialized and added to controller: "); + WS_DEBUG_PRINTLN(device_name); // If we're using a MUX, clear the channel for any subsequent bus // operations that may not involve the MUX @@ -912,6 +1250,7 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { // Create and publish the I2cDeviceAddedorReplaced message to the broker WS_DEBUG_PRINTLN("[i2c] MQTT Publish I2cDeviceAddedorReplaced not yet " "implemented!"); + // TODO! /* if (!PublishI2cDeviceAddedorReplaced(device_descriptor, device_status)) return false; */ } @@ -919,36 +1258,6 @@ bool I2cController::Handle_I2cDeviceAddOrReplace(pb_istream_t *stream) { return true; } -/***********************************************************************/ -/*! - @brief Scans the I2C bus for devices and stores the results. - @param default_bus - True to scan the default I2C bus, False to scan the - alternative I2C bus. - @returns True if the I2C bus was successfully scanned, False - if the scan failed with an error. -*/ -/***********************************************************************/ -bool I2cController::ScanI2cBus(bool default_bus = true) { - // zero-out the scan I2cBusScanned message before attempting a scan - _scan_results = wippersnapper_i2c_I2cBusScanned_init_zero; - - // Scan the desired i2c bus - if (default_bus) { - if (_i2c_bus_default->GetBusStatus() != - wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) { - _i2c_bus_default->InitBus(default_bus); - } - return _i2c_bus_default->ScanBus(&_scan_results); - } else { - if (_i2c_bus_alt->GetBusStatus() != - wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) { - _i2c_bus_alt->InitBus(default_bus); - } - return _i2c_bus_alt->ScanBus(&_scan_results); - } -} - /***********************************************************************/ /*! @brief Returns the I2C bus object. @@ -965,28 +1274,6 @@ TwoWire *I2cController::GetI2cBus(bool is_alt_bus) { return _i2c_bus_default->GetBus(); } -/***********************************************************************/ -/*! - @brief Checks if a device was found on the i2c bus. MUST be called - after scanning was performed. - @param address - The desired I2C device address. - @returns True if the device is on the bus, False otherwise. -*/ -/***********************************************************************/ -bool I2cController::WasDeviceScanned(uint32_t address) { - pb_size_t num_found_devices = _scan_results.i2c_bus_found_devices_count; - if (num_found_devices == 0) - return false; // no devices found on bus, or scan was not performed - - // Check if the device was found on the bus - for (pb_size_t i; i < num_found_devices; i++) { - if (_scan_results.i2c_bus_found_devices[i].i2c_device_address == address) - return true; // device found on bus! - } - return false; // exhausted all scanned devices, didn't find it -} - /***********************************************************************/ /*! @brief Returns an i2c address of a device found on the bus. @@ -1011,7 +1298,36 @@ size_t I2cController::GetScanDeviceCount() { return _scan_results.i2c_bus_found_devices_count; } -/********************************************************************************/ +/***********************************************************************/ +/*! + @brief Scans the I2C bus for devices and stores the results. + @param default_bus + True to scan the default I2C bus, False to scan the + alternative I2C bus. + @returns True if the I2C bus was successfully scanned, False + if the scan failed with an error. +*/ +/***********************************************************************/ +bool I2cController::ScanI2cBus(bool default_bus = true) { + // zero-out the scan I2cBusScanned message before attempting a scan + _scan_results = wippersnapper_i2c_I2cBusScanned_init_zero; + + // Scan the desired i2c bus + if (default_bus) { + if (_i2c_bus_default->GetBusStatus() != + wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) { + _i2c_bus_default->InitBus(default_bus); + } + return _i2c_bus_default->ScanBus(&_scan_results); + } else { + if (_i2c_bus_alt->GetBusStatus() != + wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS) { + _i2c_bus_alt->InitBus(default_bus); + } + return _i2c_bus_alt->ScanBus(&_scan_results); + } +} + /*! @brief Enables a MUX channel on the appropriate I2C bus. @param mux_channel @@ -1019,7 +1335,6 @@ size_t I2cController::GetScanDeviceCount() { @param is_alt_bus True if an alternative I2C bus is being used, False otherwise. */ -/********************************************************************************/ void I2cController::ConfigureMuxChannel(uint32_t mux_channel, bool is_alt_bus) { if (is_alt_bus) { _i2c_bus_alt->ClearMuxChannel(); // sanity-check @@ -1030,6 +1345,9 @@ void I2cController::ConfigureMuxChannel(uint32_t mux_channel, bool is_alt_bus) { _i2c_bus_default->SelectMuxChannel(mux_channel); } +/*! + @brief Prints all drivers attached to the I2C controller. +*/ void I2cController::PrintAllDrivers() { WS_DEBUG_PRINTLN("[i2c] Printing all drivers..."); for (drvBase *drv : _i2c_drivers) { @@ -1037,26 +1355,26 @@ void I2cController::PrintAllDrivers() { } } -/***********************************************************************/ /*! @brief Handles polling, reading, and logger for i2c devices attached to the I2C controller. */ -/***********************************************************************/ void I2cController::update() { - if (_i2c_drivers.size() == 0) + // WS_DEBUG_PRINTLN("[i2c] Updating I2C controller..."); + if (_i2c_drivers.empty()) return; // bail out if no drivers exist for (auto *drv : _i2c_drivers) { // Does this driver have any enabled sensors? - size_t sensor_count = drv->GetNumSensorTypes(); + size_t sensor_count = drv->GetEnabledSensorCnt(); if (sensor_count == 0) continue; // bail out if driver has no sensors enabled // Did driver's period elapse yet? ulong cur_time = millis(); - if (cur_time - drv->GetSensorPeriodPrv() < drv->GetSensorPeriod()) + if (cur_time - drv->GetSensorPeriodPrv() < drv->GetSensorPeriod()) { continue; // bail out if the period hasn't elapsed yet + } // Optionally configure the I2C MUX uint32_t mux_channel = drv->GetMuxChannel(); @@ -1084,7 +1402,7 @@ void I2cController::update() { // Handle the DeviceEvent message if (WsV2._sdCardV2->isModeOffline()) { - if (!WsV2._sdCardV2->LogI2cDeviceEvent(_i2c_model->GetI2cDeviceEvent())) { + if (!WsV2._sdCardV2->LogEventI2c(_i2c_model->GetI2cDeviceEvent())) { WS_DEBUG_PRINTLN( "[i2c] ERROR: Unable to log the I2cDeviceEvent to SD!"); statusLEDSolid(WS_LED_STATUS_FS_WRITE); diff --git a/src/components/i2c/controller.h b/src/components/i2c/controller.h index 10e15f8c3..6390365ed 100644 --- a/src/components/i2c/controller.h +++ b/src/components/i2c/controller.h @@ -1,5 +1,5 @@ /*! - * @file controller.h + * @file src/components/i2c/controller.h * * Routing controller for WipperSnapper's I2C component. * @@ -20,7 +20,7 @@ // I2C Drivers #include "drivers/drvAdt7410.h" #include "drivers/drvAhtx0.h" -#include "drivers/drvBase.h" ///< Base driver class +#include "drivers/drvBase.h" ///< Base i2c input driver class #include "drivers/drvBh1750.h" #include "drivers/drvBme280.h" #include "drivers/drvBme680.h" @@ -29,16 +29,12 @@ #include "drivers/drvDps310.h" #include "drivers/drvDs2484.h" #include "drivers/drvEns160.h" -#include "drivers/drvHdc302x.h" #include "drivers/drvHts221.h" #include "drivers/drvHtu21d.h" -#include "drivers/drvHtu31d.h" #include "drivers/drvIna219.h" -#include "drivers/drvIna260.h" #include "drivers/drvLc709203f.h" #include "drivers/drvLps22hb.h" #include "drivers/drvLps25hb.h" -#include "drivers/drvLps28dfw.h" #include "drivers/drvLps3xhw.h" #include "drivers/drvLtr329_Ltr303.h" #include "drivers/drvLtr390.h" @@ -49,13 +45,16 @@ #include "drivers/drvMprls.h" #include "drivers/drvMs8607.h" #include "drivers/drvNau7802.h" +#include "drivers/drvOut7Seg.h" +#include "drivers/drvOutCharLcd.h" +#include "drivers/drvOutQuadAlphaNum.h" +#include "drivers/drvOutSsd1306.h" +#include "drivers/drvOutputBase.h" ///< Base i2c output driver class #include "drivers/drvPct2075.h" #include "drivers/drvPm25.h" #include "drivers/drvScd30.h" #include "drivers/drvScd4x.h" #include "drivers/drvSen5x.h" -#include "drivers/drvSen6x.h" -#include "drivers/drvSgp30.h" #include "drivers/drvSgp40.h" #include "drivers/drvSht3x.h" #include "drivers/drvSht4x.h" @@ -72,26 +71,16 @@ #include "drivers/drvVl6180x.h" #include "drivers/drvVncl4020.h" #include "drivers/drvVncl4040.h" -#include "drivers/drvVncl4200.h" - -#define SCAN_DEVICE \ - "UNKNOWN_SCAN" ///< Name for I2C devices found by an i2c scan - -typedef struct { - uint32_t address; ///< I2C address of the device - bool is_initialized; ///< Flag to indicate if the device is initialized -} initialized_device_t; ///< Struct to hold initialized device info class Wippersnapper_V2; ///< Forward declaration class I2cModel; ///< Forward declaration +class I2cOutputModel; ///< Forward declaration class I2cHardware; ///< Forward declaration -/**************************************************************************/ /*! @brief Routes messages using the i2c.proto API to the appropriate hardware, model, and device driver classes. */ -/**************************************************************************/ class I2cController { public: I2cController(); @@ -99,31 +88,33 @@ class I2cController { void update(); // Routing // bool Handle_I2cDeviceAddOrReplace(pb_istream_t *stream); - // TODO [Online]: These are for Online mode and not yet implemented + bool Handle_I2cBusScan(pb_istream_t *stream); bool Handle_I2cDeviceRemove(pb_istream_t *stream); - // bool Handle_I2cBusScan(pb_istream_t *stream); + bool Handle_I2cDeviceOutputWrite(pb_istream_t *stream); // Publishing // bool PublishI2cDeviceAddedorReplaced( const wippersnapper_i2c_I2cDeviceDescriptor &device_descriptor, const wippersnapper_i2c_I2cDeviceStatus &device_status); // Helpers // - bool IsBusStatusOK(bool is_alt_bus); + bool IsBusStatusOK(bool is_alt_bus = false); bool InitMux(const char *name, uint32_t address, bool is_alt_bus); void ConfigureMuxChannel(uint32_t mux_channel, bool is_alt_bus); + bool RemoveDriver(uint32_t address, bool is_output_device); bool ScanI2cBus(bool default_bus); - bool WasDeviceScanned(uint32_t address); + TwoWire *GetI2cBus(bool is_alt_bus = false); uint32_t GetScanDeviceAddress(int index); size_t GetScanDeviceCount(); - bool - IsDriverInitialized(wippersnapper_i2c_I2cDeviceDescriptor &device_descriptor); - TwoWire *GetI2cBus(bool is_alt_bus = false); void PrintAllDrivers(); private: - I2cModel *_i2c_model; ///< Pointer to an I2C model object - I2cHardware *_i2c_bus_default; ///< Pointer to the default I2C bus - I2cHardware *_i2c_bus_alt; ///< Pointer to an alternative I2C bus - std::vector _i2c_drivers; ///< Vector of ptrs to I2C device drivers + I2cModel *_i2c_model = nullptr; ///< Pointer to an I2C model object + I2cOutputModel *_i2c_output_model = + nullptr; ///< Pointer to an I2C output model object + I2cHardware *_i2c_bus_default = nullptr; ///< Pointer to the default I2C bus + I2cHardware *_i2c_bus_alt = nullptr; ///< Pointer to an alternative I2C bus + std::vector _i2c_drivers; ///< Vector of ptrs to I2C input drivers + std::vector + _i2c_drivers_output; ///< Vector of ptrs to I2C output drivers wippersnapper_i2c_I2cBusScanned _scan_results; ///< Stores results of I2C bus scan }; diff --git a/src/components/i2c/drivers/drvAdt7410.h b/src/components/i2c/drivers/drvAdt7410.h index aa99773ec..8bbd3be46 100644 --- a/src/components/i2c/drivers/drvAdt7410.h +++ b/src/components/i2c/drivers/drvAdt7410.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a ADT7410 sensor. */ -/**************************************************************************/ class drvAdt7410 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a ADT7410 sensor. @param i2c @@ -37,35 +34,29 @@ class drvAdt7410 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvAdt7410(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an ADT7410 sensor. */ - /*******************************************************************************/ ~drvAdt7410() { // Called when a ADT7410 component is deleted. delete _ADT7410; } - /*******************************************************************************/ /*! @brief Initializes the ADT7410 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _ADT7410 = new Adafruit_ADT7410(); return _ADT7410->begin((uint8_t)_address, _i2c); } - /*******************************************************************************/ /*! @brief Gets the ADT7410's current temperature. @param tempEvent @@ -73,7 +64,6 @@ class drvAdt7410 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _ADT7410->readTempC(); return true; diff --git a/src/components/i2c/drivers/drvAhtx0.h b/src/components/i2c/drivers/drvAhtx0.h index 8643c4c67..fb435f71d 100644 --- a/src/components/i2c/drivers/drvAhtx0.h +++ b/src/components/i2c/drivers/drvAhtx0.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the AHTX0 temperature and humidity sensor. */ -/**************************************************************************/ class drvAhtx0 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an AHTX0 sensor. @param i2c @@ -40,27 +37,22 @@ class drvAhtx0 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvAhtx0(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an AHTX0 sensor. */ - /*******************************************************************************/ ~drvAhtx0() { delete _aht; } - /*******************************************************************************/ /*! @brief Initializes the AHTX0 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { // attempt to initialize the driver _aht = new Adafruit_AHTX0(); @@ -78,7 +70,6 @@ class drvAhtx0 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the AHTX0's current temperature. @param tempEvent @@ -86,13 +77,11 @@ class drvAhtx0 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { _aht_temp->getEvent(tempEvent); return true; } - /*******************************************************************************/ /*! @brief Gets the AHTX0's current humidity. @param humidEvent @@ -100,7 +89,6 @@ class drvAhtx0 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { _aht_humidity->getEvent(humidEvent); return true; diff --git a/src/components/i2c/drivers/drvBase.h b/src/components/i2c/drivers/drvBase.h index 2efbb71e5..70ee12352 100644 --- a/src/components/i2c/drivers/drvBase.h +++ b/src/components/i2c/drivers/drvBase.h @@ -22,15 +22,12 @@ #define NO_MUX_CH 0xFFFF; ///< No MUX channel specified #define DEFAULT_SENSOR_PERIOD 15.0f ///< Default sensor period, in seconds -/**************************************************************************/ /*! @brief Base class for I2C Drivers. */ -/**************************************************************************/ class drvBase { public: - /*******************************************************************************/ /*! @brief Instanciates an I2C sensor. @param i2c @@ -43,7 +40,6 @@ class drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBase(TwoWire *i2c, uint16_t address, uint32_t mux_channel, const char *driver_name) { _i2c = i2c; @@ -58,47 +54,36 @@ class drvBase { _default_sensor_types_count = 0; } - /*******************************************************************************/ /*! @brief Destructor for an I2C sensor. */ - /*******************************************************************************/ virtual ~drvBase() {} - /*******************************************************************************/ /*! @brief Gets the name of the driver. @returns The driver's name. */ - /*******************************************************************************/ const char *GetDrvName() { return _name; } - /*******************************************************************************/ /*! @brief Gets the I2C device's address. @returns The I2C device's unique i2c address. */ - /*******************************************************************************/ uint16_t GetAddress() { return _address; } - /*******************************************************************************/ /*! @brief Gets the I2C MUX address. @returns The I2C MUX address. */ - /*******************************************************************************/ uint32_t GetMuxAddress() { return _i2c_mux_addr; } - /*******************************************************************************/ /*! @brief Sets the I2C MUX address. @param mux_address The I2C MUX address. */ - /*******************************************************************************/ void SetMuxAddress(uint32_t mux_address) { _i2c_mux_addr = mux_address; } - /*******************************************************************************/ /*! @brief Set if the I2C driver has an alternative I2C bus. @param scl_pin @@ -106,7 +91,6 @@ class drvBase { @param sda_pin The SDA pin for the alternative I2C bus. */ - /*******************************************************************************/ void EnableAltI2CBus(char *scl_pin, char *sda_pin) { if (scl_pin == nullptr || sda_pin == nullptr) { WS_DEBUG_PRINTLN("[drvBase] ERROR: Invalid Pins for Alt I2C bus!"); @@ -117,48 +101,37 @@ class drvBase { _has_alt_i2c_bus = true; } - /*******************************************************************************/ /*! @brief Gets the SCL pin for the alternative I2C bus. @returns The SCL pin for the alternative I2C bus. */ - /*******************************************************************************/ const char *GetPinSCL() { return _pin_scl; } - /*******************************************************************************/ /*! @brief Gets the SDA pin for the alternative I2C bus. @returns The SDA pin for the alternative I2C bus. */ - /*******************************************************************************/ const char *GetPinSDA() { return _pin_sda; } - /*******************************************************************************/ /*! @brief Checks if the I2C driver uses an alternative I2C bus. @returns True if the I2C driver uses an alternative I2C bus, False otherwise. */ - /*******************************************************************************/ bool HasAltI2CBus() { return _has_alt_i2c_bus; } - /*******************************************************************************/ /*! @brief Gets the I2C MUX channel connected to the I2C device. @returns The desired MUX channel. */ - /*******************************************************************************/ uint32_t GetMuxChannel() { return _i2c_mux_channel; } - /*******************************************************************************/ /*! @brief Checks if the I2C driver is attached to an I2C MUX. @returns True if the I2C driver uses an I2C MUX, False otherwise. */ - /*******************************************************************************/ bool HasMux() { return _i2c_mux_channel != NO_MUX_CH; } - /*******************************************************************************/ /*! @brief Configures an i2c device's sensors. @param use_default_types @@ -169,11 +142,10 @@ class drvBase { @param sensor_types_count The number of active sensors to read from the device. */ - /*******************************************************************************/ - void SetSensorTypes(bool use_default_types = false, - wippersnapper_sensor_SensorType *sensor_types = nullptr, - size_t sensor_types_count = 0) { - + void + EnableSensorReads(bool use_default_types = false, + wippersnapper_sensor_SensorType *sensor_types = nullptr, + size_t sensor_types_count = 0) { // Assign number of sensors if (use_default_types) { // Configure the driver with values from THE DRIVER @@ -194,7 +166,6 @@ class drvBase { } } - /*******************************************************************************/ /*! @brief Gets a ptr to an array containing the sensor types, as strings. @returns Pointer to an array of strings. @@ -214,50 +185,42 @@ class drvBase { @brief Gets the number of enabled sensors. @returns The number of enabled sensors. */ - /*******************************************************************************/ - size_t GetNumSensorTypes() { return _sensors_count; } + size_t GetEnabledSensorCnt() { return _sensors_count; } - /*******************************************************************************/ /*! @brief Initializes the I2C sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ virtual bool begin() = 0; - /*******************************************************************************/ /*! @brief Sets the sensor's period and converts from seconds to milliseconds. @param period The period for the sensor to return values within, in seconds. */ - /*******************************************************************************/ - void SetPeriod(float period) { - if (period < 0) - _sensor_period = DEFAULT_SENSOR_PERIOD; + void SetSensorPeriod(float period) { + if (period < 0) { + _sensor_period = 0; + return; + } _sensor_period = (unsigned long)(period * 1000.0f); } - /*******************************************************************************/ /*! @brief Sets the sensor's previous period and converts from seconds to milliseconds. @param period The period for the sensor to return values within, in seconds. */ - /*******************************************************************************/ void SetSensorPeriodPrv(ulong period) { _sensor_period_prv = period; } - /*******************************************************************************/ /*! @brief Gets the sensor's period. @returns The sensor's period, in milliseconds. */ - /*******************************************************************************/ ulong GetSensorPeriod() { return _sensor_period; } - /*******************************************************************************/ /*! @brief Gets the sensor's types @returns A pointer to an array of SensorTypes. @@ -265,15 +228,15 @@ class drvBase { /*******************************************************************************/ wippersnapper_sensor_SensorType *GetSensorTypes() { return _sensors; } + size_t GetNumSensorTypes() { return _sensors_count; } + /*******************************************************************************/ /*! @brief Gets the sensor's previous period. @returns The sensor's previous period, in milliseconds. */ - /*******************************************************************************/ ulong GetSensorPeriodPrv() { return _sensor_period_prv; } - /*******************************************************************************/ /*! @brief Gets a sensor's CO2 value. @param co2Event @@ -281,10 +244,8 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventCO2(sensors_event_t *co2Event) { return false; }; - /*******************************************************************************/ /*! @brief Gets a sensor's eCO2 value. @param eco2Event @@ -292,10 +253,8 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventECO2(sensors_event_t *eco2Event) { return false; } - /*******************************************************************************/ /*! @brief Gets a sensor's TVOC value. @param tvocEvent @@ -303,10 +262,8 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventTVOC(sensors_event_t *tvocEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads an ambient temperature sensor (°C). Expects value to return in the proper SI unit. @@ -315,10 +272,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventAmbientTemp(sensors_event_t *tempEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a humidity sensor and converts the reading into the expected SI unit. @@ -327,12 +282,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventRelativeHumidity(sensors_event_t *humidEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a pressure sensor and converts the reading into the expected SI unit. @@ -341,12 +294,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPressure(sensors_event_t *pressureEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a Altitude sensor and converts the reading into the expected SI unit. @@ -355,12 +306,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventAltitude(sensors_event_t *altitudeEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object temperature sensor and converts the reading into the expected SI unit. @@ -369,12 +318,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventObjectTemp(sensors_event_t *objectTempEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Selects a MUX channel for use with the I2C device. @@ -383,7 +330,6 @@ class drvBase { */ virtual void SelectMUXChannel(uint8_t channel) { return; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object light sensor and converts the reading into the expected SI unit. @@ -392,10 +338,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventLight(sensors_event_t *lightEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm10 std. sensor and converts the reading into the expected SI unit. @@ -404,10 +348,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm10 env. sensor and converts the reading into the expected SI unit. @@ -416,10 +358,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM10_Env(sensors_event_t *pm10EnvEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm25 std. sensor and converts the reading into the expected SI unit. @@ -428,10 +368,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm25 env. sensor and converts the reading into the expected SI unit. @@ -440,10 +378,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM25_Env(sensors_event_t *pm25EnvEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm100 std. sensor and converts the reading into the expected SI unit. @@ -452,12 +388,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object pm100 env. sensor and converts the reading into the expected SI unit. @@ -466,12 +400,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventPM100_Env(sensors_event_t *pm100EnvEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a object unitless % std. sensor and converts the reading into the expected SI unit. @@ -480,12 +412,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventUnitlessPercent(sensors_event_t *unitlessPercentEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a voltage sensor and converts the reading into the expected SI unit. @@ -494,10 +424,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventVoltage(sensors_event_t *voltageEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a current sensor and converts the reading into the expected SI unit. @@ -506,10 +434,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventCurrent(sensors_event_t *currentEvent) { return false; } - /*******************************************************************************/ /*! @brief Gets a sensor's Raw value. @param rawEvent @@ -517,10 +443,8 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventRaw(sensors_event_t *rawEvent) { return false; } - /*******************************************************************************/ /*! @brief Helper function to obtain a sensor's ambient temperature value in °F. Requires `getEventAmbientTemp()` to be fully @@ -530,7 +454,6 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventAmbientTempF(sensors_event_t *AmbientTempFEvent) { // obtain ambient temp. in °C if (!getEventAmbientTemp(AmbientTempFEvent)) { @@ -542,7 +465,6 @@ class drvBase { return true; } - /*******************************************************************************/ /*! @brief Helper function to obtain a sensor's object temperature value in °F. Requires `getEventObjectTemp()` to be fully @@ -552,7 +474,6 @@ class drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventObjectTempF(sensors_event_t *objectTempFEvent) { // obtain ambient temp. in °C if (!getEventObjectTemp(objectTempFEvent)) @@ -566,7 +487,6 @@ class drvBase { /****************************** SENSOR_TYPE: Gas Resistance (ohms) * *******************************/ - /*******************************************************************************/ /*! @brief Base implementation - Reads a gas resistance sensor and converts the reading into the expected SI unit. @@ -575,12 +495,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventGasResistance(sensors_event_t *gasEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a NOx Index sensor and converts the reading into the expected SI unit. @@ -589,10 +507,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventNOxIndex(sensors_event_t *gasEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a VOC Index sensor and converts the reading into the expected SI unit. @@ -601,10 +517,8 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventVOCIndex(sensors_event_t *gasEvent) { return false; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a proximity sensor and converts the reading into the expected SI unit. @@ -613,12 +527,10 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventProximity(sensors_event_t *proximityEvent) { return false; } - /*******************************************************************************/ /*! @brief Reads a sensor's event from the i2c driver. @param sensor_type @@ -628,7 +540,6 @@ class drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool GetSensorEvent(wippersnapper_sensor_SensorType sensor_type, sensors_event_t *sensors_event) { auto it = SensorEventHandlers.find(sensor_type); @@ -637,14 +548,12 @@ class drvBase { return it->second(sensors_event); } - /*******************************************************************************/ /*! @brief Function type for sensor event handlers @param sensors_event_t* Pointer to the sensor event structure to be filled @returns True if event was successfully read, False otherwise */ - /*******************************************************************************/ using fnGetEvent = std::function; // Maps SensorType to function calls diff --git a/src/components/i2c/drivers/drvBh1750.h b/src/components/i2c/drivers/drvBh1750.h index 98658c40f..a9f402320 100644 --- a/src/components/i2c/drivers/drvBh1750.h +++ b/src/components/i2c/drivers/drvBh1750.h @@ -18,7 +18,6 @@ #include "drvBase.h" #include //include the library for the BH1750 sensor -/**************************************************************************/ /*! @brief Class that provides a driver interface for a BH1750 Light sensor. @@ -30,10 +29,8 @@ This driver uses the One Time Measurement feature of the BH1750. The sensor returns to Power Down mode after each reading. */ -/**************************************************************************/ class drvBh1750 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a BH1750 sensor. @param i2c @@ -45,30 +42,25 @@ class drvBh1750 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBh1750(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for a BH1750 sensor. */ - /*******************************************************************************/ ~drvBh1750() { // Called when a BH1750 component is deleted. delete _bh1750; } - /*******************************************************************************/ /*! @brief Initializes the BH1750 sensor and begins I2C. The set the quality to the H-Resolution Mode. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _bh1750 = new hp_BH1750(); // attempt to initialize BH1750 @@ -79,7 +71,6 @@ class drvBh1750 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the One Time Measurement feature of the BH1750. The sensor goes to Power Down mode after @@ -89,7 +80,6 @@ class drvBh1750 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { _bh1750->start(); lightEvent->light = _bh1750->getLux(); diff --git a/src/components/i2c/drivers/drvBme280.h b/src/components/i2c/drivers/drvBme280.h index de5f6d378..56b2726ee 100644 --- a/src/components/i2c/drivers/drvBme280.h +++ b/src/components/i2c/drivers/drvBme280.h @@ -21,16 +21,13 @@ #define SEALEVELPRESSURE_HPA (1013.25) ///< Default sea level pressure, in hPa -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the BME280 temperature and humidity sensor. */ -/**************************************************************************/ class drvBme280 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an BME280 sensor. @param i2c @@ -42,26 +39,21 @@ class drvBme280 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBme280(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an BME280 sensor. */ - /*******************************************************************************/ ~drvBme280() { delete _bme; } - /*******************************************************************************/ /*! @brief Initializes the BME280 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _bme = new Adafruit_BME280(); // attempt to initialize BME280 @@ -83,7 +75,6 @@ class drvBme280 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the BME280's current temperature. @param tempEvent @@ -91,12 +82,10 @@ class drvBme280 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _bme_temp->getEvent(tempEvent); } - /*******************************************************************************/ /*! @brief Gets the BME280's current relative humidity reading. @param humidEvent @@ -104,12 +93,10 @@ class drvBme280 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { return _bme_humidity->getEvent(humidEvent); } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -118,12 +105,10 @@ class drvBme280 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { return _bme_pressure->getEvent(pressureEvent); } - /*******************************************************************************/ /*! @brief Reads a the BME280's altitude sensor into an event. @param altitudeEvent @@ -131,7 +116,6 @@ class drvBme280 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAltitude(sensors_event_t *altitudeEvent) { altitudeEvent->altitude = _bme->readAltitude(SEALEVELPRESSURE_HPA); return true; diff --git a/src/components/i2c/drivers/drvBme680.h b/src/components/i2c/drivers/drvBme680.h index 70b76e4be..93b698f55 100644 --- a/src/components/i2c/drivers/drvBme680.h +++ b/src/components/i2c/drivers/drvBme680.h @@ -21,16 +21,13 @@ #define SEALEVELPRESSURE_HPA (1013.25) ///< Default sea level pressure, in hPa -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the BME680 temperature and humidity sensor. */ -/**************************************************************************/ class drvBme680 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an BME680 sensor. @param i2c @@ -42,26 +39,21 @@ class drvBme680 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBme680(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an BME680 sensor. */ - /*******************************************************************************/ ~drvBme680() { delete _bme; } - /*******************************************************************************/ /*! @brief Initializes the BME680 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _bme = new Adafruit_BME680(_i2c); @@ -87,15 +79,12 @@ class drvBme680 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a reading in blocking mode. @returns True if the reading succeeded, False otherwise. */ - /*******************************************************************************/ bool bmePerformReading() { return _bme->performReading(); } - /*******************************************************************************/ /*! @brief Gets the BME680's current temperature. @param tempEvent @@ -103,7 +92,6 @@ class drvBme680 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { if (!bmePerformReading()) return false; @@ -111,7 +99,6 @@ class drvBme680 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the BME680's current relative humidity reading. @param humidEvent @@ -119,7 +106,6 @@ class drvBme680 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { if (!bmePerformReading()) return false; @@ -127,7 +113,6 @@ class drvBme680 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -136,7 +121,6 @@ class drvBme680 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { if (!bmePerformReading()) return false; @@ -144,7 +128,6 @@ class drvBme680 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a the BME680's altitude sensor into an event. @param altitudeEvent @@ -152,7 +135,6 @@ class drvBme680 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAltitude(sensors_event_t *altitudeEvent) { if (!bmePerformReading()) return false; @@ -160,7 +142,6 @@ class drvBme680 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Base implementation - Reads a gas resistance sensor and converts the reading into the expected SI unit. @@ -169,7 +150,6 @@ class drvBme680 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ virtual bool getEventGasResistance(sensors_event_t *gasEvent) { if (!bmePerformReading()) return false; diff --git a/src/components/i2c/drivers/drvBmp280.h b/src/components/i2c/drivers/drvBmp280.h index ef06a846b..20af54ce3 100644 --- a/src/components/i2c/drivers/drvBmp280.h +++ b/src/components/i2c/drivers/drvBmp280.h @@ -21,16 +21,13 @@ #define SEALEVELPRESSURE_HPA (1013.25) ///< Default sea level pressure, in hPa -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the BMP280 temperature and pressure sensor. */ -/**************************************************************************/ class drvBmp280 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an BMP280 sensor. @param i2c @@ -42,26 +39,21 @@ class drvBmp280 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBmp280(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an BMP280 sensor. */ - /*******************************************************************************/ ~drvBmp280() { delete _bmp; } - /*******************************************************************************/ /*! @brief Initializes the BMP280 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _bmp = new Adafruit_BMP280(_i2c); // attempt to initialize BMP280 @@ -86,7 +78,6 @@ class drvBmp280 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the BMP280's current temperature. @param tempEvent @@ -94,12 +85,10 @@ class drvBmp280 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _bmp_temp->getEvent(tempEvent); } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -108,12 +97,10 @@ class drvBmp280 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { return _bmp_pressure->getEvent(pressureEvent); } - /*******************************************************************************/ /*! @brief Reads a the BMP280's altitude sensor into an event. @param altitudeEvent @@ -121,7 +108,6 @@ class drvBmp280 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAltitude(sensors_event_t *altitudeEvent) { altitudeEvent->altitude = _bmp->readAltitude(SEALEVELPRESSURE_HPA); return true; diff --git a/src/components/i2c/drivers/drvBmp3xx.h b/src/components/i2c/drivers/drvBmp3xx.h index c602dde4c..bcecf080d 100644 --- a/src/components/i2c/drivers/drvBmp3xx.h +++ b/src/components/i2c/drivers/drvBmp3xx.h @@ -21,15 +21,12 @@ #define SEALEVELPRESSURE_HPA (1013.25) ///< Default sea level pressure, in hPa -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the BMP3XX temperature and pressure sensor. */ -/**************************************************************************/ class drvBmp3xx : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an BMP3XX sensor. @param i2c @@ -41,26 +38,21 @@ class drvBmp3xx : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvBmp3xx(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an BMP3XX sensor. */ - /*******************************************************************************/ ~drvBmp3xx() { delete _bmp3xx; } - /*******************************************************************************/ /*! @brief Initializes the BMP3XX sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _bmp3xx = new Adafruit_BMP3XX(); // attempt to initialize BMP3XX @@ -80,7 +72,6 @@ class drvBmp3xx : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the BMP3XX's current temperature. @param tempEvent @@ -88,7 +79,6 @@ class drvBmp3xx : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { if (!_bmp3xx->performReading()) return false; @@ -96,7 +86,6 @@ class drvBmp3xx : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -105,7 +94,6 @@ class drvBmp3xx : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { if (!_bmp3xx->performReading()) return false; @@ -113,7 +101,6 @@ class drvBmp3xx : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a the BMP3XX's altitude sensor into an event. @param altitudeEvent @@ -121,7 +108,6 @@ class drvBmp3xx : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAltitude(sensors_event_t *altitudeEvent) { if (!_bmp3xx->performReading()) return false; diff --git a/src/components/i2c/drivers/drvDps310.h b/src/components/i2c/drivers/drvDps310.h index 76499a010..92c701bb9 100644 --- a/src/components/i2c/drivers/drvDps310.h +++ b/src/components/i2c/drivers/drvDps310.h @@ -18,16 +18,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the DPS310 barometric pressure sensor. */ -/**************************************************************************/ class drvDps310 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a DPS310 sensor. @param i2c @@ -39,7 +36,6 @@ class drvDps310 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvDps310(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { @@ -51,19 +47,15 @@ class drvDps310 : public drvBase { _last_read = 0; } - /*******************************************************************************/ /*! @brief Destructor for an DPS310 sensor. */ - /*******************************************************************************/ ~drvDps310() { delete _dps310; } - /*******************************************************************************/ /*! @brief Initializes the DPS310 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { // initialize DPS310 _dps310 = new Adafruit_DPS310(); @@ -85,24 +77,20 @@ class drvDps310 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the DPS310's temperature and pressure. @returns True if the measurements were read successfully, False otherwise. */ - /*******************************************************************************/ bool alreadyRecentlyRead() { return (_last_read != 0 && (millis() - _last_read < 1000)); } - /*******************************************************************************/ /*! @brief Reads the DPS310's temperature and pressure. @returns True if the measurements were read successfully, False otherwise. */ - /*******************************************************************************/ bool ReadMeasurements() { if (alreadyRecentlyRead()) return true; @@ -117,7 +105,6 @@ class drvDps310 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the DPS310's current temperature. @param tempEvent @@ -125,7 +112,6 @@ class drvDps310 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { if (!ReadMeasurements()) { return false; @@ -134,7 +120,6 @@ class drvDps310 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the DPS310's pressure reading. @param pressureEvent @@ -142,7 +127,6 @@ class drvDps310 : public drvBase { @returns True if the pressure was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { if (!ReadMeasurements()) { return false; diff --git a/src/components/i2c/drivers/drvEns160.h b/src/components/i2c/drivers/drvEns160.h index 83bbdf663..fcc325d09 100644 --- a/src/components/i2c/drivers/drvEns160.h +++ b/src/components/i2c/drivers/drvEns160.h @@ -21,16 +21,13 @@ #define SEALEVELPRESSURE_HPA (1013.25) ///< Default sea level pressure, in hPa -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the ENS160 temperature and humidity sensor. */ -/**************************************************************************/ class drvEns160 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an ENS160 sensor. @param i2c @@ -42,26 +39,21 @@ class drvEns160 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvEns160(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an ENS160 sensor. */ - /*******************************************************************************/ ~drvEns160() { delete _ens160; } - /*******************************************************************************/ /*! @brief Initializes the ENS160 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _ens160 = new ScioSense_ENS160((TwoWire *)_i2c, (uint8_t)_address); @@ -75,17 +67,14 @@ class drvEns160 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a reading in blocking mode. @returns True if the reading succeeded, False otherwise. */ - /*******************************************************************************/ bool ensPerformReading() { return _ens160->available() && _ens160->measure(true); } - /*******************************************************************************/ /*! @brief Reads the ENS160's eCO2 sensor into an event. @param eco2Event @@ -93,7 +82,6 @@ class drvEns160 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventECO2(sensors_event_t *eco2Event) { if (!ensPerformReading()) return false; @@ -101,7 +89,6 @@ class drvEns160 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the ENS160's TVOC sensor into an event. @param tvocEvent @@ -109,7 +96,6 @@ class drvEns160 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventTVOC(sensors_event_t *tvocEvent) { if (!ensPerformReading()) return false; @@ -117,7 +103,6 @@ class drvEns160 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the ENS160's AQI value into an event. @param rawEvent @@ -125,7 +110,6 @@ class drvEns160 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *rawEvent) { if (!ensPerformReading()) return false; diff --git a/src/components/i2c/drivers/drvHts221.h b/src/components/i2c/drivers/drvHts221.h index 84cff35f5..e527bf67c 100644 --- a/src/components/i2c/drivers/drvHts221.h +++ b/src/components/i2c/drivers/drvHts221.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the HTS221 humidity and temperature sensor. This implementation uses the 1 Hz data rate. */ -/**************************************************************************/ class drvHts221 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an HTS221 sensor. @param i2c @@ -40,27 +37,22 @@ class drvHts221 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvHts221(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an HTS221 sensor. */ - /*******************************************************************************/ ~drvHts221() { delete _hts221; } - /*******************************************************************************/ /*! @brief Initializes the HTS221 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { // attempt to initialize the HTS221 using the I2C interface _hts221 = new Adafruit_HTS221(); @@ -82,7 +74,6 @@ class drvHts221 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the HTS221's current temperature. @param tempEvent @@ -90,12 +81,10 @@ class drvHts221 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _hts221_temp->getEvent(tempEvent); } - /*******************************************************************************/ /*! @brief Gets the HTS221's current humidity. @param humidEvent @@ -103,7 +92,6 @@ class drvHts221 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { return _hts221_humidity->getEvent(humidEvent); } diff --git a/src/components/i2c/drivers/drvHtu21d.h b/src/components/i2c/drivers/drvHtu21d.h index 37a7869ff..e1dc0f0d5 100644 --- a/src/components/i2c/drivers/drvHtu21d.h +++ b/src/components/i2c/drivers/drvHtu21d.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the HTU21D humidity and temperature sensor. */ -/**************************************************************************/ class drvHtu21d : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an HTU21D sensor. @param i2c @@ -40,34 +37,28 @@ class drvHtu21d : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvHtu21d(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an HTU21D sensor. */ - /*******************************************************************************/ ~drvHtu21d() { delete _htu21d; } - /*******************************************************************************/ /*! @brief Initializes the HTU21D sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { // attempt to initialize the HTU21D using the I2C interface _htu21d = new Adafruit_HTU21DF(); return _htu21d->begin(_i2c); } - /*******************************************************************************/ /*! @brief Gets the HTU21D's current temperature. @param tempEvent @@ -75,13 +66,11 @@ class drvHtu21d : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _htu21d->readTemperature(); return true; } - /*******************************************************************************/ /*! @brief Gets the HTU21D's current humidity. @param humidEvent @@ -89,7 +78,6 @@ class drvHtu21d : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { humidEvent->relative_humidity = _htu21d->readHumidity(); return true; diff --git a/src/components/i2c/drivers/drvIna219.h b/src/components/i2c/drivers/drvIna219.h index a5b866147..5fd7851ba 100644 --- a/src/components/i2c/drivers/drvIna219.h +++ b/src/components/i2c/drivers/drvIna219.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a INA219 sensor. */ -/**************************************************************************/ class drvIna219 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a INA219 sensor. @param i2c @@ -37,26 +34,21 @@ class drvIna219 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvIna219(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an INA219 sensor. */ - /*******************************************************************************/ ~drvIna219() { delete _ina219; } - /*******************************************************************************/ /*! @brief Initializes the INA219 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _ina219 = new Adafruit_INA219(_address); if (!_ina219->begin(_i2c)) @@ -67,7 +59,6 @@ class drvIna219 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a voltage sensor and converts the reading into the expected SI unit. @@ -76,7 +67,6 @@ class drvIna219 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventVoltage(sensors_event_t *voltageEvent) { float shuntvoltage_mV = _ina219->getShuntVoltage_mV(); float busvoltage_V = _ina219->getBusVoltage_V(); diff --git a/src/components/i2c/drivers/drvLc709203f.h b/src/components/i2c/drivers/drvLc709203f.h index 1cd432379..dbcfdd88b 100644 --- a/src/components/i2c/drivers/drvLc709203f.h +++ b/src/components/i2c/drivers/drvLc709203f.h @@ -19,14 +19,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a LC709203F sensor. */ -/**************************************************************************/ class drvLc709203f : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a LC709203F sensor. @param i2c @@ -38,26 +35,21 @@ class drvLc709203f : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLc709203f(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LC709203F sensor. */ - /*******************************************************************************/ ~drvLc709203f() { delete _lc; } - /*******************************************************************************/ /*! @brief Initializes the LC709203F sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _lc = new Adafruit_LC709203F(); if (!_lc->begin(_i2c)) @@ -76,7 +68,6 @@ class drvLc709203f : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads a voltage sensor and converts the reading into the expected SI unit. @@ -85,13 +76,11 @@ class drvLc709203f : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventVoltage(sensors_event_t *voltageEvent) { voltageEvent->voltage = _lc->cellVoltage(); return true; } - /*******************************************************************************/ /*! @brief Reads a sensor's unitless % reading and converts the reading into the expected SI unit. @@ -100,7 +89,6 @@ class drvLc709203f : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventUnitlessPercent(sensors_event_t *unitlessPercentEvent) { unitlessPercentEvent->unitless_percent = _lc->cellPercent(); return true; diff --git a/src/components/i2c/drivers/drvLps22hb.h b/src/components/i2c/drivers/drvLps22hb.h index 2ade99ef1..5fe8f6463 100644 --- a/src/components/i2c/drivers/drvLps22hb.h +++ b/src/components/i2c/drivers/drvLps22hb.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the LPS22HB temperature and pressure sensor. */ -/**************************************************************************/ class drvLps22hb : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an LPS22HB sensor. @param i2c @@ -40,26 +37,21 @@ class drvLps22hb : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLps22hb(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LPS22HB sensor. */ - /*******************************************************************************/ ~drvLps22hb() { delete _lps22; } - /*******************************************************************************/ /*! @brief Initializes the LPS22HB sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _lps22 = new Adafruit_LPS22(); // attempt to initialize LPS22HB @@ -77,7 +69,6 @@ class drvLps22hb : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the LPS22HB's current temperature. @param tempEvent @@ -85,12 +76,10 @@ class drvLps22hb : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _temp->getEvent(tempEvent); } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -99,7 +88,6 @@ class drvLps22hb : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { return _pressure->getEvent(pressureEvent); } diff --git a/src/components/i2c/drivers/drvLps25hb.h b/src/components/i2c/drivers/drvLps25hb.h index 5df77131a..c947c80a9 100644 --- a/src/components/i2c/drivers/drvLps25hb.h +++ b/src/components/i2c/drivers/drvLps25hb.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the LPS25HB temperature and pressure sensor. */ -/**************************************************************************/ class drvLps25hb : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an LPS25HB sensor. @param i2c @@ -40,26 +37,21 @@ class drvLps25hb : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLps25hb(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LPS25HB sensor. */ - /*******************************************************************************/ ~drvLps25hb() { delete _lps25; } - /*******************************************************************************/ /*! @brief Initializes the LPS25HB sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _lps25 = new Adafruit_LPS25(); // attempt to initialize LPS25HB @@ -77,7 +69,6 @@ class drvLps25hb : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the LPS25HB's current temperature. @param tempEvent @@ -85,12 +76,10 @@ class drvLps25hb : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _temp->getEvent(tempEvent); } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -99,7 +88,6 @@ class drvLps25hb : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { return _pressure->getEvent(pressureEvent); } diff --git a/src/components/i2c/drivers/drvLps3xhw.h b/src/components/i2c/drivers/drvLps3xhw.h index 282529f34..b36e620d3 100644 --- a/src/components/i2c/drivers/drvLps3xhw.h +++ b/src/components/i2c/drivers/drvLps3xhw.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the LPS3XHW temperature and pressure sensor. */ -/**************************************************************************/ class drvLps3xhw : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an LPS3XHW sensor. @param i2c @@ -40,26 +37,21 @@ class drvLps3xhw : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLps3xhw(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LPS3XHW sensor. */ - /*******************************************************************************/ ~drvLps3xhw() { delete _lps3xhw; } - /*******************************************************************************/ /*! @brief Initializes the LPS3XHW sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _lps3xhw = new Adafruit_LPS35HW(); // attempt to initialize LPS3XHW @@ -73,7 +65,6 @@ class drvLps3xhw : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the LPS3XHW's current temperature. @param tempEvent @@ -81,14 +72,12 @@ class drvLps3xhw : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { _lps3xhw->takeMeasurement(); tempEvent->temperature = _lps3xhw->readTemperature(); return true; } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -97,7 +86,6 @@ class drvLps3xhw : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { _lps3xhw->takeMeasurement(); pressureEvent->pressure = _lps3xhw->readPressure(); diff --git a/src/components/i2c/drivers/drvLtr329_Ltr303.h b/src/components/i2c/drivers/drvLtr329_Ltr303.h index 4e4d91f5f..0469f9fc4 100644 --- a/src/components/i2c/drivers/drvLtr329_Ltr303.h +++ b/src/components/i2c/drivers/drvLtr329_Ltr303.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a LTR329/303 sensor. */ -/**************************************************************************/ class drvLtr329_Ltr303 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a LTR329/303 sensor. @param i2c @@ -37,26 +34,21 @@ class drvLtr329_Ltr303 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLtr329_Ltr303(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LTR329/303 sensor. */ - /*******************************************************************************/ ~drvLtr329_Ltr303() { delete _LTR329; } - /*******************************************************************************/ /*! @brief Initializes the LTR329/303 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _LTR329 = new Adafruit_LTR329(); // Attempt to initialize LTR329 @@ -72,7 +64,6 @@ class drvLtr329_Ltr303 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the LTR329's ambient light level ([Visible+IR] - IR-only) @param lightEvent @@ -80,7 +71,6 @@ class drvLtr329_Ltr303 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { if (!_LTR329->newDataAvailable()) return false; @@ -91,7 +81,6 @@ class drvLtr329_Ltr303 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the LTR329's infrared value into an event. @param rawEvent @@ -99,7 +88,6 @@ class drvLtr329_Ltr303 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *rawEvent) { if (!_LTR329->newDataAvailable()) { diff --git a/src/components/i2c/drivers/drvLtr390.h b/src/components/i2c/drivers/drvLtr390.h index f725126a2..343cd0833 100644 --- a/src/components/i2c/drivers/drvLtr390.h +++ b/src/components/i2c/drivers/drvLtr390.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a LTR390 sensor. */ -/**************************************************************************/ class drvLtr390 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a LTR390 sensor. @param i2c @@ -37,26 +34,21 @@ class drvLtr390 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvLtr390(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an LTR390 sensor. */ - /*******************************************************************************/ ~drvLtr390() { delete _ltr390; } - /*******************************************************************************/ /*! @brief Initializes the LTR390 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _ltr390 = new Adafruit_LTR390(); // Attempt to initialize LTR390 @@ -72,7 +64,6 @@ class drvLtr390 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the Adafruit Unified Sensor API. @@ -81,7 +72,6 @@ class drvLtr390 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { if (_ltr390->getMode() != LTR390_MODE_ALS) { _ltr390->setMode(LTR390_MODE_ALS); @@ -95,7 +85,6 @@ class drvLtr390 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the LTR390's UV value into an event. @param rawEvent @@ -103,7 +92,6 @@ class drvLtr390 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *rawEvent) { if (_ltr390->getMode() != LTR390_MODE_UVS) { _ltr390->setMode(LTR390_MODE_UVS); diff --git a/src/components/i2c/drivers/drvMCP9808.h b/src/components/i2c/drivers/drvMCP9808.h index df9ce1159..3d055765d 100644 --- a/src/components/i2c/drivers/drvMCP9808.h +++ b/src/components/i2c/drivers/drvMCP9808.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a MCP9808 sensor. */ -/**************************************************************************/ class drvMcp9808 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a MCP9808 sensor. @param i2c @@ -37,35 +34,29 @@ class drvMcp9808 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvMcp9808(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an MCP9808 sensor. */ - /*******************************************************************************/ ~drvMcp9808() { // Called when a MCP9808 component is deleted. delete _mcp9808; } - /*******************************************************************************/ /*! @brief Initializes the MCP9808 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _mcp9808 = new Adafruit_MCP9808(); return _mcp9808->begin((uint8_t)_address, _i2c); } - /*******************************************************************************/ /*! @brief Gets the MCP9808's current temperature. @param tempEvent @@ -73,7 +64,6 @@ class drvMcp9808 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _mcp9808->readTempC(); return true; diff --git a/src/components/i2c/drivers/drvMax1704x.h b/src/components/i2c/drivers/drvMax1704x.h index 70c163496..56d8b485e 100644 --- a/src/components/i2c/drivers/drvMax1704x.h +++ b/src/components/i2c/drivers/drvMax1704x.h @@ -19,14 +19,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a MAX17048 sensor. */ -/**************************************************************************/ class drvMax1704x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a MAX17048 sensor. @param i2c @@ -38,32 +35,26 @@ class drvMax1704x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvMax1704x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an MAX17048 sensor. */ - /*******************************************************************************/ ~drvMax1704x() { delete _maxlipo; } - /*******************************************************************************/ /*! @brief Initializes the MAX17048 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _maxlipo = new Adafruit_MAX17048(); return _maxlipo->begin(_i2c); } - /*******************************************************************************/ /*! @brief Reads a voltage sensor and converts the reading into the expected SI unit. @@ -72,13 +63,11 @@ class drvMax1704x : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventVoltage(sensors_event_t *voltageEvent) { voltageEvent->voltage = _maxlipo->cellVoltage(); return true; } - /*******************************************************************************/ /*! @brief Reads a sensor's unitless % reading and converts the reading into the expected SI unit. @@ -87,7 +76,6 @@ class drvMax1704x : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventUnitlessPercent(sensors_event_t *unitlessPercentEvent) { unitlessPercentEvent->unitless_percent = _maxlipo->cellPercent(); return true; diff --git a/src/components/i2c/drivers/drvMpl115a2.h b/src/components/i2c/drivers/drvMpl115a2.h index 2c93e741b..d4d950088 100644 --- a/src/components/i2c/drivers/drvMpl115a2.h +++ b/src/components/i2c/drivers/drvMpl115a2.h @@ -19,16 +19,13 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the MPL115A2 temperature and pressure sensor. */ -/**************************************************************************/ class drvMpl115a2 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an MPL115A2 sensor. @param i2c @@ -40,32 +37,26 @@ class drvMpl115a2 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvMpl115a2(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an MPL115A2 sensor. */ - /*******************************************************************************/ ~drvMpl115a2() { delete _mpl115a2; } - /*******************************************************************************/ /*! @brief Initializes the MPL115A2 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _mpl115a2 = new Adafruit_MPL115A2(); return _mpl115a2->begin(_address, _i2c); } - /*******************************************************************************/ /*! @brief Gets the MPL115A2's current temperature. @param tempEvent @@ -73,13 +64,11 @@ class drvMpl115a2 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _mpl115a2->getTemperature(); return true; } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit (hPa). @@ -88,7 +77,6 @@ class drvMpl115a2 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { pressureEvent->pressure = _mpl115a2->getPressure() * 10; return true; diff --git a/src/components/i2c/drivers/drvMprls.h b/src/components/i2c/drivers/drvMprls.h index b5f642847..db137a991 100644 --- a/src/components/i2c/drivers/drvMprls.h +++ b/src/components/i2c/drivers/drvMprls.h @@ -19,15 +19,12 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the MPRLS sensor. */ -/**************************************************************************/ class drvMprls : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an MPRLS sensor. @param i2c @@ -39,33 +36,27 @@ class drvMprls : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvMprls(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an MPRLS sensor. */ - /*******************************************************************************/ ~drvMprls() { delete _mprls; } - /*******************************************************************************/ /*! @brief Initializes the MPRLS sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _mprls = new Adafruit_MPRLS(); // attempt to initialize MPRLS return _mprls->begin(_address, _i2c); } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -74,7 +65,6 @@ class drvMprls : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { pressureEvent->pressure = _mprls->readPressure(); return pressureEvent->pressure != NAN; diff --git a/src/components/i2c/drivers/drvMs8607.h b/src/components/i2c/drivers/drvMs8607.h index 00cdf57b6..b0ad98eef 100644 --- a/src/components/i2c/drivers/drvMs8607.h +++ b/src/components/i2c/drivers/drvMs8607.h @@ -19,15 +19,12 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a sensor driver for the MS8607 PHT sensor. */ -/**************************************************************************/ class drvMs8607 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for an MS8607 sensor. @param i2c @@ -39,26 +36,21 @@ class drvMs8607 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvMs8607(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an MS8607 sensor. */ - /*******************************************************************************/ ~drvMs8607() { delete _ms8607; } - /*******************************************************************************/ /*! @brief Initializes the MS8607 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _ms8607 = new Adafruit_MS8607(); // attempt to initialize MS8607 @@ -78,7 +70,6 @@ class drvMs8607 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the MS8607's current temperature. @param tempEvent @@ -86,13 +77,11 @@ class drvMs8607 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { _ms8607_temp->getEvent(tempEvent); return true; } - /*******************************************************************************/ /*! @brief Gets the MS8607's current relative humidity reading. @param humidEvent @@ -100,13 +89,11 @@ class drvMs8607 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { _ms8607_humidity->getEvent(humidEvent); return true; } - /*******************************************************************************/ /*! @brief Reads a pressure sensor and converts the reading into the expected SI unit. @@ -115,7 +102,6 @@ class drvMs8607 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPressure(sensors_event_t *pressureEvent) { _ms8607_pressure->getEvent(pressureEvent); return true; diff --git a/src/components/i2c/drivers/drvOut7Seg.h b/src/components/i2c/drivers/drvOut7Seg.h new file mode 100644 index 000000000..d70621dab --- /dev/null +++ b/src/components/i2c/drivers/drvOut7Seg.h @@ -0,0 +1,209 @@ +/*! + * @file drvOut7Seg.h + * + * Device driver designed specifically to work with the Adafruit LED 7-Segment + * I2C backpacks: + * ----> http://www.adafruit.com/products/881 + * ----> http://www.adafruit.com/products/880 + * ----> http://www.adafruit.com/products/879 + * ----> http://www.adafruit.com/products/878 + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell for Adafruit Industries 2025 + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_OUT_7SEG_H +#define DRV_OUT_7SEG_H + +#include "drvOutputBase.h" +#include +#include + +#define LED_BACKPACK_ALIGNMENT_UNSPECIFIED 0 ///< Unspecified alignment +#define LED_BACKPACK_ALIGNMENT_LEFT 1 ///< Left alignment +#define LED_BACKPACK_ALIGNMENT_RIGHT 2 ///< Right alignment +#define LED_BACKPACK_ALIGNMENT_DEFAULT \ + LED_BACKPACK_ALIGNMENT_LEFT ///< Default alignment +#define LED_MAX_CHARS \ + 4 ///< Maximum number of characters to display on the 7-segment display + +/*! + @brief Class that provides a driver for an Adafruit 7-Segment LED matrix + w/I2C backpack. +*/ +class drvOut7Seg : public drvOutputBase { +public: + /*! + @brief Constructor for an Adafruit 7-Segment LED matrix w/I2C backpack. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + @param mux_channel + The I2C multiplexer channel. + @param driver_name + The name of the driver. + */ + drvOut7Seg(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, + const char *driver_name) + : drvOutputBase(i2c, sensorAddress, mux_channel, driver_name) { + // Initialization handled by drvBase constructor + } + + /*! + @brief Destructor for an Adafruit 7-Segment LED matrix w/I2C backpack. + */ + ~drvOut7Seg() { + if (_matrix) { + delete _matrix; + _matrix = nullptr; + } + } + + /*! + @brief Initializes the 7-segment LED matrix and begins I2C + communication. + @returns True if initialized successfully, False otherwise. + */ + bool begin() override { + _matrix = new Adafruit_7segment(); + bool did_begin = _matrix->begin(0x70, _i2c); + return did_begin; + } + + /*! + @brief Configures the LED matrix's I2C backpack. + @param brightness + The brightness of the i2c backpack (0-15). + @param alignment + The alignment of the i2c backpack's LED matrix (left/right). +*/ + void ConfigureI2CBackpack(int32_t brightness, uint32_t alignment) { + if (alignment == LED_BACKPACK_ALIGNMENT_RIGHT) { + _alignment = LED_BACKPACK_ALIGNMENT_RIGHT; + } else { + _alignment = LED_BACKPACK_ALIGNMENT_DEFAULT; + } + + // Note: Adafruit_LEDBackpack normalizes only > 15, but not < 0, + // so, here we're normalizing < 0 to 8 (median brightness) + if (brightness < 0) { + brightness = 8; // Set to median brightness if out of range + } + } + + /*! + @brief Sets the brightness of the LED backpack. + @param b + The brightness value, from 0 (off) to 15 (full brightness). + */ + void SetLedBackpackBrightness(uint8_t b) { + if (_matrix == nullptr) { + return; + } + _matrix->setBrightness(b); + } + + /*! + @brief Writes the first four characters of a message to the Adafruit + 7-segment LED matrix. + @param message + The message to be displayed. + */ + void WriteMessage(const char *message) { + if (_matrix == nullptr || message == nullptr) { + return; + } + + // Clear before writing + _matrix->clear(); + + // Calculate the number of characters to display + size_t len_display = min(strlen(message), (size_t)LED_MAX_CHARS); + + // Set the starting position based on alignment + int pos_start; + if (_alignment == LED_BACKPACK_ALIGNMENT_LEFT) { + pos_start = 0; // start at the leftmost position of the display + } else { + // Exclude decimal points from the character count because those get + // displayed on a "special" segment of the LED display + int seg_chars = 0; + for (size_t i = 0; i < len_display; i++) { + if (message[i] != '.') { + seg_chars++; + } + } + // start at the rightmost position of the display + pos_start = LED_MAX_CHARS - seg_chars; + } + + // Write to the display's buffer + int cur_idx = pos_start; + for (size_t i = 0; i < len_display; i++) { + // skip position 2 + if (cur_idx == 2) { + cur_idx++; + } + // Save the character because if there's a decimal, we need to skip it in + // the buffer + char ch = message[i]; + + // Look-ahead for a decimal point to attach to the current character + bool display_dot = false; + if (i + 1 < len_display && message[i + 1] == '.') { + display_dot = true; + i++; + len_display++; + } + + // Write the character to the display buffer + _matrix->writeDigitAscii(cur_idx, ch, display_dot); + cur_idx++; + } + // Issue the buffered data in RAM to the display + _matrix->writeDisplay(); + } + + /*! + @brief Writes a floating point value to the Adafruit 7-segment LED + matrix. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + void WriteValue(float value) { + char message[8 + 1]; + snprintf(message, sizeof(message), "%.5f", value); + WriteMessage(message); + } + + /*! + @brief Writes an integer value to the Adafruit 7-segment LED matrix. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + void WriteValue(int32_t value) { + char message[LED_MAX_CHARS + 1]; + snprintf(message, sizeof(message), "%ld", value); + WriteMessage(message); + } + +protected: + Adafruit_7segment *_matrix = + nullptr; ///< ptr to a 7-segment LED matrix object + int32_t _brightness; ///< Brightness of the LED backpack, from 0 (off) to 15 + ///< (full brightness) + uint32_t _alignment = + LED_BACKPACK_ALIGNMENT_DEFAULT; ///< Determines L/R alignment of the + ///< message displayed +}; + +#endif // DRV_OUT_7SEG_H \ No newline at end of file diff --git a/src/components/i2c/drivers/drvOutCharLcd.h b/src/components/i2c/drivers/drvOutCharLcd.h new file mode 100644 index 000000000..3da962409 --- /dev/null +++ b/src/components/i2c/drivers/drvOutCharLcd.h @@ -0,0 +1,149 @@ +/*! + * @file drvOutCharLcd.h + * + * Device driver for I2C Character LCDs (HD44780) + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell for Adafruit Industries 2025 + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_OUT_CHAR_LCD +#define DRV_OUT_CHAR_LCD + +#include "drvOutputBase.h" +#include +#include + +/*! + @brief Class that provides a driver interface for a lcd character display. + This class is a wrapper around the Adafruit_LiquidCrystal library. +*/ +class drvOutCharLcd : public drvOutputBase { +public: + /*! + @brief Constructor for a lcd character display. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + @param mux_channel + The I2C multiplexer channel. + @param driver_name + The name of the driver. + */ + drvOutCharLcd(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, + const char *driver_name) + : drvOutputBase(i2c, sensorAddress, mux_channel, driver_name) { + // Initialization handled by drvOutPutBase constructor + } + + /*! + @brief Destructor for a quad alphanumeric display. + */ + ~drvOutCharLcd() { + if (_lcd) { + delete _lcd; + _lcd = nullptr; + } + } + + /*! + @brief Initializes an I2C character LCD and begins I2C communication. + @returns True if initialized successfully, False otherwise. + */ + bool begin() override { + _lcd = new Adafruit_LiquidCrystal(_address, _i2c); + bool did_begin = _lcd->begin(_cols, _rows); + if (did_begin && _enable_backlight) { + _lcd->setBacklight(HIGH); + } + return did_begin; + } + + /*! + @brief Writes a message to the LCD. + @note MUST be called prior to begin() to configure the LCD's size + @param rows + The number of rows in the LCD. + @param cols + The number of columns in the LCD. + @param enable_backlight + True if the backlight is enabled, False otherwise. + */ + void ConfigureCharLcd(uint8_t rows, uint8_t cols, bool enable_backlight) { + _rows = rows; + _cols = cols; + _enable_backlight = enable_backlight; + } + + /*! + @brief Enables or disables the backlight on a character LCD. + @param enable_backlight + True to enable the backlight, False to disable it. + */ + void EnableBackLightCharLCD(bool enable_backlight) { + if (_lcd == nullptr) + return; + if (_enable_backlight) { + _lcd->setBacklight(HIGH); + } else { + _lcd->setBacklight(LOW); + } + _enable_backlight = enable_backlight; + } + + /*! + @brief Writes a message to the LCD. + @param message + The message to be displayed. + */ + void WriteMessage(const char *message) { + if (_lcd == nullptr) + return; + + // Before writing, let's clear the display + _lcd->clear(); + + size_t message_length = strlen(message); + size_t cur_idx = 0; // Current index in the message + + // Write each row until it hits: \n, or the end of the message, or the last + // column/row position + for (int cur_row = 0; cur_row < _rows && cur_idx < message_length; + cur_row++) { + // Write each row out at the beginning of the row + _lcd->setCursor(0, cur_row); + for (int cur_col = 0; cur_col < _cols && cur_idx < message_length; + cur_col++) { + char c = message[cur_idx]; + if (c == '\\' && cur_idx + 1 < message_length && + message[cur_idx + 1] == 'n') { + cur_idx += 2; // Skip the '\n' character in the buffer + break; // and move to the next row + } else if (c == 194 && cur_idx + 1 < message_length && + message[cur_idx + 1] == 176) { + cur_idx += 2; // Skip the degree symbol sequence in the buffer + _lcd->write(0xDF); // and write the degree symbol + } else { + _lcd->write(c); + cur_idx++; + } + } + } + } + +protected: + Adafruit_LiquidCrystal *_lcd = + nullptr; ///< Pointer to the Adafruit_LiquidCrystal object + uint8_t _rows; ///< Number of rows in the display + uint8_t _cols; ///< Number of columns in the display + bool _enable_backlight; ///< Flag to enable/disable backlight +}; + +#endif // DRV_OUT_CHAR_LCD \ No newline at end of file diff --git a/src/components/i2c/drivers/drvOutQuadAlphaNum.h b/src/components/i2c/drivers/drvOutQuadAlphaNum.h new file mode 100644 index 000000000..18cced030 --- /dev/null +++ b/src/components/i2c/drivers/drvOutQuadAlphaNum.h @@ -0,0 +1,201 @@ +/*! + * @file drvOutQuadAlphaNum.h + * + * Device driver for Quad Alphanumeric Displays w/I2C Backpack + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell for Adafruit Industries 2025 + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_OUT_QUAD_ALPHANUM_H +#define DRV_OUT_QUAD_ALPHANUM_H + +#include "drvOutputBase.h" +#include +#include + +#define LED_BACKPACK_ALIGNMENT_UNSPECIFIED 0 ///< Unspecified alignment +#define LED_BACKPACK_ALIGNMENT_LEFT 1 ///< Left alignment +#define LED_BACKPACK_ALIGNMENT_RIGHT 2 ///< Right alignment +#define LED_BACKPACK_ALIGNMENT_DEFAULT \ + LED_BACKPACK_ALIGNMENT_LEFT ///< Default alignment +#define LED_MAX_CHARS \ + 4 ///< Maximum number of characters to display on the alphanumeric display + +/*! + @brief Class that provides a driver interface for Quad Alphanumeric + Displays w/I2C Backpack +*/ +class drvOutQuadAlphaNum : public drvOutputBase { +public: + /*! + @brief Constructor for a quad alphanumeric display.. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + @param mux_channel + The I2C multiplexer channel. + @param driver_name + The name of the driver. + */ + drvOutQuadAlphaNum(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, + const char *driver_name) + : drvOutputBase(i2c, sensorAddress, mux_channel, driver_name) { + // Initialization handled by drvBase constructor + } + + /*! + @brief Destructor for a quad alphanumeric display. + */ + ~drvOutQuadAlphaNum() { + if (_alpha4) { + delete _alpha4; + _alpha4 = nullptr; + } + } + + /*! + @brief Initializes the drvOutQuadAlphaNum sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + bool begin() override { + _alpha4 = new Adafruit_AlphaNum4(); + bool did_begin = _alpha4->begin(_address, _i2c); + _alpha4->setBrightness(_brightness); + return did_begin; + } + + /*! + @brief Configures a LED backpack. + @param brightness + The brightness of the LED backpack. + @param alignment + The alignment of the LED backpack. +*/ + void ConfigureI2CBackpack(int32_t brightness, uint32_t alignment) { + WS_DEBUG_PRINTLN("[i2c] drvOutQuadAlphaNum::ConfigureI2CBackpack() called"); + if (alignment == LED_BACKPACK_ALIGNMENT_RIGHT) { + _alignment = LED_BACKPACK_ALIGNMENT_RIGHT; + } else { + _alignment = LED_BACKPACK_ALIGNMENT_DEFAULT; + } + _brightness = brightness; + WS_DEBUG_PRINTLN("[i2c] drvOutQuadAlphaNum::configured"); + } + + /*! + @brief Sets the brightness of the LED backpack. + @param b + The brightness value, from 0 (off) to 15 (full brightness). + */ + void SetLedBackpackBrightness(uint8_t b) { + if (_alpha4 == nullptr) { + return; + } + _alpha4->setBrightness(b); + } + + /*! + @brief Writes a message to the quad alphanumeric display. + @param message + The message to be displayed. + */ + void WriteMessage(const char *message) { + if (_alpha4 == nullptr || message == nullptr) { + return; + } + // Clear before writing + _alpha4->clear(); + + // Calculate the total message length + size_t msg_len = strlen(message); + + // Count all characters but excluding decimal points as they are part of the + // "segment" + int char_count = 0; + for (size_t i = 0; i < msg_len && char_count < LED_MAX_CHARS; i++) { + if (message[i] != '.') { + char_count++; + } + } + + // Set the starting position based on alignment + int pos_start; + if (_alignment == LED_BACKPACK_ALIGNMENT_LEFT) { + pos_start = 0; // start at the leftmost position + } else { + pos_start = LED_MAX_CHARS - char_count; // start at the rightmost position + } + + // Write to the display's buffer + int cur_idx = pos_start; + for (size_t i = 0; i < msg_len && cur_idx < LED_MAX_CHARS; i++) { + char ch = message[i]; + + if (ch == '.') { + // If the previous character has already been processed (or we're at the + // start of the message), we need to show the decimal as a character + if (i == 0 || message[i - 1] == '.') { + _alpha4->writeDigitAscii(cur_idx, ch, false); + cur_idx++; + } else { + // Set decimal on an already written character + _alpha4->writeDigitAscii(cur_idx - 1, message[i - 1], true); + } + continue; + } + + // Perform a look-ahead for the decimal + bool display_dot = (i + 1 < msg_len && message[i + 1] == '.'); + + // Write the character to the display buffer + _alpha4->writeDigitAscii(cur_idx, ch, display_dot); + cur_idx++; + } + + // Issue the buffered data in RAM to the display + _alpha4->writeDisplay(); + } + + /*! + @brief Writes a floating point value to the quad alphanumeric display. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + void WriteValue(float value) { + char message[8 + 1]; + snprintf(message, sizeof(message), "%.3f", value); + WriteMessage(message); + } + + /*! + @brief Writes an integer value to the quad alphanumeric display. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + void WriteValue(int32_t value) { + char message[LED_MAX_CHARS + 1]; + snprintf(message, sizeof(message), "%ld", value); + WriteMessage(message); + } + +protected: + Adafruit_AlphaNum4 *_alpha4 = + nullptr; ///< ptr to a 4-digit alphanumeric display object + int32_t _brightness; ///< Brightness of the LED backpack, from 0 (off) to 15 + ///< (full brightness) + uint32_t _alignment = + LED_BACKPACK_ALIGNMENT_DEFAULT; ///< Determines L/R alignment of the + ///< message displayed +}; + +#endif // DRV_OUT_QUAD_ALPHANUM_H \ No newline at end of file diff --git a/src/components/i2c/drivers/drvOutSsd1306.h b/src/components/i2c/drivers/drvOutSsd1306.h new file mode 100644 index 000000000..f02a5c867 --- /dev/null +++ b/src/components/i2c/drivers/drvOutSsd1306.h @@ -0,0 +1,138 @@ +/*! + * @file drvOutSsd1306.h + * + * Device driver for OLED displays with a SSD1306 driver + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell for Adafruit Industries 2025 + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_OUT_SSD1306_H +#define DRV_OUT_SSD1306_H + +#include "drvOutputBase.h" +#include +#include + +/*! + @brief Class that provides a driver interface for a SSD1306 OLED display. + This class is a wrapper around the Adafruit_SSD1306 library. +*/ +class drvOutSsd1306 : public drvOutputBase { +public: + /*! + @brief Constructor for a lcd character display. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + @param mux_channel + The I2C multiplexer channel. + @param driver_name + The name of the driver. + */ + drvOutSsd1306(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, + const char *driver_name) + : drvOutputBase(i2c, sensorAddress, mux_channel, driver_name) { + // Initialization handled by drvOutPutBase constructor + } + + /*! + @brief Destructor for a quad alphanumeric display. + */ + ~drvOutSsd1306() { + if (_display != nullptr) { + _display->ssd1306_command(SSD1306_DISPLAYOFF); + delete _display; + _display = nullptr; + } + } + + /*! + @brief Initializes the SSD1306 display and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + bool begin() { + // Attempt to create and allocate a SSD1306 obj. + _display = new Adafruit_SSD1306(_width, _height, _i2c); + if (!_display->begin(SSD1306_SWITCHCAPVCC, _address)) + return false; + // Clear the buffer + _display->clearDisplay(); + // Configure the text size and color + _display->setTextSize(_text_sz); + _display->setTextColor(SSD1306_WHITE); + // Reset the cursor position + _display->setCursor(0, 0); + _display->display(); + return true; + } + + /*! + @brief Configures a SSD1306 OLED display. Must be called before driver + begin() + @param width + The width of the display in pixels. + @param height + The height of the display in pixels. + @param text_size + The magnification factor for the text size. + */ + void ConfigureSSD1306(uint8_t width, uint8_t height, uint8_t text_size) { + _width = width; + _height = height; + _text_sz = text_size; + } + + /*! + @brief Writes a message to the SSD1306 display. + @param message + The message to be displayed. + */ + void WriteMessageSSD1306(const char *message) { + if (_display == nullptr) + return; + + // Start with a fresh display buffer + // and settings + int16_t y_idx = 0; + _display->clearDisplay(); + _display->setTextSize(_text_sz); + _display->setTextColor(SSD1306_WHITE); + _display->setCursor(0, y_idx); + _display->display(); + + // Calculate the line height based on the text size (NOTE: base height is + // 8px) + int16_t line_height = 8 * _text_sz; + uint16_t c_idx = 0; + size_t msg_size = strlen(message); + for (size_t i = 0; i < msg_size && c_idx < msg_size; i++) { + if (message[i] == '\\' && i + 1 < msg_size && message[i + 1] == 'n') { + // detected a newline char sequence (\n) + i++; + // Skip to the next possible line + y_idx += line_height; + _display->setCursor(0, y_idx); + } else { + _display->print(message[i]); + _display->display(); + } + } + } + +protected: + Adafruit_SSD1306 *_display = + nullptr; ///< Pointer to the Adafruit_SSD1306 object + uint8_t _width; ///< Width of the display in pixels + uint8_t _height; ///< Height of the display in pixels + uint8_t _text_sz; ///< Text size of the display +}; + +#endif // DRV_OUT_SSD1306_H \ No newline at end of file diff --git a/src/components/i2c/drivers/drvOutputBase.h b/src/components/i2c/drivers/drvOutputBase.h new file mode 100644 index 000000000..ad75e12e3 --- /dev/null +++ b/src/components/i2c/drivers/drvOutputBase.h @@ -0,0 +1,160 @@ +/*! + * @file drvOutputBase.h + * + * Base implementation for I2C output device drivers. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_OUTPUT_BASE_H +#define DRV_OUTPUT_BASE_H +#include "drvBase.h" +#include +#include + +/*! + @brief Base class for I2C Output Drivers. +*/ +class drvOutputBase : public drvBase { + +public: + /*! + @brief Instantiates an I2C output device. + @param i2c + The I2C hardware interface, default is Wire. + @param address + The I2C device's unique address. + @param mux_channel + An optional channel number used to address a device on a I2C + MUX. + @param driver_name + The name of the driver. + */ + drvOutputBase(TwoWire *i2c, uint16_t address, uint32_t mux_channel, + const char *driver_name) + : drvBase(i2c, address, mux_channel, driver_name) { + // TODO + } + + /*! + @brief Destructor for an I2C output device. + */ + virtual ~drvOutputBase() {} + + /*! + @brief Initializes the I2C output device and begins I2C. + @param message + The message to be displayed. + */ + virtual void WriteMessage(const char *message) { + // noop + } + + /*! + @brief Writes a floating point value to the quad alphanumeric display. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + virtual void WriteValue(float value) { + // noop + } + + /*! + @brief Writes a floating point value to the quad alphanumeric display. + @param value + The value to be displayed. Only the first four digits are + displayed. + */ + virtual void WriteValue(int32_t value) { + // noop + } + + /*! + @brief Configures a LED backpack. + @param brightness + The brightness of the LED backpack. + @param alignment + The alignment of the LED backpack. + */ + virtual void ConfigureI2CBackpack(int32_t brightness, uint32_t alignment) { + // noop + } + + /*! + @brief Configures a character LCD. + @param rows + The number of rows in the LCD. + @param cols + The number of columns in the LCD. + @param enable_backlight + True if the backlight is enabled, False otherwise. + */ + virtual void ConfigureCharLcd(uint32_t rows, uint32_t cols, + bool enable_backlight) { + // noop + } + + /*! + @brief Enables or disables the backlight on a character LCD. + @param enable_backlight + True to enable the backlight, False to disable it. + */ + void EnableBackLightCharLCD(bool enable_backlight) { + // noop + } + + /*! + @brief Writes a message to the LCD. + @param write_char_lcd + Pointer to a wippersnapper_i2c_output_CharLCDWrite message. + @returns True if the message was written successfully, False otherwise. + */ + bool + WriteMessageCharLCD(wippersnapper_i2c_output_CharLCDWrite *write_char_lcd) { + EnableBackLightCharLCD(write_char_lcd->enable_backlight); + WriteMessage(write_char_lcd->message); + return true; + } + + /*! + @brief Sets the brightness of the LED backpack. + @param b + The brightness value, from 0 (off) to 15 (full brightness). + */ + virtual void SetLedBackpackBrightness(uint8_t b) { + // noop + } + + /*! + @brief Configures a SSD1306 OLED display. Must be called before driver + begin() + @param width + The width of the display in pixels. + @param height + The height of the display in pixels. + @param text_size + The magnification factor for the text size. + */ + virtual void ConfigureSSD1306(uint8_t width, uint8_t height, + uint8_t text_size) { + // noop + } + + /*! + @brief Writes a message to the SSD1306 display. + @param message + The message to be displayed. + */ + virtual void WriteMessageSSD1306(const char *message) { + // noop + } +}; +#endif // DRV_OUTPUT_BASE_H \ No newline at end of file diff --git a/src/components/i2c/drivers/drvPct2075.h b/src/components/i2c/drivers/drvPct2075.h index e8a7b9187..be2630b20 100644 --- a/src/components/i2c/drivers/drvPct2075.h +++ b/src/components/i2c/drivers/drvPct2075.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a PCT2075 sensor. */ -/**************************************************************************/ class drvPct2075 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a PCT2075 sensor. @param i2c @@ -37,35 +34,29 @@ class drvPct2075 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvPct2075(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an PCT2075 sensor. */ - /*******************************************************************************/ ~drvPct2075() { // Called when a PCT2075 component is deleted. delete _pct2075; } - /*******************************************************************************/ /*! @brief Initializes the PCT2075 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _pct2075 = new Adafruit_PCT2075(); return _pct2075->begin((uint8_t)_address, _i2c); } - /*******************************************************************************/ /*! @brief Gets the PCT2075's current temperature. @param tempEvent @@ -73,7 +64,6 @@ class drvPct2075 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _pct2075->getTemperature(); return true; diff --git a/src/components/i2c/drivers/drvPm25.h b/src/components/i2c/drivers/drvPm25.h index 9fd1353d4..28ecc4bb9 100644 --- a/src/components/i2c/drivers/drvPm25.h +++ b/src/components/i2c/drivers/drvPm25.h @@ -1,13 +1,13 @@ /*! * @file drvPm25.h * - * Device driver for the Adafruit PM2.5 Air Quality Sensor. + * I2C driver for the Adafruit PM2.5 Air Quality Sensor. * * Adafruit invests time and resources providing this open source code, * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2022 for Adafruit Industries. + * Copyright (c) Brent Rubell 2022-2025 for Adafruit Industries. * * MIT license, all text here must be included in any redistribution. * @@ -20,15 +20,12 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the PM25 sensor. */ -/**************************************************************************/ class drvPm25 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a PM25 sensor. @param i2c @@ -40,19 +37,16 @@ class drvPm25 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvPm25(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the PM25 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _pm25 = new Adafruit_PM25AQI(); // Wait three seconds for the sensor to boot up! @@ -60,7 +54,6 @@ class drvPm25 : public drvBase { return _pm25->begin_I2C(_i2c); } - /*******************************************************************************/ /*! @brief Gets the PM25 sensor's PM1.0 STD reading. @param pm10StdEvent @@ -68,7 +61,6 @@ class drvPm25 : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { PM25_AQI_Data data; if (!_pm25->read(&data)) { @@ -82,7 +74,6 @@ class drvPm25 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the PM25 sensor's PM2.5 STD reading. @param pm25StdEvent @@ -90,7 +81,6 @@ class drvPm25 : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { PM25_AQI_Data data; if (!_pm25->read(&data)) { @@ -103,7 +93,6 @@ class drvPm25 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the PM25 sensor's PM10.0 STD reading. @param pm100StdEvent @@ -111,7 +100,6 @@ class drvPm25 : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { PM25_AQI_Data data; if (!_pm25->read(&data)) { diff --git a/src/components/i2c/drivers/drvScd30.h b/src/components/i2c/drivers/drvScd30.h index 9bf6538d8..1f418379f 100644 --- a/src/components/i2c/drivers/drvScd30.h +++ b/src/components/i2c/drivers/drvScd30.h @@ -19,15 +19,12 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SCD30 sensor. */ -/**************************************************************************/ class drvScd30 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SCD30 sensor. @param i2c @@ -39,25 +36,21 @@ class drvScd30 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvScd30(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SCD30 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _scd = new Adafruit_SCD30(); return _scd->begin((uint8_t)_address, _i2c); } - /*******************************************************************************/ /*! @brief Checks if sensor was read within last 1s, or is the first read. @returns True if the sensor was recently read, False otherwise. @@ -66,12 +59,10 @@ class drvScd30 : public drvBase { return _lastRead != 0 && millis() - _lastRead < 1000; } - /*******************************************************************************/ /*! @brief Checks if the sensor is ready to be read @returns True if the sensor is ready, False otherwise. */ - /*******************************************************************************/ bool isSensorReady() { if (!_scd->dataReady()) { // failed, one more quick attempt @@ -83,12 +74,10 @@ class drvScd30 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Reads the SCD30 sensor. @returns True if the sensor was read successfully, False otherwise. */ - /*******************************************************************************/ bool readSensorData() { // dont read sensor more than once per second if (hasBeenReadInLastSecond()) { @@ -109,7 +98,6 @@ class drvScd30 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD30's current temperature. @param tempEvent @@ -117,7 +105,6 @@ class drvScd30 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // check if sensor is enabled and data is available if (!readSensorData()) { @@ -128,7 +115,6 @@ class drvScd30 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD30's current relative humidity reading. @param humidEvent @@ -136,7 +122,6 @@ class drvScd30 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // check if sensor is enabled and data is available if (!readSensorData()) { @@ -147,7 +132,6 @@ class drvScd30 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD30's current CO2 reading. @param co2Event @@ -155,7 +139,6 @@ class drvScd30 : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventCO2(sensors_event_t *co2Event) { // check if sensor is enabled and data is available if (!readSensorData()) { diff --git a/src/components/i2c/drivers/drvScd4x.h b/src/components/i2c/drivers/drvScd4x.h index 59f512efb..d688c5a34 100644 --- a/src/components/i2c/drivers/drvScd4x.h +++ b/src/components/i2c/drivers/drvScd4x.h @@ -21,15 +21,12 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SCD40 sensor. */ -/**************************************************************************/ class drvScd4x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SCD40 sensor. @param i2c @@ -41,19 +38,16 @@ class drvScd4x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvScd4x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SCD40 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _scd = new SensirionI2cScd4x(); _scd->begin(*_i2c, _address); @@ -73,13 +67,11 @@ class drvScd4x : public drvBase { return true; } - /********************************************************************************/ /*! @brief Attempts to read the SCD4x's sensor measurements @returns True if the measurements were read without errors, False if read errors occured or if sensor did not have data ready. */ - /********************************************************************************/ bool readSensorMeasurements() { uint16_t error; bool isDataReady = false; @@ -100,7 +92,6 @@ class drvScd4x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD40's current temperature. @param tempEvent @@ -108,7 +99,6 @@ class drvScd4x : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // read all sensor measurements readSensorMeasurements(); @@ -116,7 +106,6 @@ class drvScd4x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD40's current relative humidity reading. @param humidEvent @@ -124,7 +113,6 @@ class drvScd4x : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // read all sensor measurements readSensorMeasurements(); @@ -132,7 +120,6 @@ class drvScd4x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SCD40's current CO2 reading. @param co2Event @@ -140,7 +127,6 @@ class drvScd4x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventCO2(sensors_event_t *co2Event) { // read all sensor measurements readSensorMeasurements(); diff --git a/src/components/i2c/drivers/drvSen5x.h b/src/components/i2c/drivers/drvSen5x.h index e4a876a83..a2da71491 100644 --- a/src/components/i2c/drivers/drvSen5x.h +++ b/src/components/i2c/drivers/drvSen5x.h @@ -21,17 +21,14 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SEN5X sensor. */ -/**************************************************************************/ class drvSen5x : public drvBase { const float OVERFLOW_SEN55 = (0xFFFF / 10); // maxes out at u_int16 / 10 public: - /*******************************************************************************/ /*! @brief Constructor for a SEN5X sensor. @param i2c @@ -43,19 +40,16 @@ class drvSen5x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvSen5x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SEN5X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _sen = new SensirionI2CSen5x(); _sen->begin(*_i2c); @@ -73,7 +67,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X's current temperature. @param tempEvent @@ -81,7 +74,6 @@ class drvSen5x : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -100,7 +92,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X's current relative humidity reading. @param humidEvent @@ -108,7 +99,6 @@ class drvSen5x : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -127,7 +117,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X's current NOX reading. Note: If this value is unknown, which is true for SEN54, @@ -138,7 +127,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventNOxIndex(sensors_event_t *noxIndexEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -157,7 +145,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X's current VOC reading. @param vocIndexEvent @@ -165,7 +152,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventVOCIndex(sensors_event_t *vocIndexEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -184,7 +170,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X sensor's PM1.0 STD reading. @param pm10StdEvent @@ -192,7 +177,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -212,7 +196,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X sensor's PM2.5 STD reading. @param pm25StdEvent @@ -220,7 +203,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -240,7 +222,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X sensor's PM4.0 STD reading. @param pm40StdEvent @@ -248,7 +229,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM40_STD(sensors_event_t *pm40StdEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, @@ -268,7 +248,6 @@ class drvSen5x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SEN5X sensor's PM10.0 STD reading. @param pm100StdEvent @@ -276,7 +255,6 @@ class drvSen5x : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { float massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0, massConcentrationPm10p0, ambientHumidity, diff --git a/src/components/i2c/drivers/drvSgp40.h b/src/components/i2c/drivers/drvSgp40.h index 2255b3a39..ba1813e8a 100644 --- a/src/components/i2c/drivers/drvSgp40.h +++ b/src/components/i2c/drivers/drvSgp40.h @@ -20,14 +20,11 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SGP40 sensor. */ -/**************************************************************************/ class drvSgp40 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SGP40 sensor. @param i2c @@ -39,19 +36,16 @@ class drvSgp40 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvSgp40(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SGP40 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _sgp40 = new Adafruit_SGP40(); if (!_sgp40->begin(_i2c)) { @@ -63,7 +57,6 @@ class drvSgp40 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the sensor's current raw unprocessed value. @param rawEvent @@ -71,13 +64,11 @@ class drvSgp40 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *rawEvent) { rawEvent->data[0] = (float)_sgp40->measureRaw(); return true; } - /*******************************************************************************/ /*! @brief Gets the SGP40's current VOC reading. @param vocIndexEvent @@ -85,7 +76,6 @@ class drvSgp40 : public drvBase { @returns True if the sensor value was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventVOCIndex(sensors_event_t *vocIndexEvent) { vocIndexEvent->voc_index = (float)_sgp40->measureVocIndex(); return true; diff --git a/src/components/i2c/drivers/drvSht3x.h b/src/components/i2c/drivers/drvSht3x.h index 4f20651a3..21260acb1 100644 --- a/src/components/i2c/drivers/drvSht3x.h +++ b/src/components/i2c/drivers/drvSht3x.h @@ -22,15 +22,12 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SHT3X sensor. */ -/**************************************************************************/ class drvSht3x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SHT3X sensor. @param i2c @@ -42,19 +39,16 @@ class drvSht3x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvSht3x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SHT3X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { if (_address == 0x44) // if address 0x44 (dec:68), alternative = 0x45 _sht3x = new SHTSensor(SHTSensor::SHT3X); @@ -86,7 +80,6 @@ class drvSht3x : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // populate temp and humidity objects with fresh data if (!_sht3x->readSample()) @@ -95,7 +88,6 @@ class drvSht3x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SHT3X's current relative humidity reading. @param humidEvent @@ -103,7 +95,6 @@ class drvSht3x : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // populate temp and humidity objects with fresh data if (!_sht3x->readSample()) diff --git a/src/components/i2c/drivers/drvSht4x.h b/src/components/i2c/drivers/drvSht4x.h index 68802fff6..a0a7f8357 100644 --- a/src/components/i2c/drivers/drvSht4x.h +++ b/src/components/i2c/drivers/drvSht4x.h @@ -21,15 +21,12 @@ #include "Adafruit_SHT4x.h" #include "drvBase.h" -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SHT4X sensor. */ -/**************************************************************************/ class drvSht4x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SHT4X sensor. @param i2c @@ -41,19 +38,16 @@ class drvSht4x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvSht4x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SHT4X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _sht4x = new Adafruit_SHT4x(); if (!_sht4x->begin()) @@ -65,7 +59,6 @@ class drvSht4x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SHT4X's current temperature. @param tempEvent @@ -73,14 +66,12 @@ class drvSht4x : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { sensors_event_t humid; _sht4x->getEvent(&humid, tempEvent); return true; } - /*******************************************************************************/ /*! @brief Gets the SHT4X's current relative humidity reading. @param humidEvent @@ -88,7 +79,6 @@ class drvSht4x : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { sensors_event_t temp; _sht4x->getEvent(humidEvent, &temp); diff --git a/src/components/i2c/drivers/drvShtc3.h b/src/components/i2c/drivers/drvShtc3.h index 28b866c93..a836493e0 100644 --- a/src/components/i2c/drivers/drvShtc3.h +++ b/src/components/i2c/drivers/drvShtc3.h @@ -22,15 +22,12 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SHTC3 sensor. */ -/**************************************************************************/ class drvShtc3 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SHTC3 sensor. @param i2c @@ -42,25 +39,21 @@ class drvShtc3 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvShtc3(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Initializes the SHTC3 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _shtc3 = new SHTSensor(SHTSensor::SHTC3); return _shtc3->init(*_i2c); } - /*******************************************************************************/ /*! @brief Gets the SHTC3's current temperature. @param tempEvent @@ -68,7 +61,6 @@ class drvShtc3 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // populate temp and humidity objects with fresh data if (!_shtc3->readSample()) @@ -77,7 +69,6 @@ class drvShtc3 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the SHTC3's current relative humidity reading. @param humidEvent @@ -85,7 +76,6 @@ class drvShtc3 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // populate temp and humidity objects with fresh data if (!_shtc3->readSample()) diff --git a/src/components/i2c/drivers/drvSi7021.h b/src/components/i2c/drivers/drvSi7021.h index 2db0e244d..ffc253f4f 100644 --- a/src/components/i2c/drivers/drvSi7021.h +++ b/src/components/i2c/drivers/drvSi7021.h @@ -21,15 +21,12 @@ #include #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the SI7021 sensor. */ -/**************************************************************************/ class drvSi7021 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a SI7021 sensor. @param i2c @@ -41,35 +38,29 @@ class drvSi7021 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvSi7021(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an SI7021 sensor. */ - /*******************************************************************************/ ~drvSi7021() { // Called when a Si7021 component is deleted. delete _si7021; } - /*******************************************************************************/ /*! @brief Initializes the SI7021 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _si7021 = new Adafruit_Si7021(_i2c); return _si7021->begin(); } - /*******************************************************************************/ /*! @brief Gets the SI7021's current temperature. @param tempEvent @@ -77,14 +68,12 @@ class drvSi7021 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // check if sensor is enabled and data is available tempEvent->temperature = _si7021->readTemperature(); return true; } - /*******************************************************************************/ /*! @brief Gets the SI7021's current relative humidity reading. @param humidEvent @@ -92,7 +81,6 @@ class drvSi7021 : public drvBase { @returns True if the humidity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // check if sensor is enabled and data is available humidEvent->relative_humidity = _si7021->readHumidity(); diff --git a/src/components/i2c/drivers/drvStemmaSoil.h b/src/components/i2c/drivers/drvStemmaSoil.h index 3f6555626..2629abd28 100644 --- a/src/components/i2c/drivers/drvStemmaSoil.h +++ b/src/components/i2c/drivers/drvStemmaSoil.h @@ -19,14 +19,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for the STEMMA soil sensor. */ -/**************************************************************************/ class drvStemmaSoil : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a STEMMA soil sensor. @param i2c @@ -38,7 +35,6 @@ class drvStemmaSoil : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvStemmaSoil(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { @@ -50,22 +46,17 @@ class drvStemmaSoil : public drvBase { _seesaw = new Adafruit_seesaw(_i2c); } - /*******************************************************************************/ /*! @brief Destructor for a STEMMA soil sensor. */ - /*******************************************************************************/ ~drvStemmaSoil() { _seesaw = nullptr; } - /*******************************************************************************/ /*! @brief Initializes the soil sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { return _seesaw->begin(_address); } - /*******************************************************************************/ /*! @brief Gets the sensor's current temperature. @param tempEvent @@ -73,13 +64,11 @@ class drvStemmaSoil : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { tempEvent->temperature = _seesaw->getTemp(); return true; } - /*******************************************************************************/ /*! @brief Gets the sensor's current moisture sensor capacitance value. @param rawEvent @@ -87,7 +76,6 @@ class drvStemmaSoil : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *rawEvent) { uint16_t touchData = _seesaw->touchRead(0); diff --git a/src/components/i2c/drivers/drvTmp117.h b/src/components/i2c/drivers/drvTmp117.h index 1302fffff..a15241ff7 100644 --- a/src/components/i2c/drivers/drvTmp117.h +++ b/src/components/i2c/drivers/drvTmp117.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a TMP117 sensor. */ -/**************************************************************************/ class drvTmp117 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a TMP117 sensor. @param i2c @@ -37,35 +34,29 @@ class drvTmp117 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvTmp117(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an TMP117 sensor. */ - /*******************************************************************************/ ~drvTmp117() { // Called when a TMP117 component is deleted. delete _tmp117; } - /*******************************************************************************/ /*! @brief Initializes the TMP117 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _tmp117 = new Adafruit_TMP117(); return _tmp117->begin((uint8_t)_address, _i2c); } - /*******************************************************************************/ /*! @brief Gets the TMP117's current temperature. @param tempEvent @@ -73,7 +64,6 @@ class drvTmp117 : public drvBase { @returns True if the temperature was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { return _tmp117->getEvent(tempEvent); } diff --git a/src/components/i2c/drivers/drvTsl2591.h b/src/components/i2c/drivers/drvTsl2591.h index 12cd33acc..b69f827fa 100644 --- a/src/components/i2c/drivers/drvTsl2591.h +++ b/src/components/i2c/drivers/drvTsl2591.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a TSL2591 sensor. */ -/**************************************************************************/ class drvTsl2591 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a TSL2591 sensor. @param i2c @@ -37,26 +34,21 @@ class drvTsl2591 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvTsl2591(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an TSL2591 sensor. */ - /*******************************************************************************/ ~drvTsl2591() { delete _tsl; } - /*******************************************************************************/ /*! @brief Initializes the TSL2591 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _tsl = new Adafruit_TSL2591(2591); // Attempt to initialize TSL2591 @@ -71,7 +63,6 @@ class drvTsl2591 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the Adafruit Unified Sensor API. @@ -80,7 +71,6 @@ class drvTsl2591 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { // Get sensor event _tsl->getEvent(lightEvent); diff --git a/src/components/i2c/drivers/drvVeml7700.h b/src/components/i2c/drivers/drvVeml7700.h index f23d56883..1789990fa 100644 --- a/src/components/i2c/drivers/drvVeml7700.h +++ b/src/components/i2c/drivers/drvVeml7700.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VEML7700 sensor. */ -/**************************************************************************/ class drvVeml7700 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VEML7700 sensor. @param i2c @@ -37,33 +34,27 @@ class drvVeml7700 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVeml7700(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VEML7700 sensor. */ - /*******************************************************************************/ ~drvVeml7700() { delete _veml; } - /*******************************************************************************/ /*! @brief Initializes the VEML7700 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _veml = new Adafruit_VEML7700(); // Attempt to initialize and configure VEML7700 return _veml->begin(_i2c); } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the Adafruit Unified Sensor API. Always uses VEML_LUX_AUTO, @@ -73,7 +64,6 @@ class drvVeml7700 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { // Get sensor event populated in lux via AUTO integration and gain lightEvent->light = _veml->readLux(VEML_LUX_AUTO); diff --git a/src/components/i2c/drivers/drvVl53l0x.h b/src/components/i2c/drivers/drvVl53l0x.h index a9f05a360..62dad943d 100644 --- a/src/components/i2c/drivers/drvVl53l0x.h +++ b/src/components/i2c/drivers/drvVl53l0x.h @@ -19,14 +19,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VL53L0X sensor. */ -/**************************************************************************/ class drvVl53l0x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VL53L0X sensor. @param i2c @@ -38,29 +35,24 @@ class drvVl53l0x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVl53l0x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VL53L0X sensor. */ - /*******************************************************************************/ ~drvVl53l0x() { // Called when a VL53L0X component is deleted. delete _vl53l0x; } - /*******************************************************************************/ /*! @brief Initializes the VL53L0X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _vl53l0x = new Adafruit_VL53L0X(); bool isInit = @@ -69,7 +61,6 @@ class drvVl53l0x : public drvBase { return isInit; } - /*******************************************************************************/ /*! @brief Gets the VL53L0X's current proximity. @param proximityEvent @@ -77,7 +68,6 @@ class drvVl53l0x : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { u_int16_t proximityMM = _vl53l0x->readRange(); if (proximityMM == 0xffff) { diff --git a/src/components/i2c/drivers/drvVl53l1x.h b/src/components/i2c/drivers/drvVl53l1x.h index 2896687f4..92dff8190 100644 --- a/src/components/i2c/drivers/drvVl53l1x.h +++ b/src/components/i2c/drivers/drvVl53l1x.h @@ -19,14 +19,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VL53L1X sensor. */ -/**************************************************************************/ class drvVl53l1x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VL53L1X sensor. @param i2c @@ -38,29 +35,24 @@ class drvVl53l1x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVl53l1x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VL53L1X sensor. */ - /*******************************************************************************/ ~drvVl53l1x() { // Called when a VL53L1X component is deleted. delete _VL53L1X; } - /*******************************************************************************/ /*! @brief Initializes the VL53L1X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _VL53L1X = new Adafruit_VL53L1X(); if (_VL53L1X->begin((uint8_t)_address, _i2c, false)) { @@ -71,7 +63,6 @@ class drvVl53l1x : public drvBase { return false; } - /*******************************************************************************/ /*! @brief Gets the VL53L1X's current proximity. @param proximityEvent @@ -79,7 +70,6 @@ class drvVl53l1x : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { if (!_VL53L1X->dataReady()) { return false; diff --git a/src/components/i2c/drivers/drvVl53l4cd.h b/src/components/i2c/drivers/drvVl53l4cd.h index 523f16708..1e52c2b19 100644 --- a/src/components/i2c/drivers/drvVl53l4cd.h +++ b/src/components/i2c/drivers/drvVl53l4cd.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VL53L4CD sensor. */ -/**************************************************************************/ class drvVl53l4cd : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VL53L4CD sensor. @param i2c @@ -37,29 +34,24 @@ class drvVl53l4cd : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVl53l4cd(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VL53L4CD sensor. */ - /*******************************************************************************/ ~drvVl53l4cd() { // Called when a VL53L4CD component is deleted. delete _VL53L4CD; } - /*******************************************************************************/ /*! @brief Initializes the VL53L4CD sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _VL53L4CD = new VL53L4CD(_i2c, -1); @@ -105,7 +97,6 @@ class drvVl53l4cd : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the VL53L4CD's current proximity. @param proximityEvent @@ -113,7 +104,6 @@ class drvVl53l4cd : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { uint8_t NewDataReady = 0; VL53L4CD_Result_t results; diff --git a/src/components/i2c/drivers/drvVl53l4cx.h b/src/components/i2c/drivers/drvVl53l4cx.h index d5521868e..c4dca6008 100644 --- a/src/components/i2c/drivers/drvVl53l4cx.h +++ b/src/components/i2c/drivers/drvVl53l4cx.h @@ -23,14 +23,11 @@ #define VL53_READING_DELAY 250 ///< Delay for reading data attempts #define VL53_TIMING_BUDGET_NS 200000 ///< Timing budget for VL53L4CX sensor -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VL53L4CX sensor. */ -/**************************************************************************/ class drvVl53l4cx : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VL53L4CX sensor. @param i2c @@ -42,29 +39,24 @@ class drvVl53l4cx : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVl53l4cx(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VL53L4CX sensor. */ - /*******************************************************************************/ ~drvVl53l4cx() { // Called when a VL53L4CX component is deleted. delete _VL53L4CX; } - /*******************************************************************************/ /*! @brief Initializes the VL53L4CX sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _VL53L4CX = new VL53L4CX(_i2c, VL53_SHUTDOWN_PIN); @@ -93,7 +85,6 @@ class drvVl53l4cx : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Gets the VL53L4CX's current proximity for first object if found. @param proximityEvent @@ -101,12 +92,10 @@ class drvVl53l4cx : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { return getProximity(proximityEvent, 0); } - /*******************************************************************************/ /*! @brief Gets the VL53L4CX's current proximity for second object if found. @@ -115,12 +104,10 @@ class drvVl53l4cx : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventRaw(sensors_event_t *proximityEvent) { return getProximity(proximityEvent, 1); } - /*******************************************************************************/ /*! @brief Gets the VL53L4CX's current proximity (first or second object). @param proximityEvent @@ -131,7 +118,6 @@ class drvVl53l4cx : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getProximity(sensors_event_t *proximityEvent, int whichObject = 0) { VL53L4CX_MultiRangingData_t MultiRangingData; VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData; @@ -181,7 +167,6 @@ class drvVl53l4cx : public drvBase { proximityEvent); } - /*******************************************************************************/ /*! @brief Checks the VL53L4CX's proximity result and sets event value. @param rangingData @@ -191,7 +176,6 @@ class drvVl53l4cx : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool updateDataPointIfValid(VL53L4CX_TargetRangeData_t rangingData, sensors_event_t *proximityEvent) { if (rangingData.RangeStatus == VL53L4CX_RANGESTATUS_RANGE_VALID) { diff --git a/src/components/i2c/drivers/drvVl6180x.h b/src/components/i2c/drivers/drvVl6180x.h index 2a1dd698f..14f682470 100644 --- a/src/components/i2c/drivers/drvVl6180x.h +++ b/src/components/i2c/drivers/drvVl6180x.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VL6180X sensor. */ -/**************************************************************************/ class drvVl6180x : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VL6180X sensor. @param i2c @@ -37,35 +34,29 @@ class drvVl6180x : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVl6180x(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VL6180X sensor. */ - /*******************************************************************************/ ~drvVl6180x() { // Called when a VL6180X component is deleted. delete _vl6180x; } - /*******************************************************************************/ /*! @brief Initializes the VL6180X sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() { _vl6180x = new Adafruit_VL6180X(_address); return _vl6180x->begin(_i2c); } - /*******************************************************************************/ /*! @brief Gets the VL6180X's current proximity. @param proximityEvent @@ -73,7 +64,6 @@ class drvVl6180x : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { uint8_t range = _vl6180x->readRange(); uint8_t status = _vl6180x->readRangeStatus(); @@ -106,7 +96,6 @@ class drvVl6180x : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a light sensor reading. @param lightEvent @@ -114,7 +103,6 @@ class drvVl6180x : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { // TODO: Update when I2C Sensor Properties allow setting custom Gain, etc. // Gain_5 results in max 41.6klux with cover glass - See 2.10.3 in datasheet diff --git a/src/components/i2c/drivers/drvVncl4020.h b/src/components/i2c/drivers/drvVncl4020.h index ccaaa66a8..fcd51d051 100644 --- a/src/components/i2c/drivers/drvVncl4020.h +++ b/src/components/i2c/drivers/drvVncl4020.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VCNL4020 sensor. */ -/**************************************************************************/ class drvVncl4020 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VCNL4020 sensor. @param i2c @@ -37,33 +34,27 @@ class drvVncl4020 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVncl4020(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VCNL4020 sensor. */ - /*******************************************************************************/ ~drvVncl4020() { delete _vcnl4020; } - /*******************************************************************************/ /*! @brief Initializes the VCNL4020 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _vcnl4020 = new Adafruit_VCNL4020(); // Attempt to initialize and configure VCNL4020 return _vcnl4020->begin(_i2c, _address); } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the Adafruit Unified Sensor API. @@ -72,14 +63,12 @@ class drvVncl4020 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { // Get sensor event populated in lux via AUTO integration and gain lightEvent->light = _vcnl4020->readAmbient(); return true; } - /*******************************************************************************/ /*! @brief Reads the VCNL4020's proximity value into an event (no unit). @param proximityEvent @@ -87,7 +76,6 @@ class drvVncl4020 : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { proximityEvent->data[0] = (float)_vcnl4020->readProximity(); return true; diff --git a/src/components/i2c/drivers/drvVncl4040.h b/src/components/i2c/drivers/drvVncl4040.h index 04de5ff40..05d87e402 100644 --- a/src/components/i2c/drivers/drvVncl4040.h +++ b/src/components/i2c/drivers/drvVncl4040.h @@ -18,14 +18,11 @@ #include "drvBase.h" #include -/**************************************************************************/ /*! @brief Class that provides a driver interface for a VCNL4040 sensor. */ -/**************************************************************************/ class drvVncl4040 : public drvBase { public: - /*******************************************************************************/ /*! @brief Constructor for a VCNL4040 sensor. @param i2c @@ -37,26 +34,21 @@ class drvVncl4040 : public drvBase { @param driver_name The name of the driver. */ - /*******************************************************************************/ drvVncl4040(TwoWire *i2c, uint16_t sensorAddress, uint32_t mux_channel, const char *driver_name) : drvBase(i2c, sensorAddress, mux_channel, driver_name) { // Initialization handled by drvBase constructor } - /*******************************************************************************/ /*! @brief Destructor for an VCNL4040 sensor. */ - /*******************************************************************************/ ~drvVncl4040() { delete _vcnl4040; } - /*******************************************************************************/ /*! @brief Initializes the VCNL4040 sensor and begins I2C. @returns True if initialized successfully, False otherwise. */ - /*******************************************************************************/ bool begin() override { _vcnl4040 = new Adafruit_VCNL4040(); // Attempt to initialize and configure VCNL4040 @@ -77,7 +69,6 @@ class drvVncl4040 : public drvBase { return true; } - /*******************************************************************************/ /*! @brief Performs a light sensor read using the Adafruit Unified Sensor API. @@ -86,14 +77,12 @@ class drvVncl4040 : public drvBase { @returns True if the sensor event was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventLight(sensors_event_t *lightEvent) { // Get sensor event populated in lux via AUTO integration and gain lightEvent->light = _vcnl4040->getLux(); return true; } - /*******************************************************************************/ /*! @brief Reads the VCNL4040's proximity value into an event (no unit). @param proximityEvent @@ -101,7 +90,6 @@ class drvVncl4040 : public drvBase { @returns True if the proximity was obtained successfully, False otherwise. */ - /*******************************************************************************/ bool getEventProximity(sensors_event_t *proximityEvent) { proximityEvent->data[0] = (float)_vcnl4040->getProximity(); return true; diff --git a/src/components/i2c/hardware.cpp b/src/components/i2c/hardware.cpp index ec7e821d0..d2931a7aa 100644 --- a/src/components/i2c/hardware.cpp +++ b/src/components/i2c/hardware.cpp @@ -1,35 +1,58 @@ +/*! + * @file src/components/i2c/hardware.cpp + * + * Hardware implementation for the i2c.proto API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ #include "hardware.h" -/***********************************************************************/ /*! - @brief I2C hardware class constructor + @brief Default I2C bus hardware class constructor +*/ +I2cHardware::I2cHardware() { + _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_UNSPECIFIED; + _has_mux = false; + InitBus(true); // Init default bus +} + +/*! + @brief I2C hardware class constructor for an alternative bus. + @param sda + The desired SDA pin. + @param scl + The desired SCL pin. */ -/***********************************************************************/ -I2cHardware::I2cHardware() { _has_mux = false; } +I2cHardware::I2cHardware(const char *sda, const char *scl) { + _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_UNSPECIFIED; + _has_mux = false; + InitBus(false, sda, scl); // Init alt. bus +} -/***********************************************************************/ /*! @brief I2C hardware class destructor */ -/***********************************************************************/ I2cHardware::~I2cHardware() { _has_mux = false; } -/***********************************************************************/ /*! @brief Returns the I2C bus' status. @returns The I2C bus status, as a wippersnapper_i2c_I2cBusStatus. */ -/***********************************************************************/ wippersnapper_i2c_I2cBusStatus I2cHardware::GetBusStatus() { return _bus_status; } -/***********************************************************************/ /*! @brief Optionally turns on the I2C bus, used for hardware with a power control pin for the I2C bus. */ -/***********************************************************************/ void I2cHardware::TogglePowerPin() { #if defined(PIN_I2C_POWER) // turn on the I2C power by setting pin to opposite of 'rest state' @@ -49,7 +72,6 @@ void I2cHardware::TogglePowerPin() { #endif } -/***********************************************************************/ /*! @brief Initializes an I2C bus. @param is_default @@ -60,9 +82,7 @@ void I2cHardware::TogglePowerPin() { @param scl The desired SCL pin. */ -/***********************************************************************/ void I2cHardware::InitBus(bool is_default, const char *sda, const char *scl) { - uint8_t pin_sda, pin_scl; if (!is_default && (sda == nullptr || scl == nullptr)) { _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_UNSPECIFIED; return; @@ -77,32 +97,32 @@ void I2cHardware::InitBus(bool is_default, const char *sda, const char *scl) { // Assign I2C bus pins if (is_default) { #ifndef ARDUINO_ARCH_RP2040 - pin_sda = SDA; - pin_scl = SCL; + _bus_sda = SDA; + _bus_scl = SCL; #else // RP2040 BSP uses a different naming scheme than Espressif for I2C pins - pin_sda = PIN_WIRE0_SDA; - pin_scl = PIN_WIRE0_SCL; + _bus_sda = PIN_WIRE0_SDA; + _bus_scl = PIN_WIRE0_SCL; #endif } else { - pin_sda = atoi(sda); - pin_scl = atoi(scl); + _bus_sda = atoi(sda); + _bus_scl = atoi(scl); } // Enable pullups - pinMode(pin_scl, INPUT_PULLUP); - pinMode(pin_sda, INPUT_PULLUP); + pinMode(_bus_scl, INPUT_PULLUP); + pinMode(_bus_sda, INPUT_PULLUP); delay(150); // Is the bus stuck LOW? - if (digitalRead(pin_scl) == 0 || digitalRead(pin_sda) == 0) { + if (digitalRead(_bus_scl) == 0 || digitalRead(_bus_sda) == 0) { _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_ERROR_PULLUPS; return; } // Reset bus to a high-impedance state - pinMode(pin_scl, INPUT); - pinMode(pin_sda, INPUT); + pinMode(_bus_scl, INPUT); + pinMode(_bus_sda, INPUT); // Initialize bus // NOTE: Each platform has a slightly different bus initialization routine @@ -111,24 +131,24 @@ void I2cHardware::InitBus(bool is_default, const char *sda, const char *scl) { _bus = new TwoWire(0); } else { _bus = new TwoWire(1); - _bus->setPins(pin_sda, pin_scl); + _bus->setPins(_bus_sda, _bus_scl); } - if (!_bus->begin(pin_sda, pin_scl)) { + if (!_bus->begin(_bus_sda, _bus_scl)) { _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_ERROR_HANG; return; } _bus->setClock(50000); #elif defined(ARDUINO_ARCH_ESP8266) _bus = new TwoWire(); - _bus->begin(pin_sda, pin_scl); + _bus->begin(_bus_sda, _bus_scl); _bus->setClock(50000); #elif defined(ARDUINO_ARCH_RP2040) _bus = &WIRE; - _bus->setSDA(pin_sda); - _bus->setSCL(pin_scl); + _bus->setSDA(_bus_sda); + _bus->setSCL(_bus_scl); _bus->begin(); -#elif defined(ARDUINO_ARCH_SAM) - _bus = new TwoWire(&PERIPH_WIRE, pin_sda, pin_scl); +#elif defined(ARDUINO_ARCH_SAMD) + _bus = new TwoWire(&PERIPH_WIRE, _bus_sda, _bus_scl); _bus->begin(); #else #error "I2C bus implementation not supported by this platform!" @@ -137,44 +157,57 @@ void I2cHardware::InitBus(bool is_default, const char *sda, const char *scl) { _bus_status = wippersnapper_i2c_I2cBusStatus_I2C_BUS_STATUS_SUCCESS; } -/***********************************************************************/ /*! - @brief Returns a pointer to the I2C bus. - @returns Pointer to the I2C bus. -*/ -/***********************************************************************/ -TwoWire *I2cHardware::GetBus() { return _bus; } - -/***********************************************************************/ -/*! - @brief Scans the I2C bus for devices. - @param scan_results - The results of the I2C bus scan. - @returns True if the bus was successfully scanned, False otherwise. + @brief Clears the MUX channel. + @param scan_results + The I2C bus scan results. + @returns True if the MUX channel was successfully cleared, + False otherwise. */ -/***********************************************************************/ bool I2cHardware::ScanBus(wippersnapper_i2c_I2cBusScanned *scan_results) { if (!scan_results) return false; - if (!_bus) { - WS_DEBUG_PRINTLN("[i2c] ERROR: I2C bus not initialized!"); - return false; - } + // TODO: WS object needs to be added for this to work? + /* #ifndef ARDUINO_ARCH_ESP32 + // Set I2C WDT timeout to catch I2C hangs, SAMD-specific + WS.enableWDT(I2C_WDT_TIMEOUT_MS); + WS.feedWDT(); + #endif */ + + // Get the SDA and SCL pins from the bus + // TODO: Abstract this? + char i2c_bus_scl[15] = {0}, i2c_bus_sda[15] = {0}; + snprintf(i2c_bus_scl, sizeof(i2c_bus_scl), "D%u", _bus_scl); + snprintf(i2c_bus_sda, sizeof(i2c_bus_sda), "D%u", _bus_sda); // Perform a bus scan WS_DEBUG_PRINTLN("[i2c]: Scanning I2C Bus for Devices..."); - for (uint8_t address = 1; address < 127; address++) { + for (uint8_t address = 1; address < 127; ++address) { + WS_DEBUG_PRINT("[i2c] Scanning Address: 0x"); + WS_DEBUG_PRINTLN(address, HEX); _bus->beginTransmission(address); uint8_t endTransmissionRC = _bus->endTransmission(); if (endTransmissionRC == 0) { - WS_DEBUG_PRINT("[i2c] Found Device at "); - WS_DEBUG_PRINT("0x"); - WS_DEBUG_PRINTLN(address, HEX); + WS_DEBUG_PRINTLN("[i2c] Found Device!"); + // TODO: Abstract this? Allow for mux flags to be set here, too scan_results ->i2c_bus_found_devices[scan_results->i2c_bus_found_devices_count] .i2c_device_address = address; + scan_results + ->i2c_bus_found_devices[scan_results->i2c_bus_found_devices_count] + .i2c_mux_address = 0xFFFF; // Tell user that device is not on a mux + strcpy( + scan_results + ->i2c_bus_found_devices[scan_results->i2c_bus_found_devices_count] + .i2c_bus_sda, + i2c_bus_sda); + strcpy( + scan_results + ->i2c_bus_found_devices[scan_results->i2c_bus_found_devices_count] + .i2c_bus_scl, + i2c_bus_scl); scan_results->i2c_bus_found_devices_count++; } #if defined(ARDUINO_ARCH_ESP32) @@ -200,15 +233,28 @@ bool I2cHardware::ScanBus(wippersnapper_i2c_I2cBusScanned *scan_results) { } #endif // ARDUINO_ARCH_ESP32 else { - // Did not find a device, keep scanning + WS_DEBUG_PRINTLN( + "[i2c] Did not find device: Unknown bus error has occured!"); continue; } } - return true; + /* + #ifndef ARDUINO_ARCH_ESP32 + // re-enable WipperSnapper SAMD WDT global timeout + WS.enableWDT(WS_WDT_TIMEOUT); + WS.feedWDT(); + #endif + */ + return true; // TODO: Change this! } -/***********************************************************************/ +/*! + @brief Returns a pointer to the I2C bus. + @returns Pointer to the I2C bus. +*/ +TwoWire *I2cHardware::GetBus() { return _bus; } + /*! @brief Adds a MUX to the I2C bus. @param address_register @@ -218,7 +264,6 @@ bool I2cHardware::ScanBus(wippersnapper_i2c_I2cBusScanned *scan_results) { @returns True if the MUX was successfully added to the bus, False otherwise. */ -/***********************************************************************/ bool I2cHardware::AddMuxToBus(uint32_t address_register, const char *name) { if (strcmp(name, "pca9546") == 0) { _mux_max_channels = 4; // PCA9546 supports 4 channels @@ -236,16 +281,23 @@ bool I2cHardware::AddMuxToBus(uint32_t address_register, const char *name) { return true; } -/***********************************************************************/ +/*! + @brief Removes a MUX from the I2C bus. +*/ +void I2cHardware::RemoveMux() { + ClearMuxChannel(); + _has_mux = false; + _mux_address = 0; + _mux_max_channels = 0; +} + /*! @brief Clears the enabled MUX channel. */ -/***********************************************************************/ void I2cHardware::ClearMuxChannel() { if (!_has_mux) return; - - _bus->beginTransmission(_mux_address); + _bus->beginTransmission((uint8_t)_mux_address); if (_mux_max_channels == 4) _bus->write(0b0000); else if (_mux_max_channels == 8) @@ -253,26 +305,45 @@ void I2cHardware::ClearMuxChannel() { _bus->endTransmission(); } -/***********************************************************************/ /*! @brief Enables a specific channel on a MUX. @param channel The desired MUX channel to enable. */ -/***********************************************************************/ void I2cHardware::SelectMuxChannel(uint32_t channel) { if (channel > _mux_max_channels - 1) return; - - _bus->beginTransmission(_mux_address); + _bus->beginTransmission((uint8_t)_mux_address); _bus->write(1 << channel); _bus->endTransmission(); } -/***********************************************************************/ /*! @brief Returns if a MUX is present on the I2C bus. @returns True if a MUX is present on the bus, False otherwise. */ -/***********************************************************************/ -bool I2cHardware::HasMux() { return _has_mux; } \ No newline at end of file +bool I2cHardware::HasMux() { return _has_mux; } + +/*! + @brief Returns the maximum number of channels on the MUX. + @param scan_results + The I2C bus scan results. + @returns The maximum number of channels on the MUX. +*/ +bool I2cHardware::ScanMux(wippersnapper_i2c_I2cBusScanned *scan_results) { + if (!HasMux()) { + WS_DEBUG_PRINTLN("[i2c] ERROR: No MUX present on the bus!"); + return false; + } + + for (uint8_t ch = 0; ch < _mux_max_channels; ch++) { + SelectMuxChannel(ch); + WS_DEBUG_PRINT("[i2c] Scanning MUX Channel # "); + WS_DEBUG_PRINTLN(ch); + if (!ScanBus(scan_results)) { + WS_DEBUG_PRINTLN("[i2c] ERROR: Failed to scan MUX channel!"); + return false; + } + } + return true; +} \ No newline at end of file diff --git a/src/components/i2c/hardware.h b/src/components/i2c/hardware.h index f0136254e..fc6117616 100644 --- a/src/components/i2c/hardware.h +++ b/src/components/i2c/hardware.h @@ -1,5 +1,5 @@ /*! - * @file hardware.cpp + * @file src/components/i2c/hardware.h * * Hardware driver for the i2c API * @@ -22,14 +22,15 @@ #define WIRE Wire #endif -/**************************************************************************/ +#define I2C_WDT_TIMEOUT_MS 50 + /*! @brief Interfaces with the I2C bus via the Arduino "Wire" API. */ -/**************************************************************************/ class I2cHardware { public: I2cHardware(); + I2cHardware(const char *sda, const char *scl); ~I2cHardware(); void InitBus(bool is_default, const char *sda = nullptr, const char *scl = nullptr); @@ -38,14 +39,18 @@ class I2cHardware { bool ScanBus(wippersnapper_i2c_I2cBusScanned *scan_results); // MUX bool AddMuxToBus(uint32_t address_register, const char *name); - void SelectMuxChannel(uint32_t channel); + void RemoveMux(); bool HasMux(); void ClearMuxChannel(); + void SelectMuxChannel(uint32_t channel); + bool ScanMux(wippersnapper_i2c_I2cBusScanned *scan_results); private: void TogglePowerPin(); wippersnapper_i2c_I2cBusStatus _bus_status; ///< I2C bus status TwoWire *_bus = nullptr; ///< I2C bus + uint8_t _bus_sda; ///< SDA pin + uint8_t _bus_scl; ///< SCL pin bool _has_mux; ///< Is a MUX present on the bus? uint32_t _mux_address; ///< I2C address for the MUX int _mux_max_channels; ///< Maximum possible number of MUX channels diff --git a/src/components/i2c/model.cpp b/src/components/i2c/model.cpp index c28a667ca..f5d0b21a7 100644 --- a/src/components/i2c/model.cpp +++ b/src/components/i2c/model.cpp @@ -1,7 +1,7 @@ /*! - * @file model.cpp + * @file src/components/i2c/model.cpp * - * Model for the i2c.proto message. + * Model for the i2c.proto and i2c_output.proto messages. * * Adafruit invests time and resources providing this open source code, * please support Adafruit and open-source hardware by purchasing @@ -14,33 +14,38 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief I2C constructor */ -/***********************************************************************/ I2cModel::I2cModel() { - _msg_i2c_bus_scan = wippersnapper_i2c_I2cBusScan_init_default; - _msg_i2c_bus_scanned = wippersnapper_i2c_I2cBusScanned_init_default; - _msg_i2c_device_add_replace = - wippersnapper_i2c_I2cDeviceAddOrReplace_init_default; - _msg_i2c_device_added_replaced = - wippersnapper_i2c_I2cDeviceAddedOrReplaced_init_default; - _msg_i2c_device_remove = wippersnapper_i2c_I2cDeviceRemove_init_default; - _msg_i2c_device_removed = wippersnapper_i2c_I2cDeviceRemoved_init_default; - _msg_i2c_device_event = wippersnapper_i2c_I2cDeviceEvent_init_default; + memset(&_msg_i2c_bus_scan, 0, sizeof(_msg_i2c_bus_scan)); + memset(&_msg_i2c_bus_scanned, 0, sizeof(_msg_i2c_bus_scanned)); + memset(&_msg_i2c_device_add_replace, 0, sizeof(_msg_i2c_device_add_replace)); + memset(&_msg_i2c_device_added_replaced, 0, + sizeof(_msg_i2c_device_added_replaced)); + memset(&_msg_i2c_device_remove, 0, sizeof(_msg_i2c_device_remove)); + memset(&_msg_i2c_device_removed, 0, sizeof(_msg_i2c_device_removed)); + memset(&_msg_i2c_device_event, 0, sizeof(_msg_i2c_device_event)); + memset(&_msg_i2c_device_output_write, 0, + sizeof(_msg_i2c_device_output_write)); } -/***********************************************************************/ /*! @brief I2C destructor */ -/***********************************************************************/ I2cModel::~I2cModel() { - // nothing to add here! + memset(&_msg_i2c_bus_scan, 0, sizeof(_msg_i2c_bus_scan)); + memset(&_msg_i2c_bus_scanned, 0, sizeof(_msg_i2c_bus_scanned)); + memset(&_msg_i2c_device_add_replace, 0, sizeof(_msg_i2c_device_add_replace)); + memset(&_msg_i2c_device_added_replaced, 0, + sizeof(_msg_i2c_device_added_replaced)); + memset(&_msg_i2c_device_remove, 0, sizeof(_msg_i2c_device_remove)); + memset(&_msg_i2c_device_removed, 0, sizeof(_msg_i2c_device_removed)); + memset(&_msg_i2c_device_event, 0, sizeof(_msg_i2c_device_event)); + memset(&_msg_i2c_device_output_write, 0, + sizeof(_msg_i2c_device_output_write)); } -/***************************************************************************/ /*! @brief Returns the numeric event value mapped to a sensor event. @param sensor_type @@ -49,7 +54,6 @@ I2cModel::~I2cModel() { The sensors_event_t event. @returns The value of the SensorType. */ -/***************************************************************************/ float GetValueFromSensorsEvent(wippersnapper_sensor_SensorType sensor_type, sensors_event_t *event) { float value = 0.0; @@ -124,7 +128,6 @@ float GetValueFromSensorsEvent(wippersnapper_sensor_SensorType sensor_type, return value; } -/****************************************************************************/ /*! @brief Decodes a I2cDeviceRemove message from an input stream. @param stream @@ -132,50 +135,130 @@ float GetValueFromSensorsEvent(wippersnapper_sensor_SensorType sensor_type, @returns True if the I2cDeviceRemove message was decoded successfully, False otherwise. */ -/****************************************************************************/ bool I2cModel::DecodeI2cDeviceRemove(pb_istream_t *stream) { - _msg_i2c_device_remove = wippersnapper_i2c_I2cDeviceRemove_init_default; - return pb_decode(stream, wippersnapper_i2c_I2cDeviceRemove_fields, - &_msg_i2c_device_remove); + WS_DEBUG_PRINTLN("[i2c] Set _msg_i2c_device_remove..."); + memset(&_msg_i2c_device_remove, 0, sizeof(_msg_i2c_device_remove)); + bool is_success = false; + is_success = pb_decode(stream, wippersnapper_i2c_I2cDeviceRemove_fields, + &_msg_i2c_device_remove); + WS_DEBUG_PRINT("is_success: "); + WS_DEBUG_PRINTLN(is_success); + return is_success; } -/**********************************************************************/ /*! @brief Returns a pointer to the I2cDeviceRemove message. @returns Pointer to the I2cDeviceRemove message. */ -/**********************************************************************/ wippersnapper_i2c_I2cDeviceRemove *I2cModel::GetI2cDeviceRemoveMsg() { return &_msg_i2c_device_remove; } -/***************************************************************************/ +/*! + @brief Decodes a I2cBusScan message from an input stream. + @param stream + A pointer to the pb_istream_t stream. + @returns True if the I2cBusScan message was decoded successfully, False + otherwise. +*/ +bool I2cModel::DecodeI2cBusScan(pb_istream_t *stream) { + memset(&_msg_i2c_bus_scan, 0, sizeof(_msg_i2c_bus_scan)); + return pb_decode(stream, wippersnapper_i2c_I2cBusScan_fields, + &_msg_i2c_bus_scan); +} + +/*! + @brief Returns a pointer to the I2cBusScan message. + @returns Pointer to a I2cBusScan message. +*/ +wippersnapper_i2c_I2cBusScan *I2cModel::GetI2cBusScanMsg() { + return &_msg_i2c_bus_scan; +} + +/*! + @brief Returns a pointer to the I2cBusScanned message. + @returns Pointer to a I2cBusScanned message. +*/ +wippersnapper_i2c_I2cBusScanned *I2cModel::GetI2cBusScannedMsg() { + return &_msg_i2c_bus_scanned; +} + +/*! + @brief Clears the I2cBusScanned message. +*/ +void I2cModel::ClearI2cBusScanned() { + memset(&_msg_i2c_bus_scanned, 0, sizeof(_msg_i2c_bus_scanned)); + _msg_i2c_bus_scanned.i2c_bus_found_devices_count = 0; // zero-out the count +} + +/*! + @brief Adds a device to the I2cBusScanned message. + @param bus_scl + The device's SCL pin. + @param bus_sda + The device's SDA pin. + @param addr_device + The device's i2c address. + @param addr_mux + Optional MUX address. + @param mux_channel + Optional MUX channel + @returns True if the device was added to the bus scan, False otherwise. +*/ +bool I2cModel::AddDeviceToBusScan(const char *bus_scl, const char *bus_sda, + uint32_t addr_device, uint32_t addr_mux, + uint32_t mux_channel) { + pb_size_t idx_device = _msg_i2c_bus_scanned.i2c_bus_found_devices_count; + if (idx_device >= MAX_I2C_SCAN_DEVICES) + return false; + // Fill I2cDeviceDescriptor + strcpy(_msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_bus_scl, + bus_scl); + strcpy(_msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_bus_sda, + bus_sda); + _msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_device_address = + addr_device; + // Optionally fill MUX info + if (_msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_mux_address != + 0xFFFF) { + _msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_mux_address = + addr_mux; + _msg_i2c_bus_scanned.i2c_bus_found_devices[idx_device].i2c_mux_channel = + mux_channel; + } + _msg_i2c_bus_scanned.i2c_bus_found_devices_count++; + return true; +} + /*! @brief Decodes a I2cDeviceAddReplace message from an input stream. @param stream A pointer to the pb_istream_t stream. @returns True if the stream was decoded successfully, False otherwise. */ -/***************************************************************************/ bool I2cModel::DecodeI2cDeviceAddReplace(pb_istream_t *stream) { - _msg_i2c_device_add_replace = - wippersnapper_i2c_I2cDeviceAddOrReplace_init_default; + memset(&_msg_i2c_device_add_replace, 0, sizeof(_msg_i2c_device_add_replace)); return pb_decode(stream, wippersnapper_i2c_I2cDeviceAddOrReplace_fields, &_msg_i2c_device_add_replace); } -/**********************************************************************/ /*! @brief Returns a pointer to the I2cDeviceAddOrReplace message. @returns Pointer to the I2cDeviceAddOrReplace message. */ -/**********************************************************************/ wippersnapper_i2c_I2cDeviceAddOrReplace * I2cModel::GetI2cDeviceAddOrReplaceMsg() { return &_msg_i2c_device_add_replace; } -/***************************************************************************/ +/*! + @brief Returns a pointer to the I2cOutputAdd message. + @returns Pointer to the I2cOutputAdd message. +*/ +wippersnapper_i2c_output_I2cOutputAdd *I2cModel::GetI2cOutputAddMsg() { + return &_msg_i2c_device_add_replace.i2c_output_add; +} + /*! @brief Encodes a I2cDeviceAddedOrReplaced message. @param device_descriptor @@ -186,7 +269,6 @@ I2cModel::GetI2cDeviceAddOrReplaceMsg() { The I2cDeviceStatus message. @returns True if the message was encoded successfully, False otherwise. */ -/***************************************************************************/ bool I2cModel::encodeMsgI2cDeviceAddedorReplaced( wippersnapper_i2c_I2cDeviceDescriptor device_descriptor, wippersnapper_i2c_I2cBusStatus bus_status, @@ -194,8 +276,8 @@ bool I2cModel::encodeMsgI2cDeviceAddedorReplaced( size_t sz_msg; // Fill I2cDeviceAddedOrReplaced message - _msg_i2c_device_added_replaced = - wippersnapper_i2c_I2cDeviceAddedOrReplaced_init_zero; + memset(&_msg_i2c_device_added_replaced, 0, + sizeof(_msg_i2c_device_added_replaced)); _msg_i2c_device_added_replaced.has_i2c_device_description = true; _msg_i2c_device_added_replaced.i2c_device_description = device_descriptor; _msg_i2c_device_added_replaced.i2c_bus_status = bus_status; @@ -214,28 +296,23 @@ bool I2cModel::encodeMsgI2cDeviceAddedorReplaced( &_msg_i2c_device_added_replaced); } -/**********************************************************************/ /*! @brief Returns a pointer to the I2cDeviceAddedOrReplaced message. @returns Pointer to the I2cDeviceAddedOrReplaced message. */ -/**********************************************************************/ wippersnapper_i2c_I2cDeviceAddedOrReplaced * I2cModel::GetMsgI2cDeviceAddedOrReplaced() { return &_msg_i2c_device_added_replaced; } -/**********************************************************************/ /*! @brief Clears the I2cDeviceEvent message. */ -/**********************************************************************/ void I2cModel::ClearI2cDeviceEvent() { - _msg_i2c_device_event = wippersnapper_i2c_I2cDeviceEvent_init_zero; + memset(&_msg_i2c_device_event, 0, sizeof(_msg_i2c_device_event)); _msg_i2c_device_event.i2c_device_events_count = 0; } -/**********************************************************************/ /*! @brief Sets the I2cDeviceEvent message's device description. @param bus_scl @@ -249,7 +326,6 @@ void I2cModel::ClearI2cDeviceEvent() { @param mux_channel The MUX channel. */ -/**********************************************************************/ void I2cModel::SetI2cDeviceEventDeviceDescripton(const char *bus_scl, const char *bus_sda, uint32_t addr_device, @@ -263,7 +339,6 @@ void I2cModel::SetI2cDeviceEventDeviceDescripton(const char *bus_scl, _msg_i2c_device_event.i2c_device_description.i2c_mux_channel = mux_channel; } -/***************************************************************************/ /*! @brief Adds a SensorEvent to the I2cDeviceEvent message. @param event @@ -272,7 +347,6 @@ void I2cModel::SetI2cDeviceEventDeviceDescripton(const char *bus_scl, The SensorType. @returns True if the SensorEvent was added successfully, False otherwise. */ -/***************************************************************************/ bool I2cModel::AddI2cDeviceSensorEvent( sensors_event_t &event, wippersnapper_sensor_SensorType sensor_type) { if (_msg_i2c_device_event.i2c_device_events_count >= MAX_DEVICE_EVENTS) @@ -290,12 +364,10 @@ bool I2cModel::AddI2cDeviceSensorEvent( return true; } -/***************************************************************************/ /*! @brief Encodes an I2cDeviceEvent message. @returns True if the message was encoded successfully, False otherwise. */ -/***************************************************************************/ bool I2cModel::EncodeI2cDeviceEvent() { size_t sz_msg; if (!pb_get_encoded_size(&sz_msg, wippersnapper_i2c_I2cDeviceEvent_fields, @@ -308,12 +380,91 @@ bool I2cModel::EncodeI2cDeviceEvent() { &_msg_i2c_device_event); } -/**********************************************************************/ /*! @brief Returns a pointer to the I2cDeviceEvent message. @returns Pointer to the I2cDeviceEvent message. */ -/**********************************************************************/ wippersnapper_i2c_I2cDeviceEvent *I2cModel::GetI2cDeviceEvent() { return &_msg_i2c_device_event; -} \ No newline at end of file +} + +/*! + @brief Decodes a I2cDeviceOutputWrite message from an input stream. + @param stream + A pointer to the pb_istream_t stream. + @returns True if the I2cDeviceOutputWrite message was decoded successfully, + False otherwise. +*/ +bool I2cModel::DecodeI2cDeviceOutputWrite(pb_istream_t *stream) { + memset(&_msg_i2c_device_output_write, 0, + sizeof(_msg_i2c_device_output_write)); + return pb_decode(stream, wippersnapper_i2c_I2cDeviceOutputWrite_fields, + &_msg_i2c_device_output_write); +} + +/*! + @brief Returns a pointer to the I2cDeviceOutputWrite message. + @returns Pointer to the I2cDeviceOutputWrite message. +*/ +wippersnapper_i2c_I2cDeviceOutputWrite *I2cModel::GetI2cDeviceOutputWriteMsg() { + return &_msg_i2c_device_output_write; +} + +/*! + @brief I2cOutputModel constructor +*/ +I2cOutputModel::I2cOutputModel() { + memset(&_msg_led_backpack_write, 0, sizeof(_msg_led_backpack_write)); + memset(&_msg_char_lcd_write, 0, sizeof(_msg_char_lcd_write)); +} + +/*! + @brief I2cOutputModel destructor +*/ +I2cOutputModel::~I2cOutputModel() { + memset(&_msg_led_backpack_write, 0, sizeof(_msg_led_backpack_write)); + memset(&_msg_char_lcd_write, 0, sizeof(_msg_char_lcd_write)); +} + +/*! + @brief Decodes a LedBackpackWrite message from an input stream. + @param stream + A pointer to the pb_istream_t stream. + @returns True if the LedBackpackWrite message was decoded successfully, + False otherwise. +*/ +bool I2cOutputModel::DecodeLedBackpackWrite(pb_istream_t *stream) { + memset(&_msg_led_backpack_write, 0, sizeof(_msg_led_backpack_write)); + return pb_decode(stream, wippersnapper_i2c_output_LedBackpackWrite_fields, + &_msg_led_backpack_write); +} + +/*! + @brief Decodes a CharLCDWrite message from an input stream. + @param stream + A pointer to the pb_istream_t stream. + @returns True if the CharLCDWrite message was decoded successfully, + False otherwise. +*/ +bool I2cOutputModel::DecodeCharLCDWrite(pb_istream_t *stream) { + memset(&_msg_char_lcd_write, 0, sizeof(_msg_char_lcd_write)); + return pb_decode(stream, wippersnapper_i2c_output_CharLCDWrite_fields, + &_msg_char_lcd_write); +} + +/*! + @brief Returns a pointer to the LedBackpackWrite message. + @returns Pointer to the LedBackpackWrite message. +*/ +wippersnapper_i2c_output_LedBackpackWrite * +I2cOutputModel::GetLedBackpackWriteMsg() { + return &_msg_led_backpack_write; +} + +/*! + @brief Returns a pointer to the CharLCDWrite message. + @returns Pointer to the CharLCDWrite message. +*/ +wippersnapper_i2c_output_CharLCDWrite *I2cOutputModel::GetCharLCDWriteMsg() { + return &_msg_char_lcd_write; +} diff --git a/src/components/i2c/model.h b/src/components/i2c/model.h index c1d3de35f..2c1cc08d2 100644 --- a/src/components/i2c/model.h +++ b/src/components/i2c/model.h @@ -1,7 +1,8 @@ /*! - * @file model.h + * @file src/components/i2c/model.h * - * Provides high-level interfaces for messages within i2c.proto. + * Provides high-level interfaces for messages within i2c.proto and + * i2c_output.proto. * * Adafruit invests time and resources providing this open source code, * please support Adafruit and open-source hardware by purchasing @@ -16,29 +17,45 @@ #define WS_I2C_MODEL_H #include "Wippersnapper_V2.h" #include +#include #define MAX_DEVICE_EVENTS \ 15 ///< Maximum number of SensorEvents within I2cDeviceEvent +#define MAX_I2C_SCAN_DEVICES 120 ///< Maximum number of devices found on the bus -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from i2c.proto. */ -/**************************************************************************/ class I2cModel { public: I2cModel(); ~I2cModel(); + // Decoders bool DecodeI2cDeviceAddReplace(pb_istream_t *stream); - wippersnapper_i2c_I2cDeviceRemove *GetI2cDeviceRemoveMsg(); bool DecodeI2cDeviceRemove(pb_istream_t *stream); - wippersnapper_i2c_I2cDeviceAddOrReplace *GetI2cDeviceAddOrReplaceMsg(); + bool DecodeI2cBusScan(pb_istream_t *stream); + bool DecodeI2cDeviceOutputWrite(pb_istream_t *stream); + // Encoders bool encodeMsgI2cDeviceAddedorReplaced( wippersnapper_i2c_I2cDeviceDescriptor i2c_device_description, wippersnapper_i2c_I2cBusStatus i2c_bus_status, wippersnapper_i2c_I2cDeviceStatus i2c_device_status); + bool EncodeI2cDeviceEvent(); + // Getters + wippersnapper_i2c_I2cDeviceRemove *GetI2cDeviceRemoveMsg(); + wippersnapper_i2c_I2cDeviceAddOrReplace *GetI2cDeviceAddOrReplaceMsg(); + wippersnapper_i2c_output_I2cOutputAdd *GetI2cOutputAddMsg(); wippersnapper_i2c_I2cDeviceAddedOrReplaced *GetMsgI2cDeviceAddedOrReplaced(); - // Device Event Message API + wippersnapper_i2c_I2cDeviceEvent *GetI2cDeviceEvent(); + wippersnapper_i2c_I2cBusScan *GetI2cBusScanMsg(); + wippersnapper_i2c_I2cBusScanned *GetI2cBusScannedMsg(); + wippersnapper_i2c_I2cDeviceOutputWrite *GetI2cDeviceOutputWriteMsg(); + // I2cBusScanned Message API + void ClearI2cBusScanned(); + bool AddDeviceToBusScan(const char *bus_scl, const char *bus_sda, + uint32_t addr_device, uint32_t addr_mux, + uint32_t mux_channel); + // DeviceEvent Message API void ClearI2cDeviceEvent(); void SetI2cDeviceEventDeviceDescripton(const char *bus_scl, const char *bus_sda, @@ -47,8 +64,6 @@ class I2cModel { uint32_t mux_channel); bool AddI2cDeviceSensorEvent(sensors_event_t &event, wippersnapper_sensor_SensorType sensor_type); - bool EncodeI2cDeviceEvent(); - wippersnapper_i2c_I2cDeviceEvent *GetI2cDeviceEvent(); private: wippersnapper_i2c_I2cBusScan _msg_i2c_bus_scan; @@ -58,5 +73,26 @@ class I2cModel { wippersnapper_i2c_I2cDeviceRemove _msg_i2c_device_remove; wippersnapper_i2c_I2cDeviceRemoved _msg_i2c_device_removed; wippersnapper_i2c_I2cDeviceEvent _msg_i2c_device_event; + wippersnapper_i2c_I2cDeviceOutputWrite _msg_i2c_device_output_write; +}; + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from i2c_output.proto. +*/ +class I2cOutputModel { +public: + I2cOutputModel(); + ~I2cOutputModel(); + // Decoders + bool DecodeLedBackpackWrite(pb_istream_t *stream); + bool DecodeCharLCDWrite(pb_istream_t *stream); + // Getters + wippersnapper_i2c_output_LedBackpackWrite *GetLedBackpackWriteMsg(); + wippersnapper_i2c_output_CharLCDWrite *GetCharLCDWriteMsg(); + +private: + wippersnapper_i2c_output_LedBackpackWrite _msg_led_backpack_write; + wippersnapper_i2c_output_CharLCDWrite _msg_char_lcd_write; }; #endif // WS_I2C_MODEL_H \ No newline at end of file diff --git a/src/components/pixels/controller.cpp b/src/components/pixels/controller.cpp new file mode 100644 index 000000000..eeed20a1c --- /dev/null +++ b/src/components/pixels/controller.cpp @@ -0,0 +1,146 @@ +/*! + * @file src/components/pixels/controller.cpp + * + * Implementation for the pixels API controller. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/*! + @brief Constructs a new PixelsController object +*/ +PixelsController::PixelsController() { + _pixels_model = new PixelsModel(); + _num_strands = 0; +} + +/*! + @brief Destructs a PixelsController object +*/ +PixelsController::~PixelsController() { + for (int i = 0; i < _num_strands; i++) { + delete _pixel_strands[i]; + } + _num_strands = 0; + delete _pixels_model; +} + +/*! + @brief Handles a request to add a pixel strand + @param stream + Protocol buffer input stream + @returns True if successful, False otherwise +*/ +bool PixelsController::Handle_Pixels_Add(pb_istream_t *stream) { + // Attempt to decode the istream into a PixelsAdd message + if (!_pixels_model->DecodePixelsAdd(stream)) { + WS_DEBUG_PRINTLN("[pixels]: Failed to decode PixelsAdd message!"); + return false; + } + wippersnapper_pixels_PixelsAdd *msg_add = _pixels_model->GetPixelsAddMsg(); + _pixel_strands[_num_strands] = new PixelsHardware(); + + // Configure the pixel strand + bool did_init = false; + did_init = _pixel_strands[_num_strands]->AddStrand( + msg_add->pixels_type, msg_add->pixels_ordering, msg_add->pixels_num, + msg_add->pixels_brightness, msg_add->pixels_pin_data, + msg_add->pixels_pin_dotstar_clock); + if (!did_init) { + WS_DEBUG_PRINTLN("[pixels] Failed to create strand!"); + } else { + _num_strands++; + WS_DEBUG_PRINT("[pixels]: Added strand #"); + WS_DEBUG_PRINTLN(_num_strands); + } + + // Publish PixelsAdded message to the broker + if (!_pixels_model->EncodePixelsAdded(msg_add->pixels_pin_data, did_init)) { + WS_DEBUG_PRINTLN("[pixels]: Failed to encode PixelsAdded message!"); + return false; + } + if (!WsV2.PublishSignal(wippersnapper_signal_DeviceToBroker_pixels_added_tag, + _pixels_model->GetPixelsAddedMsg())) { + WS_DEBUG_PRINTLN("[pixels]: Unable to publish PixelsAdded message!"); + return false; + } + + return true; +} + +/*! + @brief Handles a request to write to a pixel strand + @param stream + Protocol buffer input stream + @returns True if successful, False otherwise +*/ +bool PixelsController::Handle_Pixels_Write(pb_istream_t *stream) { + // Decode the PixelsWrite message + if (!_pixels_model->DecodePixelsWrite(stream)) { + WS_DEBUG_PRINTLN("[pixels]: Failed to decode PixelsWrite message!"); + return false; + } + wippersnapper_pixels_PixelsWrite *msg_write = + _pixels_model->GetPixelsWriteMsg(); + uint16_t pin_data = atoi(msg_write->pixels_pin_data + 1); + uint16_t idx = GetStrandIndex(pin_data); + if (idx == STRAND_NOT_FOUND) { + WS_DEBUG_PRINTLN("[pixels]: Failed to find strand index!"); + return false; + } + + // Call hardware to fill the strand + WS_DEBUG_PRINTLN("[pixels]: Filling strand!"); + _pixel_strands[idx]->FillStrand(msg_write->pixels_color); + return true; +} + +/*! + @brief Handles a request to remove a pixel strand + @param stream + Protocol buffer input stream + @returns True if successful, False otherwise +*/ +bool PixelsController::Handle_Pixels_Remove(pb_istream_t *stream) { + // Decode the PixelsRemove message + if (!_pixels_model->DecodePixelsRemove(stream)) { + WS_DEBUG_PRINTLN("[pixels]: Failed to decode PixelsRemove message!"); + return false; + } + wippersnapper_pixels_PixelsRemove *msg_remove = + _pixels_model->GetPixelsRemoveMsg(); + + uint16_t pin_data = atoi(msg_remove->pixels_pin_data + 1); + uint16_t idx = GetStrandIndex(pin_data); + if (idx == STRAND_NOT_FOUND) { + WS_DEBUG_PRINTLN("[pixels]: Failed to find strand index!"); + return false; + } + + // Call hardware to deinitialize the strand + _pixel_strands[idx]->RemoveStrand(); + return true; +} + +/*! + @brief Gets the index of a strand by its data pin + @param pin_data + The desired data pin + @returns Desired strand index, or STRAND_NOT_FOUND if not found. +*/ +uint16_t PixelsController::GetStrandIndex(uint16_t pin_data) { + for (uint8_t i = 0; i < _num_strands; i++) { + if (_pixel_strands[i]->GetPinData() == pin_data) { + return i; + } + } + return STRAND_NOT_FOUND; // Sentinel value indicating "not found" +} \ No newline at end of file diff --git a/src/components/pixels/controller.h b/src/components/pixels/controller.h new file mode 100644 index 000000000..3053b7e97 --- /dev/null +++ b/src/components/pixels/controller.h @@ -0,0 +1,50 @@ +/*! + * @file src/components/pixels/controller.h + * + * Controller for the pixels API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PIXELS_CONTROLLER_H +#define WS_PIXELS_CONTROLLER_H +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" + +#define MAX_PIXEL_STRANDS \ + 10 ///< Maximum number of pixel strands connected to a WipperSnapper device +#define STRAND_NOT_FOUND 0xFF ///< Strand not found in the array + +class Wippersnapper_V2; ///< Forward declaration +class PixelsModel; ///< Forward declaration +class PixelsHardware; ///< Forward declaration + +/*! + @brief Routes messages using the pixels.proto API to the + appropriate hardware and model classes, controls and tracks + the state of pixel strands. +*/ +class PixelsController { +public: + PixelsController(); + ~PixelsController(); + bool Handle_Pixels_Add(pb_istream_t *stream); + bool Handle_Pixels_Write(pb_istream_t *stream); + bool Handle_Pixels_Remove(pb_istream_t *stream); + +private: + PixelsModel *_pixels_model = nullptr; ///< Pointer to the model class + PixelsHardware *_pixel_strands[MAX_PIXEL_STRANDS] = { + nullptr}; ///< Pointer to the hardware class + uint8_t _num_strands; ///< Number of pixel strands + uint16_t GetStrandIndex(uint16_t pin_data); // Returns 0xFF if not found +}; +extern Wippersnapper_V2 WsV2; ///< Global V2 instance +#endif // WS_PIXELS_CONTROLLER_H \ No newline at end of file diff --git a/src/components/pixels/hardware.cpp b/src/components/pixels/hardware.cpp new file mode 100644 index 000000000..a88b6f0d7 --- /dev/null +++ b/src/components/pixels/hardware.cpp @@ -0,0 +1,273 @@ +/*! + * @file src/components/pixels/hardware.cpp + * + * Hardware interface for NeoPixel/DotStar strands. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" +#include "Wippersnapper_V2.h" + +/*! + @brief Constructs a new PixelsHardware object +*/ +PixelsHardware::PixelsHardware() { + _type = wippersnapper_pixels_PixelsType_PIXELS_TYPE_UNSPECIFIED; +} + +/*! + @brief Destructs a PixelsHardware object +*/ +PixelsHardware::~PixelsHardware() {} + +/*! + @brief Initializes a new NeoPixel strand on a desired pin. + @param num_pixels + Number of pixels in the strand. + @param pin_data + The desired data pin for the pixel strand. + @param order + The desired color ordering for the pixel strand. + @param brightness + The desired brightness of the pixel strand (0-255). + @returns True if successful, False otherwise. +*/ +bool PixelsHardware::AddNeoPixel(uint16_t num_pixels, uint16_t pin_data, + neoPixelType order, uint8_t brightness) { + if (getStatusNeoPixelPin() == pin_data && WsV2.lockStatusNeoPixelV2) { + WS_DEBUG_PRINTLN("[pixels] Releasing status pixel for use"); + ReleaseStatusPixel(); // Release the status pixel for use + } + + _neopixel = new Adafruit_NeoPixel(num_pixels, pin_data, order); + _neopixel->begin(); + _neopixel->setBrightness(brightness); + _neopixel->clear(); + _neopixel->show(); + // Check if the NeoPixel object was created successfully + if (_neopixel->numPixels() != num_pixels) + return false; + + WS_DEBUG_PRINT("[pixels] Added NeoPixel strand on pin "); + WS_DEBUG_PRINT(pin_data); + return true; +} + +/*! + @brief Initializes a new DotStar strand on a desired pin. + @param num_pixels + Number of pixels in the strand. + @param pin_data + The desired data pin for the pixel strand. + @param pin_clock + The desired clock pin for the pixel strand. + @param order + The desired color ordering for the pixel strand. + @param brightness + The desired brightness of the pixel strand (0-255). + @returns True if successful, False otherwise. +*/ +bool PixelsHardware::AddDotStar(uint16_t num_pixels, uint16_t pin_data, + uint16_t pin_clock, + wippersnapper_pixels_PixelsOrder order, + uint8_t brightness) { + if (getStatusDotStarDataPin() == pin_data && WsV2.lockStatusDotStarV2) + ReleaseStatusPixel(); // Release the status pixel for use + + _dotstar = new Adafruit_DotStar(num_pixels, pin_data, pin_clock, + GetStrandOrderDotStar(order)); + _dotstar->begin(); + _dotstar->setBrightness(brightness); + _dotstar->clear(); + _dotstar->show(); + // Check if the DotStar object was created successfully + if (_dotstar->numPixels() != num_pixels) { + WS_DEBUG_PRINTLN("[pixels] Failed to create DotStar strand!"); + return false; + } + + WS_DEBUG_PRINT("[pixels] Added DotStar strand on pin "); + WS_DEBUG_PRINT(pin_data); + WS_DEBUG_PRINT(" and clock pin "); + WS_DEBUG_PRINT(pin_clock); + return true; +} + +/*! + @brief Initializes a new pixel strand on a desired pin. + @param type + The desired pixel type (NeoPixel or DotStar). + @param order + The desired color ordering for the pixel strand. + @param num_pixels + Number of pixels in the strand. + @param brightness + The desired brightness of the pixel strand (0-255). + @param pin_data + The desired data pin for the pixel strand. + @param pin_clock + The desired clock pin for the pixel strand (for DotStar). + @returns True if successful, False otherwise. +*/ +bool PixelsHardware::AddStrand(wippersnapper_pixels_PixelsType type, + wippersnapper_pixels_PixelsOrder order, + uint32_t num_pixels, uint32_t brightness, + const char *pin_data, const char *pin_clock) { + _type = type; + // Convert the pin string to an integer + uint16_t p_data = atoi(pin_data + 1); + _pin_data = p_data; // Store the pin data for later reference + + if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_NEOPIXEL) { + if (!AddNeoPixel(num_pixels, p_data, GetStrandOrderNeoPixel(order), + (uint8_t)brightness)) { + WS_DEBUG_PRINTLN("[pixels] Failed to create NeoPixel strand!"); + return false; + } + return true; + } else if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_DOTSTAR) { + uint16_t p_clock = atoi(pin_clock + 1); + if (!AddDotStar(num_pixels, p_data, p_clock, order, (uint8_t)brightness)) { + WS_DEBUG_PRINTLN("[pixels] Failed to create DotStar strand!"); + return false; + } + return true; + } else { + WS_DEBUG_PRINTLN("[pixels] Unknown pixel type!"); + return false; + } + return true; +} + +/*! + @brief Sets the color of all pixels in the strand + @param color + 32-bit color value +*/ +void PixelsHardware::FillStrand(uint32_t color) { + // Apply gamma correction to match IO Web + uint32_t color_gamma = ApplyGammaCorrection(color); + if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_NEOPIXEL) { + _neopixel->fill(color_gamma); + _neopixel->show(); + } else if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_DOTSTAR) { + _dotstar->fill(color_gamma); + _dotstar->show(); + } else { + WS_DEBUG_PRINTLN("[pixels] Unknown pixel type!"); + } +} + +/*! + @brief Applies gamma correction to a color value to match IO Web. + @param color + The color value to be corrected + @returns The gamma-corrected color value +*/ +uint32_t PixelsHardware::ApplyGammaCorrection(uint32_t color) { + if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_NEOPIXEL) { + return _neopixel->gamma32(color); + } else if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_DOTSTAR) { + return _dotstar->gamma32(color); + } else { + WS_DEBUG_PRINTLN("[pixels] Unknown pixel type!"); + return color; + } +} + +/*! + @brief Deinitializes a pixel strand +*/ +void PixelsHardware::RemoveStrand() { + if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_NEOPIXEL) { + if (_neopixel != nullptr) { + _neopixel->clear(); + _neopixel->show(); + delete _neopixel; + _neopixel = nullptr; + } + } else if (_type == wippersnapper_pixels_PixelsType_PIXELS_TYPE_DOTSTAR) { + if (_dotstar != nullptr) { + _dotstar->clear(); + _dotstar->show(); + delete _dotstar; + _dotstar = nullptr; + } + } else { + WS_DEBUG_PRINTLN("[pixels] Unknown pixel type!"); + } + // Optionally re-init the status pixel for reuse by app. + if (getStatusNeoPixelPin() == _pin_data && !WsV2.lockStatusNeoPixelV2) { + WS_DEBUG_PRINTLN("[pixels] Re-initializing status pixel"); + initStatusLED(); + } + WS_DEBUG_PRINT("[pixels] Removed pixel strand from pin: "); + WS_DEBUG_PRINTLN(_pin_data); + + _pin_data = 0; // Reset the pin data + _type = wippersnapper_pixels_PixelsType_PIXELS_TYPE_UNSPECIFIED; +} + +/**************************************************************************/ +/** + * @brief Gets the data pin used by the pixel strand + * + * @return The desired data pin + */ +/**************************************************************************/ +uint16_t PixelsHardware::GetPinData() { return _pin_data; } + +/*! + @brief Gets the color ordering for NeoPixel strands + @param order + The desired color ordering for the pixel strand. + @returns The color ordering for NeoPixel strands +*/ +neoPixelType +PixelsHardware::GetStrandOrderNeoPixel(wippersnapper_pixels_PixelsOrder order) { + switch (order) { + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_GRB: + return NEO_GRB + NEO_KHZ800; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_GRBW: + return NEO_GRBW + NEO_KHZ800; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_RGB: + return NEO_RGB + NEO_KHZ800; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_RGBW: + return NEO_RGBW + NEO_KHZ800; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_BRG: + return NEO_BRG + NEO_KHZ800; + default: + return NEO_GRB + NEO_KHZ800; + } +} + +/*! + @brief Gets the color ordering for DotStar strands + @param order + The desired color ordering for the pixel strand. + @returns The color ordering for DotStar strands +*/ +uint8_t +PixelsHardware::GetStrandOrderDotStar(wippersnapper_pixels_PixelsOrder order) { + switch (order) { + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_GRB: + return DOTSTAR_GRB; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_RGB: + return DOTSTAR_RGB; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_BRG: + return DOTSTAR_BRG; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_GBR: + return DOTSTAR_GBR; + case wippersnapper_pixels_PixelsOrder_PIXELS_ORDER_BGR: + return DOTSTAR_BGR; + default: + return DOTSTAR_BRG; + } +} diff --git a/src/components/pixels/hardware.h b/src/components/pixels/hardware.h new file mode 100644 index 000000000..0580c2ab9 --- /dev/null +++ b/src/components/pixels/hardware.h @@ -0,0 +1,48 @@ +/*! + * @file src/components/pixels/hardware.h + * + * Hardware interface for NeoPixel/DotStar strands. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PIXELS_HARDWARE_H +#define WS_PIXELS_HARDWARE_H +#include "Wippersnapper_V2.h" + +/*! + @brief Interface for interacting with NeoPixel or Dotstar + pixel strands +*/ +class PixelsHardware { +public: + PixelsHardware(); + ~PixelsHardware(); + bool AddStrand(wippersnapper_pixels_PixelsType type, + wippersnapper_pixels_PixelsOrder order, uint32_t num_pixels, + uint32_t brightness, const char *pin_data, + const char *pin_clock); + uint16_t GetPinData(); + void FillStrand(uint32_t color); + void RemoveStrand(); + +private: + Adafruit_NeoPixel *_neopixel = nullptr; ///< Used for NeoPixel strands + Adafruit_DotStar *_dotstar = nullptr; ///< Used for DotStar strands + wippersnapper_pixels_PixelsType _type; ///< Holds the type of strand + uint16_t _pin_data; ///< Data pin for the strand + bool AddNeoPixel(uint16_t num_pixels, uint16_t pin_data, neoPixelType order, + uint8_t brightness); + bool AddDotStar(uint16_t num_pixels, uint16_t pin_data, uint16_t pin_clock, + wippersnapper_pixels_PixelsOrder order, uint8_t brightness); + neoPixelType GetStrandOrderNeoPixel(wippersnapper_pixels_PixelsOrder order); + uint8_t GetStrandOrderDotStar(wippersnapper_pixels_PixelsOrder order); + uint32_t ApplyGammaCorrection(uint32_t color); +}; +#endif // WS_PIXELS_HARDWARE_H \ No newline at end of file diff --git a/src/components/pixels/model.cpp b/src/components/pixels/model.cpp new file mode 100644 index 000000000..3184233dd --- /dev/null +++ b/src/components/pixels/model.cpp @@ -0,0 +1,133 @@ +/*! + * @file src/components/pixels/model.cpp + * + * Implementation for the pixels.proto message model. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" +#include "Wippersnapper_V2.h" +#include "nanopb/ws_pb_helpers.h" + +/*! + @brief Constructs a new PixelsModel object +*/ +PixelsModel::PixelsModel() { + memset(&_msg_pixels_add, 0, sizeof(_msg_pixels_add)); + memset(&_msg_pixels_remove, 0, sizeof(_msg_pixels_remove)); + memset(&_msg_pixels_write, 0, sizeof(_msg_pixels_write)); + memset(&_msg_pixels_added, 0, sizeof(_msg_pixels_added)); + // no-op +} + +/*! + @brief Destructs a PixelsModel object +*/ +PixelsModel::~PixelsModel() { + memset(&_msg_pixels_add, 0, sizeof(_msg_pixels_add)); + memset(&_msg_pixels_remove, 0, sizeof(_msg_pixels_remove)); + memset(&_msg_pixels_write, 0, sizeof(_msg_pixels_write)); + memset(&_msg_pixels_added, 0, sizeof(_msg_pixels_added)); +} + +/*! + @brief Decodes a PixelsAdd message from a protocol buffer input stream. + @param stream + Protocol buffer input stream. + @returns True if successful, False otherwise. +*/ +bool PixelsModel::DecodePixelsAdd(pb_istream_t *stream) { + memset(&_msg_pixels_add, 0, sizeof(_msg_pixels_add)); + return pb_decode(stream, wippersnapper_pixels_PixelsAdd_fields, + &_msg_pixels_add); +} + +/*! + @brief Returns a pointer to the PixelsAdd message. + @returns Pointer to the PixelsAdd message object. +*/ +wippersnapper_pixels_PixelsAdd *PixelsModel::GetPixelsAddMsg() { + return &_msg_pixels_add; +} + +/*! + @brief Decodes a PixelsRemove message from a protocol buffer input stream. + @param stream + Protocol buffer input stream. + @returns True if successful, False otherwise. +*/ +bool PixelsModel::DecodePixelsRemove(pb_istream_t *stream) { + memset(&_msg_pixels_remove, 0, sizeof(_msg_pixels_remove)); + return pb_decode(stream, wippersnapper_pixels_PixelsRemove_fields, + &_msg_pixels_remove); +} + +/*! + @brief Returns a pointer to the PixelsRemove message. + @returns Pointer to the PixelsRemove message object. +*/ +wippersnapper_pixels_PixelsRemove *PixelsModel::GetPixelsRemoveMsg() { + return &_msg_pixels_remove; +} + +/*! + @brief Decodes a PixelsWrite message from a protocol buffer input stream. + @param stream + Protocol buffer input stream. + @returns True if successful, False otherwise. +*/ +bool PixelsModel::DecodePixelsWrite(pb_istream_t *stream) { + memset(&_msg_pixels_write, 0, sizeof(_msg_pixels_write)); + WS_DEBUG_PRINTLN("Decoding PixelsWrite message..."); + return pb_decode(stream, wippersnapper_pixels_PixelsWrite_fields, + &_msg_pixels_write); +} + +/*! + @brief Returns a pointer to the PixelsWrite message. + @returns Pointer to the PixelsWrite message object. +*/ +wippersnapper_pixels_PixelsWrite *PixelsModel::GetPixelsWriteMsg() { + return &_msg_pixels_write; +} + +/*! + @brief Encodes a PixelsAdded message. + @param pin_data + The pin the pixels strand is connected to. + @param success + True if strand was successfully initialized, False otherwise. + @returns True if successful, False otherwise. +*/ +bool PixelsModel::EncodePixelsAdded(char *pin_data, bool success) { + // Fill the message + memset(&_msg_pixels_added, 0, sizeof(_msg_pixels_added)); + _msg_pixels_added.is_success = success; + strncpy(_msg_pixels_added.pixels_pin_data, pin_data, + sizeof(_msg_pixels_added.pixels_pin_data)); + + // Encode it! + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_pixels_PixelsAdded_fields, + &_msg_pixels_added)) + return false; + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_pixels_PixelsAdded_fields, + &_msg_pixels_added); +} + +/*! + @brief Returns a pointer to the PixelsAdded message. + @returns Pointer to the PixelsAdded message object. +*/ +wippersnapper_pixels_PixelsAdded *PixelsModel::GetPixelsAddedMsg() { + return &_msg_pixels_added; +} \ No newline at end of file diff --git a/src/components/pixels/model.h b/src/components/pixels/model.h new file mode 100644 index 000000000..69bfef963 --- /dev/null +++ b/src/components/pixels/model.h @@ -0,0 +1,49 @@ +/*! + * @file src/components/pixels/model.h + * + * Model for the pixels.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PIXELS_MODEL_H +#define WS_PIXELS_MODEL_H +#include "Wippersnapper_V2.h" + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from pixels.proto. +*/ +class PixelsModel { +public: + PixelsModel(); + ~PixelsModel(); + // PixelsAdd + bool DecodePixelsAdd(pb_istream_t *stream); + wippersnapper_pixels_PixelsAdd *GetPixelsAddMsg(); + // PixelsRemove + bool DecodePixelsRemove(pb_istream_t *stream); + wippersnapper_pixels_PixelsRemove *GetPixelsRemoveMsg(); + // PixelsWrite + bool DecodePixelsWrite(pb_istream_t *stream); + wippersnapper_pixels_PixelsWrite *GetPixelsWriteMsg(); + // PixelsAdded + bool EncodePixelsAdded(char *pin_data, bool success); + wippersnapper_pixels_PixelsAdded *GetPixelsAddedMsg(); + +private: + wippersnapper_pixels_PixelsAdd _msg_pixels_add; ///< PixelsAdd message object + wippersnapper_pixels_PixelsRemove + _msg_pixels_remove; ///< PixelsRemove message object + wippersnapper_pixels_PixelsWrite + _msg_pixels_write; ///< PixelsWrite message object + wippersnapper_pixels_PixelsAdded + _msg_pixels_added; ///< PixelsAdded message object +}; +#endif // WS_PIXELS_MODEL_H \ No newline at end of file diff --git a/src/components/pwm/controller.cpp b/src/components/pwm/controller.cpp new file mode 100644 index 000000000..4b68c37f6 --- /dev/null +++ b/src/components/pwm/controller.cpp @@ -0,0 +1,191 @@ +/*! + * @file src/components/pwm/controller.cpp + * + * Controller for the pwm API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/*! + @brief Ctor for PWMController. +*/ +PWMController::PWMController() { + _pwm_model = new PWMModel(); + _active_pwm_pins = 0; +} + +/*! + @brief Dtor for PWMController. +*/ +PWMController::~PWMController() { delete _pwm_model; } + +/*! + @brief Handles the PWM_Add message. + @param stream The stream containing the message data. + @return True if the message was handled successfully, false otherwise. +*/ +bool PWMController::Handle_PWM_Add(pb_istream_t *stream) { + bool did_attach; + if (!_pwm_model->DecodePWMAdd(stream)) { + WS_DEBUG_PRINTLN("[pwm] Failed to decode PWMAdd message!"); + return false; + } + wippersnapper_pwm_PWMAdd msg_add = *_pwm_model->GetPWMAddMsg(); + uint8_t pin = atoi(msg_add.pin + 1); + _pwm_hardware[_active_pwm_pins] = new PWMHardware(); + + WS_DEBUG_PRINT("[pwm] Attaching pin: "); + WS_DEBUG_PRINT(msg_add.pin); + did_attach = _pwm_hardware[_active_pwm_pins]->AttachPin( + pin, (uint32_t)msg_add.frequency, (uint32_t)msg_add.resolution); + if (!did_attach) { + WS_DEBUG_PRINTLN("[pwm] Failed to attach pin!"); + delete _pwm_hardware[_active_pwm_pins]; + } else { + _active_pwm_pins++; + } + + // Publish PixelsAdded message to the broker + if (!_pwm_model->EncodePWMAdded(msg_add.pin, did_attach)) { + WS_DEBUG_PRINTLN("[pwm]: Failed to encode PWMAdded message!"); + return false; + } + if (!WsV2.PublishSignal(wippersnapper_signal_DeviceToBroker_pwm_added_tag, + _pwm_model->GetPWMAddedMsg())) { + WS_DEBUG_PRINTLN("[PWM]: Unable to publish PWMAdded message!"); + return false; + } + WS_DEBUG_PRINTLN("...attached!"); + return true; +} + +/*! + @brief Handles the PWM_Remove message. + @param stream The stream containing the message data. + @return True if the message was handled successfully, false otherwise. +*/ +bool PWMController::Handle_PWM_Remove(pb_istream_t *stream) { + if (!_pwm_model->DecodePWMRemove(stream)) { + WS_DEBUG_PRINTLN("[pwm] Error: Failed to decode PWMRemove message!"); + return false; + } + wippersnapper_pwm_PWMRemove msg_remove = *_pwm_model->GetPWMRemoveMsg(); + uint8_t pin = atoi(msg_remove.pin + 1); + int pin_idx = GetPWMHardwareIdx(pin); + if (pin_idx == -1) { + WS_DEBUG_PRINTLN("[pwm] Error: pin not found!"); + return false; + } + + // Detach and free the pin for other uses + WS_DEBUG_PRINT("[pwm] Detaching pin: "); + WS_DEBUG_PRINT(msg_remove.pin); + if (_pwm_hardware[pin_idx] != nullptr) { + bool detach_result = _pwm_hardware[pin_idx]->DetachPin(); + if (!detach_result) { + WS_DEBUG_PRINTLN("[pwm] Error: Failed to detach pin."); + } + delete _pwm_hardware[pin_idx]; + _pwm_hardware[pin_idx] = nullptr; + } else { + WS_DEBUG_PRINTLN("[pwm] Error: Pin not attached!"); + } + + // Reorganize _active_pwm_pins + _active_pwm_pins--; + for (int i = pin_idx; i < _active_pwm_pins; i++) { + _pwm_hardware[i] = _pwm_hardware[i + 1]; + } + _pwm_hardware[_active_pwm_pins] = nullptr; + WS_DEBUG_PRINTLN("...detached!"); + return true; +} + +/*! + @brief Returns the index of the PWM hardware object that corresponds + to the given pin. + @param pin The pin number to search for. + @return The index of the PWM hardware object, or -1 if not found. +*/ +int PWMController::GetPWMHardwareIdx(uint8_t pin) { + for (int i = 0; i < _active_pwm_pins; i++) { + if (_pwm_hardware[i]->GetPin() == pin) { + return i; + } + } + return -1; +} + +/*! + @brief Handles the PWM_Write_DutyCycle message. + @param stream The stream containing the message data. + @return True if the message was handled successfully, false otherwise. +*/ +bool PWMController::Handle_PWM_Write_DutyCycle(pb_istream_t *stream) { + if (!_pwm_model->DecodePWMWriteDutyCycle(stream)) { + WS_DEBUG_PRINTLN( + "[pwm] Error: Failed to decode PWMWriteDutyCycle message!"); + return false; + } + + wippersnapper_pwm_PWMWriteDutyCycle msg_write_duty_cycle = + *_pwm_model->GetPWMWriteDutyCycleMsg(); + uint8_t pin = atoi(msg_write_duty_cycle.pin + 1); + int pin_idx = GetPWMHardwareIdx(pin); + if (pin_idx == -1) { + WS_DEBUG_PRINTLN("[pwm] Error: pin not found!"); + return false; + } + + // Write the duty cycle to the pin + if (!_pwm_hardware[pin_idx]->WriteDutyCycle( + msg_write_duty_cycle.duty_cycle)) { + WS_DEBUG_PRINTLN("[pwm] Error: Failed to write duty cycle!"); + return false; + } + WS_DEBUG_PRINTLN("[pwm] Wrote duty cycle: "); + WS_DEBUG_PRINT(msg_write_duty_cycle.duty_cycle); + WS_DEBUG_PRINTLN(" to pin: "); + WS_DEBUG_PRINT(msg_write_duty_cycle.pin); + return true; +} + +/*! + @brief Handles the PWM_Write_Frequency message. + @param stream The stream containing the message data. + @return True if the message was handled successfully, false otherwise. +*/ +bool PWMController::Handle_PWM_Write_Frequency(pb_istream_t *stream) { + if (!_pwm_model->DecodePWMWriteFrequency(stream)) { + WS_DEBUG_PRINTLN( + "[pwm] Error: Failed to decode PWMWriteFrequency message!"); + return false; + } + wippersnapper_pwm_PWMWriteFrequency msg_write_frequency = + *_pwm_model->GetPWMWriteFrequencyMsg(); + uint8_t pin = atoi(msg_write_frequency.pin + 1); + int pin_idx = GetPWMHardwareIdx(pin); + if (pin_idx == -1) { + WS_DEBUG_PRINTLN("[pwm] Error: pin not found!"); + return false; + } + + if (_pwm_hardware[pin_idx]->WriteTone(msg_write_frequency.frequency) != + msg_write_frequency.frequency) { + WS_DEBUG_PRINTLN("[pwm] Error: Failed to write frequency!"); + return false; + } + WS_DEBUG_PRINTLN("[pwm] Wrote frequency: "); + WS_DEBUG_PRINT(msg_write_frequency.frequency); + WS_DEBUG_PRINTLN(" to pin: "); + WS_DEBUG_PRINT(msg_write_frequency.pin); + return true; +} \ No newline at end of file diff --git a/src/components/pwm/controller.h b/src/components/pwm/controller.h new file mode 100644 index 000000000..f088879d8 --- /dev/null +++ b/src/components/pwm/controller.h @@ -0,0 +1,47 @@ +/*! + * @file src/components/pwm/controller.h + * + * Controller for the pwm API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PWM_CONTROLLER_H +#define WS_PWM_CONTROLLER_H +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" +#define MAX_PWM_PINS 25 ///< Maximum number of PWM pins supported + +class Wippersnapper_V2; // Forward declaration +class PWMModel; // Forward declaration +class PWMHardware; // Forward declaration + +/*! + @brief Routes messages using the pwm.proto API to the + appropriate hardware and model classes, controls and tracks + the state of the hardware's PWM pins. +*/ +class PWMController { +public: + PWMController(); + ~PWMController(); + bool Handle_PWM_Add(pb_istream_t *stream); + bool Handle_PWM_Write_DutyCycle(pb_istream_t *stream); + bool Handle_PWM_Write_Frequency(pb_istream_t *stream); + bool Handle_PWM_Remove(pb_istream_t *stream); + int GetPWMHardwareIdx(uint8_t pin); + +private: + PWMModel *_pwm_model; + PWMHardware *_pwm_hardware[MAX_PWM_PINS] = {nullptr}; + int _active_pwm_pins; +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_PWM_CONTROLLER_H \ No newline at end of file diff --git a/src/components/pwm/hardware.cpp b/src/components/pwm/hardware.cpp new file mode 100644 index 000000000..01030292e --- /dev/null +++ b/src/components/pwm/hardware.cpp @@ -0,0 +1,155 @@ +/*! + * @file src/components/pwm/hardware.cpp + * + * Hardware for the pwm.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" + +/*! + @brief Ctor for PWMHardware +*/ +PWMHardware::PWMHardware() { _is_attached = false; } + +/*! + @brief Dtor for PWMHardware +*/ +PWMHardware::~PWMHardware() { + if (_is_attached) + DetachPin(); +} + +/*! + @brief Attach a pin to the PWM hardware + @param pin The pin to attach + @param frequency The frequency of the PWM signal + @param resolution The resolution of the PWM signal + @return true if the pin was successfully attached, false otherwise +*/ +bool PWMHardware::AttachPin(uint8_t pin, uint32_t frequency, + uint32_t resolution) { +#ifdef ARDUINO_ARCH_ESP32 + _is_attached = ledcAttach(pin, frequency, resolution); +#else + _is_attached = true; +#endif + + if (_is_attached) { + _pin = pin; + _frequency = frequency; + _resolution = resolution; + _duty_cycle = 0; + } + + return _is_attached; +} + +/*! + @brief Detaches a PWM pin and frees it for use. + @return true if the PWM pin was successfully detached, false otherwise. +*/ +bool PWMHardware::DetachPin() { + if (!_is_attached) { + WS_DEBUG_PRINTLN("[pwm] Pin not attached!"); + return false; + } + bool did_detach = false; +#ifdef ARDUINO_ARCH_ESP32 + did_detach = ledcDetach(_pin); +#else + digitalWrite(_pin, LOW); // "Disable" the pin's output + did_detach = true; +#endif + + _is_attached = false; // always mark as false, for tracking + return did_detach; +} + +/*! + @brief Writes a duty cycle to a PWM pin with a fixed frequency + of 5kHz and 8-bit resolution. + @param duty The desired duty cycle to write to the pin. + @return true if the duty cycle was successfully written, false otherwise +*/ +bool PWMHardware::WriteDutyCycle(uint32_t duty) { + if (!_is_attached) { + WS_DEBUG_PRINTLN("[pwm] Pin not attached!"); + return false; + } + bool did_write = false; +#if defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) && defined(STATUS_LED_PIN) + // Adafruit Feather ESP8266's analogWrite() gets inverted since the builtin + // LED is reverse-wired + _duty_cycle = 255 - duty; +#else + _duty_cycle = duty; +#endif + +#ifdef ARDUINO_ARCH_ESP32 + did_write = analogWrite(_duty_cycle); +#else + analogWrite(_pin, duty); + did_write = true; +#endif + + return true; +} + +/*! + @brief Writes a frequency to a PWM pin with a fixed duty cycle. + @param freq The desired frequency to write to the pin. + @return The frequency that was written to the pin. +*/ +uint32_t PWMHardware::WriteTone(uint32_t freq) { + if (!_is_attached) { + WS_DEBUG_PRINTLN("[pwm] Pin not attached!"); + return false; + } + + uint32_t rc = 0; +#ifdef ARDUINO_ARCH_ESP32 + rc = ledcWriteTone(_pin, freq); +#else + tone(_pin, freq); + rc = freq; +#endif + + return rc; +} + +/*! + @brief Returns the pin number of the PWM pin + @return The logical pin number of the PWM pin +*/ +uint8_t PWMHardware::GetPin() { return _pin; } + +// LEDC API Wrappers +#ifdef ARDUINO_ARCH_ESP32 +/*! + @brief Mocks the Arduino analogWrite() function for the Arduino-ESP32 + LEDC API + @param value The value to write (0-255) + @return true if the value was successfully written, false otherwise +*/ +bool PWMHardware::analogWrite(uint32_t value) { + // clamp + if (value > 255 || value < 0) { + WS_DEBUG_PRINTLN("[pwm] Value out of range!"); + return false; + } + + // Calculate duty cycle for the `value` + uint32_t dutyCycle = + (uint32_t)(((double)_resolution / 255.0) * min(value, (uint32_t)255)); + return ledcWrite(_pin, dutyCycle); +} + +#endif // ARDUINO_ARCH_ESP32 \ No newline at end of file diff --git a/src/components/pwm/hardware.h b/src/components/pwm/hardware.h new file mode 100644 index 000000000..a6f250c26 --- /dev/null +++ b/src/components/pwm/hardware.h @@ -0,0 +1,47 @@ +/*! + * @file src/components/pwm/hardware.h + * + * Hardware for the pwm.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PWM_HARDWARE_H +#define WS_PWM_HARDWARE_H +#include "Wippersnapper_V2.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-ledc.h" +#include "esp_err.h" +#endif + +/*! + @brief Interface for interacting with hardware's PWM API. +*/ +class PWMHardware { +public: + PWMHardware(); + ~PWMHardware(); + bool AttachPin(uint8_t pin, uint32_t frequency, uint32_t resolution); + bool DetachPin(); + bool WriteDutyCycle(uint32_t duty); + uint32_t WriteTone(uint32_t freq); + uint8_t GetPin(); + +// Abstractions for LEDC API +#ifdef ARDUINO_ARCH_ESP32 + bool analogWrite(uint32_t value); +#endif +private: + bool _is_attached; + uint8_t _pin; + uint32_t _frequency; + uint32_t _resolution; + uint32_t _duty_cycle; +}; +#endif // WS_PWM_HARDWARE_H diff --git a/src/components/pwm/model.cpp b/src/components/pwm/model.cpp new file mode 100644 index 000000000..f8b7249d5 --- /dev/null +++ b/src/components/pwm/model.cpp @@ -0,0 +1,142 @@ +/*! + * @file src/components/pwm/model.cpp + * + * Model for the pwm.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" + +/*! + @brief Ctor for PWMModel. +*/ +PWMModel::PWMModel() { + memset(&_msg_pwm_add, 0, sizeof(_msg_pwm_add)); + memset(&_msg_pwm_added, 0, sizeof(_msg_pwm_added)); + memset(&_msg_pwm_remove, 0, sizeof(_msg_pwm_remove)); + memset(&_msg_pwm_write_duty_cycle, 0, sizeof(_msg_pwm_write_duty_cycle)); + memset(&_msg_pwm_write_frequency, 0, sizeof(_msg_pwm_write_frequency)); +} + +/*! + @brief Dtor for PWMModel. +*/ +PWMModel::~PWMModel() { + memset(&_msg_pwm_add, 0, sizeof(_msg_pwm_add)); + memset(&_msg_pwm_added, 0, sizeof(_msg_pwm_added)); + memset(&_msg_pwm_remove, 0, sizeof(_msg_pwm_remove)); + memset(&_msg_pwm_write_duty_cycle, 0, sizeof(_msg_pwm_write_duty_cycle)); + memset(&_msg_pwm_write_frequency, 0, sizeof(_msg_pwm_write_frequency)); +} + +/*! + @brief Decodes a PWMAdd message from an input stream. + @param stream The stream to decode from. + @return true if successful, false otherwise. +*/ +bool PWMModel::DecodePWMAdd(pb_istream_t *stream) { + memset(&_msg_pwm_add, 0, sizeof(_msg_pwm_add)); + return pb_decode(stream, wippersnapper_pwm_PWMAdd_fields, &_msg_pwm_add); +} + +/*! + @brief Returns a pointer to the PWMAdd message. + @return Pointer to the PWMAdd message. +*/ +wippersnapper_pwm_PWMAdd *PWMModel::GetPWMAddMsg() { return &_msg_pwm_add; } + +/*! + @brief Encodes a PWMAdded message with the given pin name and attach + status. + @param pin_name The name of the pin. + @param did_attach True if the pin was successfully attached, false + otherwise. + @return true if successful, false otherwise. +*/ +bool PWMModel::EncodePWMAdded(char *pin_name, bool did_attach) { + // Fill the message + memset(&_msg_pwm_added, 0, sizeof(_msg_pwm_added)); + _msg_pwm_added.did_attach = did_attach; + strncpy(_msg_pwm_added.pin, pin_name, sizeof(_msg_pwm_added.pin)); + // Encode it! + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_pwm_PWMAdded_fields, + &_msg_pwm_added)) + return false; + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_pwm_PWMAdded_fields, + &_msg_pwm_added); +} + +/*! + @brief Returns a pointer to the PWMAdded message. + @return Pointer to the PWMAdded message. +*/ +wippersnapper_pwm_PWMAdded *PWMModel::GetPWMAddedMsg() { + return &_msg_pwm_added; +} + +/*! + @brief Decodes a PWMRemove message from an input stream. + @param stream The stream to decode from. + @return true if successful, false otherwise. +*/ +bool PWMModel::DecodePWMRemove(pb_istream_t *stream) { + memset(&_msg_pwm_remove, 0, sizeof(_msg_pwm_remove)); + return pb_decode(stream, wippersnapper_pwm_PWMRemove_fields, + &_msg_pwm_remove); +} + +/*! + @brief Returns a pointer to the PWMRemove message. + @return Pointer to the PWMRemove message. +*/ +wippersnapper_pwm_PWMRemove *PWMModel::GetPWMRemoveMsg() { + return &_msg_pwm_remove; +} + +/*! + @brief Decodes a PWMWriteDutyCycle message from an input stream. + @param stream The stream to decode from. + @return true if successful, false otherwise. +*/ +bool PWMModel::DecodePWMWriteDutyCycle(pb_istream_t *stream) { + memset(&_msg_pwm_write_duty_cycle, 0, sizeof(_msg_pwm_write_duty_cycle)); + return pb_decode(stream, wippersnapper_pwm_PWMWriteDutyCycle_fields, + &_msg_pwm_write_duty_cycle); +} + +/*! + @brief Returns a pointer to the PWMWriteDutyCycle message. + @return Pointer to the PWMWriteDutyCycle message. +*/ +wippersnapper_pwm_PWMWriteDutyCycle *PWMModel::GetPWMWriteDutyCycleMsg() { + return &_msg_pwm_write_duty_cycle; +} + +/*! + @brief Decodes a PWMWriteFrequency message from an input stream. + @param stream The stream to decode from. + @return true if successful, false otherwise. +*/ +bool PWMModel::DecodePWMWriteFrequency(pb_istream_t *stream) { + memset(&_msg_pwm_write_frequency, 0, sizeof(_msg_pwm_write_frequency)); + return pb_decode(stream, wippersnapper_pwm_PWMWriteFrequency_fields, + &_msg_pwm_write_frequency); +} + +/*! + @brief Returns a pointer to the PWMWriteFrequency message. + @return Pointer to the PWMWriteFrequency message. +*/ +wippersnapper_pwm_PWMWriteFrequency *PWMModel::GetPWMWriteFrequencyMsg() { + return &_msg_pwm_write_frequency; +} diff --git a/src/components/pwm/model.h b/src/components/pwm/model.h new file mode 100644 index 000000000..3f951d21c --- /dev/null +++ b/src/components/pwm/model.h @@ -0,0 +1,47 @@ +/*! + * @file src/components/pwm/model.h + * + * Model for the pwm.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PWM_MODEL_H +#define WS_PWM_MODEL_H +#include "Wippersnapper_V2.h" + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from pwm.proto. +*/ +class PWMModel { +public: + PWMModel(); + ~PWMModel(); + bool DecodePWMAdd(pb_istream_t *stream); + wippersnapper_pwm_PWMAdd *GetPWMAddMsg(); + bool EncodePWMAdded(char *pin_name, bool did_attach); + wippersnapper_pwm_PWMAdded *GetPWMAddedMsg(); + bool DecodePWMRemove(pb_istream_t *stream); + wippersnapper_pwm_PWMRemove *GetPWMRemoveMsg(); + bool DecodePWMWriteDutyCycle(pb_istream_t *stream); + wippersnapper_pwm_PWMWriteDutyCycle *GetPWMWriteDutyCycleMsg(); + bool DecodePWMWriteFrequency(pb_istream_t *stream); + wippersnapper_pwm_PWMWriteFrequency *GetPWMWriteFrequencyMsg(); + +private: + wippersnapper_pwm_PWMAdd _msg_pwm_add; ///< PWMAdd message object + wippersnapper_pwm_PWMAdded _msg_pwm_added; ///< PWMAdded message object + wippersnapper_pwm_PWMRemove _msg_pwm_remove; ///< PWMRemove message object + wippersnapper_pwm_PWMWriteDutyCycle + _msg_pwm_write_duty_cycle; ///< PWMWriteDutyCycle message object + wippersnapper_pwm_PWMWriteFrequency + _msg_pwm_write_frequency; ///< PWMWriteFrequency message object +}; +#endif // WS_PWM_MODEL_H \ No newline at end of file diff --git a/src/components/sensor/model.cpp b/src/components/sensor/model.cpp index 85f1cb3d3..95b748200 100644 --- a/src/components/sensor/model.cpp +++ b/src/components/sensor/model.cpp @@ -1,5 +1,5 @@ /*! - * @file model.cpp + * @file src/components/sensor/model.cpp * * Model for the sensor.proto message. * @@ -14,21 +14,18 @@ */ #include "model.h" -/***********************************************************************/ /*! @brief SensorModel constructor */ -/***********************************************************************/ SensorModel::SensorModel() { - _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_zero; + memset(&_msg_sensor_event, 0, sizeof(_msg_sensor_event)); + // no-op } -/***********************************************************************/ /*! @brief SensorModel destructor */ -/***********************************************************************/ SensorModel::~SensorModel() { // Zero-out the SensorEvent message - _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_zero; + memset(&_msg_sensor_event, 0, sizeof(_msg_sensor_event)); } \ No newline at end of file diff --git a/src/components/sensor/model.h b/src/components/sensor/model.h index aa014c48c..cd862cbda 100644 --- a/src/components/sensor/model.h +++ b/src/components/sensor/model.h @@ -1,5 +1,5 @@ /*! - * @file model.h + * @file src/components/sensor/model.h * * Model for the sensor.proto message. * @@ -17,12 +17,10 @@ #include "Wippersnapper_V2.h" #include "protos/sensor.pb.h" -/**************************************************************************/ /*! @brief Provides an interface for creating, encoding, and parsing messages from sensor.proto. */ -/**************************************************************************/ class SensorModel { public: SensorModel(); diff --git a/src/components/servo/controller.cpp b/src/components/servo/controller.cpp new file mode 100644 index 000000000..33caec5c7 --- /dev/null +++ b/src/components/servo/controller.cpp @@ -0,0 +1,183 @@ +/*! + * @file src/components/servo/controller.cpp + * + * Controller for the servo API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/*! + @brief Constructor +*/ +ServoController::ServoController() { + _servo_model = new ServoModel(); + _active_servo_pins = 0; +} + +/*! + @brief Destructor +*/ +ServoController::~ServoController() { + // De-initialize all servos + for (int i = 0; i < _active_servo_pins; i++) { + if (_servo_hardware[i] != nullptr) { + delete _servo_hardware[i]; + _servo_hardware[i] = nullptr; + } + } + + if (_servo_model != nullptr) { + delete _servo_model; + _servo_model = nullptr; + } +} + +/*! + @brief Publishes a ServoAdded message to the broker + @param servo_pin + Pin number of the servo + @param did_attach + True if the servo was attached successfully, False otherwise + @returns True if successful, False otherwise +*/ +bool ServoController::PublishServoAddedMsg( + const char *servo_pin, bool did_attach, + wippersnapper_servo_ServoAdd *msg_add) { + _servo_model->EncodeServoAdded(msg_add->servo_pin, did_attach); + if (!WsV2.PublishSignal(wippersnapper_signal_DeviceToBroker_servo_added_tag, + _servo_model->GetServoAddedMsg())) { + WS_DEBUG_PRINTLN("[servo] Error: Failed publishing a ServoAdded message!"); + return false; + } + return true; +} + +/*! + @brief Handles a ServoAdd message + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoController::Handle_Servo_Add(pb_istream_t *stream) { + bool did_attach; + if (_active_servo_pins >= MAX_SERVOS) { + WS_DEBUG_PRINTLN("[servo] Error: Maximum number of servos reached!"); + PublishServoAddedMsg("PIN_UNKNOWN", false, _servo_model->GetServoAddMsg()); + return false; + } + + if (!_servo_model->DecodeServoAdd(stream)) { + WS_DEBUG_PRINTLN("[servo] Error: Failed to decode ServoAdd message!"); + PublishServoAddedMsg("PIN_UNKNOWN", false, _servo_model->GetServoAddMsg()); + return false; + } + + wippersnapper_servo_ServoAdd *msg_add = _servo_model->GetServoAddMsg(); + uint8_t pin = atoi(msg_add->servo_pin + 1); + _servo_hardware[_active_servo_pins] = new ServoHardware( + pin, (int)msg_add->min_pulse_width, (int)msg_add->max_pulse_width, + (int)msg_add->servo_freq); + // Attempt to attach the servo to the pin + did_attach = _servo_hardware[_active_servo_pins]->ServoAttach(); + + // Write the pulse width to the servo + if (did_attach) { + _servo_hardware[_active_servo_pins]->ServoWrite( + (int)msg_add->min_pulse_width); + WS_DEBUG_PRINT("[servo] Servo attached to pin: "); + WS_DEBUG_PRINTLN(msg_add->servo_pin); + _active_servo_pins++; + } else { + WS_DEBUG_PRINT("[servo] Error: Failed to attach servo to pin !"); + WS_DEBUG_PRINT(msg_add->servo_pin); + delete _servo_hardware[_active_servo_pins]; + _servo_hardware[_active_servo_pins] = nullptr; + } + + // Publish ServoAdded message to IO + if (!PublishServoAddedMsg("", false, _servo_model->GetServoAddMsg())) + return false; + return true; +} + +/*! + @brief Handles a ServoWrite message + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoController::Handle_Servo_Write(pb_istream_t *stream) { + if (!_servo_model->DecodeServoWrite(stream)) { + WS_DEBUG_PRINTLN("[servo] Error: Failed to decode ServoWrite message!"); + return false; + } + wippersnapper_servo_ServoWrite *msg_write = _servo_model->GetServoWriteMsg(); + uint8_t pin = atoi(msg_write->servo_pin + 1); + int servo_idx = GetServoIndex(pin); + if (servo_idx == -1) { + WS_DEBUG_PRINTLN("[servo] Error: Servo pin not found!"); + return false; + } + // Write the pulse width to the servo + _servo_hardware[servo_idx]->ServoWrite(msg_write->pulse_width); + return true; +} + +/*! + @brief Handles a ServoRemove message + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoController::Handle_Servo_Remove(pb_istream_t *stream) { + if (_active_servo_pins <= 0) { + WS_DEBUG_PRINTLN("[servo] Error: No active servos!"); + return false; + } + + if (!_servo_model->DecodeServoRemove(stream)) { + WS_DEBUG_PRINTLN("[servo] Error: Failed to decode ServoRemove message!"); + return false; + } + wippersnapper_servo_ServoRemove *msg_remove = + _servo_model->GetServoRemoveMsg(); + uint8_t pin = atoi(msg_remove->servo_pin + 1); + int servo_idx = GetServoIndex(pin); + if (servo_idx == -1) { + WS_DEBUG_PRINTLN("[servo] Error: Servo pin not found!"); + return false; + } + + // The destructor of ServoHardware will handle proper detachment + delete _servo_hardware[servo_idx]; + _servo_hardware[servo_idx] = nullptr; + + // Shift _active_servo_pins down + for (int i = servo_idx; i < _active_servo_pins - 1; i++) { + _servo_hardware[i] = _servo_hardware[i + 1]; + _servo_hardware[i + 1] = nullptr; + } + _servo_hardware[_active_servo_pins - 1] = nullptr; + _active_servo_pins--; + + WS_DEBUG_PRINT("[servo] Servo removed from pin: "); + WS_DEBUG_PRINTLN(msg_remove->servo_pin); + return true; +} + +int ServoController::GetServoIndex(uint8_t pin) { + for (int i = 0; i < _active_servo_pins; i++) { + if (_servo_hardware[i]->GetPin() == pin) { + return i; + } + } + return -1; +} \ No newline at end of file diff --git a/src/components/servo/controller.h b/src/components/servo/controller.h new file mode 100644 index 000000000..ebcdef5d1 --- /dev/null +++ b/src/components/servo/controller.h @@ -0,0 +1,55 @@ +/*! + * @file src/components/servo/controller.h + * + * Controller for the servo API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_SERVO_CONTROLLER_H +#define WS_SERVO_CONTROLLER_H +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" + +#ifdef ARDUINO_ARCH_RP2040 +#define MAX_SERVOS \ + 8 ///< Maximum number of servo objects for RP2040, + ///< https://arduino-pico.readthedocs.io/en/latest/servo.html +#else +#define MAX_SERVOS 16 ///< Maximum number of servo objects +#endif + +class Wippersnapper_V2; // Forward declaration +class ServoModel; // Forward declaration +class ServoHardware; // Forward declaration + +/*! + @brief Routes messages using the servo.proto API to the + appropriate hardware and model classes, controls and tracks + the state of the hardware's Servo pins. +*/ +class ServoController { +public: + ServoController(); + ~ServoController(); + bool Handle_Servo_Add(pb_istream_t *stream); + bool Handle_Servo_Write(pb_istream_t *stream); + bool Handle_Servo_Remove(pb_istream_t *stream); + +private: + int GetServoIndex(uint8_t pin); + bool PublishServoAddedMsg(const char *servo_pin, bool did_attach, + wippersnapper_servo_ServoAdd *msg_add); + ServoModel *_servo_model; + ServoHardware *_servo_hardware[MAX_SERVOS] = {nullptr}; + int _active_servo_pins; ///< Number of active servo pins +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_SERVO_CONTROLLER_H \ No newline at end of file diff --git a/src/components/servo/hardware.cpp b/src/components/servo/hardware.cpp new file mode 100644 index 000000000..f7dd56051 --- /dev/null +++ b/src/components/servo/hardware.cpp @@ -0,0 +1,198 @@ +/*! + * @file src/components/servo/hardware.cpp + * + * Hardware for the servo.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" + +/*! + @brief Constructs a ServoHardware object + @param pin + The GPIO pin to attach the servo to + @param min_pulse_width + The minimum pulse width, in microseconds + @param max_pulse_width + The maximum pulse width, in microseconds + @param frequency + The frequency of the PWM signal, in Hz (50Hz is sent from IO) +*/ +ServoHardware::ServoHardware(int pin, int min_pulse_width, int max_pulse_width, + int frequency) { + _pin = pin; + _min_pulse_width = min_pulse_width; + _max_pulse_width = max_pulse_width; + _frequency = frequency; + +#ifndef ARDUINO_ARCH_ESP32 + _servo = new Servo(); +#else + _is_attached = false; +#endif +} + +/*! + @brief Detaches the servo from the pin and frees the pin for + other uses. +*/ +ServoHardware::~ServoHardware() { + if (!ServoDetach()) { + WS_DEBUG_PRINTLN("[servo] Error: Failed to detach servo!"); + } +} + +/*! + @brief Detaches the servo from the pin and frees the pin for + other uses. + @returns true if successful, false otherwise +*/ +bool ServoHardware::ServoDetach() { +#ifdef ARDUINO_ARCH_ESP32 + if (!attached()) { + WS_DEBUG_PRINTLN("[servo] Detach Error: Servo not attached!"); + return false; + } + detach(); +#else + if (_servo == nullptr || !_servo->attached()) { + WS_DEBUG_PRINTLN("[servo] Detach Error: Servo not attached!"); + return false; + } + _servo->detach(); + delete _servo; + _servo = nullptr; +#endif + return true; +} + +/*! + @brief Attempts to attach a servo to a GPIO pin. + @returns true if successful, false otherwise. +*/ +bool ServoHardware::ServoAttach() { + uint16_t rc; + +// Attach the servo to the pin +#ifdef ARDUINO_ARCH_ESP32 + if (!ledcAttach(_pin, _frequency, LEDC_TIMER_WIDTH)) { + rc = ERROR_SERVO_ATTACH; + } else { + WS_DEBUG_PRINTLN("[servo:hw:L99] Servo attached to pin"); + rc = 1; + _is_attached = true; + } +#else + if (_servo == nullptr) { + WS_DEBUG_PRINTLN("[servo] Attach Error: Servo not initialized!"); + return false; + } + rc = _servo->attach(_pin, _min_pulse_width, _max_pulse_width); +#endif + + if (rc == ERROR_SERVO_ATTACH) { + WS_DEBUG_PRINT("[servo] Error: Failed to attach servo to pin: "); + WS_DEBUG_PRINTLN(_pin); + return false; + } + + return true; +} + +/*! + @brief Returns the logical pin number of the servo + @returns The logical pin number of the servo +*/ +uint8_t ServoHardware::GetPin() { return _pin; } + +/*! + @brief Clamps the pulse width to the min/max range + @param value + The value to clamp + @returns The clamped value +*/ +int ServoHardware::ClampPulseWidth(int value) { + if (value < _min_pulse_width) { + value = _min_pulse_width; + } + if (value > _max_pulse_width) { + value = _max_pulse_width; + } + return value; +} + +/*! + @brief Writes a value to the servo pin + @param value + The value to write to the servo pin +*/ +void ServoHardware::ServoWrite(int value) { +#ifdef ARDUINO_ARCH_ESP32 + if (!attached()) { + WS_DEBUG_PRINTLN("[servo] Error: Servo not attached!"); + return; + } + writeMicroseconds(value); +#else + if (_servo == nullptr || !_servo->attached()) { + WS_DEBUG_PRINTLN("[servo] Error: Servo not attached!"); + return; + } + value = ClampPulseWidth(value); + _servo->writeMicroseconds(value); + WS_DEBUG_PRINT("[servo] Set Pulse Width: "); + WS_DEBUG_PRINT(value); + WS_DEBUG_PRINT(" µs on pin: "); + WS_DEBUG_PRINT(_pin); +#endif +} + +#ifdef ARDUINO_ARCH_ESP32 +/*! + @brief Mocks writeMicroseconds() call in arduino/servo api for + ESP32x's LEDC manager. + @param value + The value to write to the servo pin. +*/ +void ServoHardware::writeMicroseconds(int value) { + // Clamp the value to the min/max range + value = ClampPulseWidth(value); + + // Formula from ESP32Servo library + // https://github.com/madhephaestus/ESP32Servo/blob/master/src/ESP32Servo.cpp + // count = (pulse_high_width / (pulse_period/2**timer_width)) + // 50Hz servo = 20ms pulse_period + uint32_t count = + ((double)value / ((double)20000 / (double)pow(2, LEDC_TIMER_WIDTH))); + if (!ledcWrite(_pin, count)) + WS_DEBUG_PRINTLN("[servo] Error: Failed to write to servo pin!"); + + WS_DEBUG_PRINT("[servo] Set Pulse Width: "); + WS_DEBUG_PRINT(value); + WS_DEBUG_PRINT(" uS on pin: "); + WS_DEBUG_PRINT(_pin); +} + +/*! + @brief Detaches the servo from the LEDC manager and frees the pin for + other uses. +*/ +void ServoHardware::detach() { + if (!attached()) + return; + _is_attached = ledcDetach(_pin); +} + +/*! + @brief Returns true if the servo is attached to the pin + @returns true if the servo is attached to the pin, false otherwise +*/ +bool ServoHardware::attached() { return _is_attached; } +#endif \ No newline at end of file diff --git a/src/components/servo/hardware.h b/src/components/servo/hardware.h new file mode 100644 index 000000000..3ac442ae3 --- /dev/null +++ b/src/components/servo/hardware.h @@ -0,0 +1,62 @@ +/*! + * @file src/components/servo/hardware.h + * + * Hardware for the servo.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_SERVO_HARDWARE_H +#define WS_SERVO_HARDWARE_H +#include "Wippersnapper_V2.h" + +#ifdef ARDUINO_ARCH_ESP32 +#include "esp32-hal-ledc.h" +#include "esp_err.h" +#define LEDC_TIMER_WIDTH \ + SOC_LEDC_TIMER_BIT_WIDTH ///< Dynamically scale bit width for each ESP32-x + ///< Arch. +#else +#include +#endif + +#define ERROR_SERVO_ATTACH 255 ///< Error code for servo attach failure + +/*! + @brief Interface for interacting with hardware's Servo API. +*/ +class ServoHardware { +public: + ServoHardware(int pin, int min_pulse_width, int max_pulse_width, + int frequency); + ~ServoHardware(); + bool ServoAttach(); + void ServoWrite(int value); + uint8_t GetPin(); + +private: + bool ServoDetach(); + int ClampPulseWidth(int value); +#ifdef ARDUINO_ARCH_ESP32 + // Mocks Servo library API for ESP32x's LEDC manager + // https://github.com/arduino-libraries/Servo/blob/master/src/Servo.h + bool attached(); + void detach(); + void writeMicroseconds(int value); + bool _is_attached; +#endif +#ifndef ARDUINO_ARCH_ESP32 + Servo *_servo = nullptr; +#endif + uint8_t _pin; + int _max_pulse_width; + int _min_pulse_width; + int _frequency; +}; +#endif // WS_SERVO_HARDWARE_H \ No newline at end of file diff --git a/src/components/servo/model.cpp b/src/components/servo/model.cpp new file mode 100644 index 000000000..987cf2a85 --- /dev/null +++ b/src/components/servo/model.cpp @@ -0,0 +1,129 @@ +/*! + * @file src/components/servo/model.cpp + * + * Model for the servo.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" + +/*! + @brief Constructor +*/ +ServoModel::ServoModel() { + memset(&_msg_servo_add, 0, sizeof(_msg_servo_add)); + memset(&_msg_servo_added, 0, sizeof(_msg_servo_added)); + memset(&_msg_servo_remove, 0, sizeof(_msg_servo_remove)); + memset(&_msg_servo_write, 0, sizeof(_msg_servo_write)); +} + +/*! + @brief Destructor +*/ +ServoModel::~ServoModel() { + memset(&_msg_servo_add, 0, sizeof(_msg_servo_add)); + memset(&_msg_servo_added, 0, sizeof(_msg_servo_added)); + memset(&_msg_servo_remove, 0, sizeof(_msg_servo_remove)); + memset(&_msg_servo_write, 0, sizeof(_msg_servo_write)); +} + +/*! + @brief Decodes a ServoAdd message from a pb_istream_t + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoModel::DecodeServoAdd(pb_istream_t *stream) { + memset(&_msg_servo_add, 0, sizeof(_msg_servo_add)); + return pb_decode(stream, wippersnapper_servo_ServoAdd_fields, + &_msg_servo_add); +} + +/*! + @brief Returns a pointer to the ServoAdd message + @returns Pointer to ServoAdd message +*/ +wippersnapper_servo_ServoAdd *ServoModel::GetServoAddMsg() { + return &_msg_servo_add; +} + +/*! + @brief Encodes a ServoAdded message + @param pin_name + Name of the pin + @param did_attach + True if a servo was attached to the pin successfully, + False otherwise + @returns True if successful, False otherwise +*/ +bool ServoModel::EncodeServoAdded(char *pin_name, bool did_attach) { + // Fill the message + memset(&_msg_servo_added, 0, sizeof(_msg_servo_added)); + _msg_servo_added.attach_success = did_attach; + strncpy(_msg_servo_added.servo_pin, pin_name, + sizeof(_msg_servo_added.servo_pin) - 1); + // Encode it! + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_servo_ServoAdded_fields, + &_msg_servo_added)) + return false; + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_servo_ServoAdded_fields, + &_msg_servo_added); +} + +/*! + @brief Returns a pointer to the ServoAdded message + @returns Pointer to ServoAdded message +*/ +wippersnapper_servo_ServoAdded *ServoModel::GetServoAddedMsg() { + return &_msg_servo_added; +} + +/*! + @brief Decodes a ServoRemove message from a pb_istream_t + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoModel::DecodeServoRemove(pb_istream_t *stream) { + memset(&_msg_servo_remove, 0, sizeof(_msg_servo_remove)); + return pb_decode(stream, wippersnapper_servo_ServoRemove_fields, + &_msg_servo_remove); +} + +/*! + @brief Returns a pointer to the ServoRemove message + @returns Pointer to ServoRemove message +*/ +wippersnapper_servo_ServoRemove *ServoModel::GetServoRemoveMsg() { + return &_msg_servo_remove; +} + +/*! + @brief Decodes a ServoWrite message from a pb_istream_t + @param stream + pb_istream_t to decode from + @returns True if successful, False otherwise +*/ +bool ServoModel::DecodeServoWrite(pb_istream_t *stream) { + memset(&_msg_servo_write, 0, sizeof(_msg_servo_write)); + return pb_decode(stream, wippersnapper_servo_ServoWrite_fields, + &_msg_servo_write); +} + +/*! + @brief Returns a pointer to the ServoWrite message + @returns Pointer to ServoWrite message +*/ +wippersnapper_servo_ServoWrite *ServoModel::GetServoWriteMsg() { + return &_msg_servo_write; +} \ No newline at end of file diff --git a/src/components/servo/model.h b/src/components/servo/model.h new file mode 100644 index 000000000..7ea0c4cf5 --- /dev/null +++ b/src/components/servo/model.h @@ -0,0 +1,45 @@ +/*! + * @file src/components/servo/model.h + * + * Model for the servo.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_SERVO_MODEL_H +#define WS_SERVO_MODEL_H +#include "Wippersnapper_V2.h" + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from servo.proto. +*/ +class ServoModel { +public: + ServoModel(); + ~ServoModel(); + bool DecodeServoAdd(pb_istream_t *stream); + wippersnapper_servo_ServoAdd *GetServoAddMsg(); + bool EncodeServoAdded(char *pin_name, bool did_attach); + wippersnapper_servo_ServoAdded *GetServoAddedMsg(); + bool DecodeServoRemove(pb_istream_t *stream); + wippersnapper_servo_ServoRemove *GetServoRemoveMsg(); + bool DecodeServoWrite(pb_istream_t *stream); + wippersnapper_servo_ServoWrite *GetServoWriteMsg(); + +private: + wippersnapper_servo_ServoAdd _msg_servo_add; ///< ServoAdd message object + wippersnapper_servo_ServoAdded + _msg_servo_added; ///< ServoAdded message object + wippersnapper_servo_ServoRemove + _msg_servo_remove; ///< ServoRemove message object + wippersnapper_servo_ServoWrite + _msg_servo_write; ///< ServoWrite message object +}; +#endif // WS_SERVO_MODEL_H \ No newline at end of file diff --git a/src/components/statusLED/Wippersnapper_StatusLED.cpp b/src/components/statusLED/Wippersnapper_StatusLED.cpp index 06d6251bf..d48a59f36 100644 --- a/src/components/statusLED/Wippersnapper_StatusLED.cpp +++ b/src/components/statusLED/Wippersnapper_StatusLED.cpp @@ -28,11 +28,9 @@ Adafruit_DotStar *statusPixelDotStar = STATUS_DOTSTAR_PIN_CLK, DOTSTAR_BRG); #endif -/****************************************************************************/ /*! @brief Initializes board-specific status LED pixel */ -/****************************************************************************/ void initStatusLED() { #ifdef USE_STATUS_NEOPIXEL if (WsV2.lockStatusNeoPixelV2 == false) { @@ -94,12 +92,10 @@ void initStatusLED() { #endif } -/****************************************************************************/ /*! @brief De-initializes the status LED and releases pin. */ -/****************************************************************************/ -void releaseStatusLED() { +void ReleaseStatusPixel() { #ifdef USE_STATUS_NEOPIXEL delete statusPixel; // Deallocate Adafruit_NeoPixel object, set data pin back // to INPUT. @@ -120,13 +116,11 @@ void releaseStatusLED() { #endif } -/****************************************************************************/ /*! @brief Sets the status pixel's brightness @param brightness Desired pixel brightness, from 0.0 (0%) to 1.0 (100%). */ -/****************************************************************************/ void setStatusLEDBrightness(float brightness) { // Clamp brightness between 0.0-1.0 (0% to 100%) if (brightness > 1.0) @@ -136,13 +130,11 @@ void setStatusLEDBrightness(float brightness) { WsV2.status_pixel_brightnessV2 = brightness; } -/****************************************************************************/ /*! @brief Sets a status RGB LED's color @param color Desired RGB color. */ -/****************************************************************************/ void setStatusLEDColor(uint32_t color) { #ifdef USE_STATUS_NEOPIXEL if (!WsV2.lockStatusNeoPixelV2) @@ -195,7 +187,6 @@ void setStatusLEDColor(uint32_t color) { #endif } -/****************************************************************************/ /*! @brief Sets a status RGB LED's color @param color @@ -203,7 +194,6 @@ void setStatusLEDColor(uint32_t color) { @param brightness Brightness level, as an integer */ -/****************************************************************************/ void setStatusLEDColor(uint32_t color, int brightness) { #ifdef USE_STATUS_NEOPIXEL if (!WsV2.lockStatusNeoPixelV2) @@ -256,12 +246,10 @@ void setStatusLEDColor(uint32_t color, int brightness) { #endif } -/****************************************************************************/ /*! @brief Retrieve the pin number used for NeoPixel data output. @return Arduino pin number (-2 if not set). */ -/****************************************************************************/ int16_t getStatusNeoPixelPin() { #ifdef USE_STATUS_NEOPIXEL return statusPixel->getPin(); @@ -269,12 +257,10 @@ int16_t getStatusNeoPixelPin() { return -2; } -/****************************************************************************/ /*! @brief Retrieve the pin number used for DotStar data output. @return Arduino pin number (-2 if not set). */ -/****************************************************************************/ int16_t getStatusDotStarDataPin() { #ifdef USE_STATUS_DOTSTAR return STATUS_DOTSTAR_PIN_DATA; @@ -282,7 +268,6 @@ int16_t getStatusDotStarDataPin() { return -2; } -/****************************************************************************/ /*! @brief Fades the status LED. @param color @@ -290,7 +275,6 @@ int16_t getStatusDotStarDataPin() { @param numFades The amount of time to fade/pulse the status LED. */ -/****************************************************************************/ void statusLEDFade(uint32_t color, int numFades = 3) { // don't fade if our pixel is off if (WsV2.status_pixel_brightnessV2 == 0.0) @@ -319,14 +303,12 @@ void statusLEDFade(uint32_t color, int numFades = 3) { #endif } -/****************************************************************************/ /*! @brief Converts the a ws_led_status_t status state to color. @param statusState Hardware's status state. @return Color as a uint32_t */ -/****************************************************************************/ uint32_t ledStatusStateToColor(ws_led_status_t statusState) { uint32_t ledColor; switch (statusState) { @@ -355,14 +337,12 @@ uint32_t ledStatusStateToColor(ws_led_status_t statusState) { return ledColor; } -/****************************************************************************/ /*! @brief Sets the status LED to a specific color depending on the hardware's state. @param statusState Hardware's status state. */ -/****************************************************************************/ void statusLEDSolid(ws_led_status_t statusState = WS_LED_STATUS_ERROR_RUNTIME) { #ifdef USE_STATUS_LED if (!WsV2.lockStatusLEDV2) @@ -378,14 +358,12 @@ void statusLEDSolid(ws_led_status_t statusState = WS_LED_STATUS_ERROR_RUNTIME) { setStatusLEDColor(ledColor); } -/****************************************************************************/ /*! @brief Blinks a status LED a specific color depending on the hardware's state. @param statusState Hardware's status state. */ -/****************************************************************************/ void statusLEDBlink(ws_led_status_t statusState) { #ifdef USE_STATUS_LED if (!WsV2.lockStatusLEDV2) diff --git a/src/components/statusLED/Wippersnapper_StatusLED.h b/src/components/statusLED/Wippersnapper_StatusLED.h index 1b8dbf326..1454a5c9d 100644 --- a/src/components/statusLED/Wippersnapper_StatusLED.h +++ b/src/components/statusLED/Wippersnapper_StatusLED.h @@ -60,7 +60,7 @@ typedef enum ws_led_status_t { // Status LED void initStatusLED(); -void releaseStatusLED(); +void ReleaseStatusPixel(); int16_t getStatusNeoPixelPin(); int16_t getStatusDotStarDataPin(); uint32_t ledStatusStateToColor(ws_led_status_t statusState); diff --git a/src/components/uart/controller.cpp b/src/components/uart/controller.cpp new file mode 100644 index 000000000..9e464ea37 --- /dev/null +++ b/src/components/uart/controller.cpp @@ -0,0 +1,322 @@ +/*! + * @file src/components/uart/controller.cpp + * + * Controller for WipperSnapper's UART component, bridges between the UART.proto + * API, the model, and the hardware layer. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/*! + @brief Constructs a new UARTController. +*/ +UARTController::UARTController() { _uart_model = new UARTModel(); } + +/*! + @brief Destructs the UARTController. +*/ +UARTController::~UARTController() { + if (_uart_model != nullptr) { + delete _uart_model; // cleanup model + _uart_model = nullptr; + } +} + +/*! + @brief Handles a UartAdd message. + @param stream + Pointer to a pb_istream_t object. + @return True if the message was handled successfully, False otherwise. +*/ +bool UARTController::Handle_UartAdd(pb_istream_t *stream) { + // Attempt to decode the UartAdd message + WS_DEBUG_PRINTLN("[uart] Decoding UartAdd message..."); + if (!_uart_model->DecodeUartAdd(stream)) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to decode UartAdd message!"); + return false; + } + WS_DEBUG_PRINTLN("[uart] UartAdd message decoded successfully!"); + // Get ref. to the UartAdd message within the model + wippersnapper_uart_UartAdd *add_msg = _uart_model->GetUartAddMsg(); + // TODO: fix the id field, currently it is a callback and should be a string. + if (!add_msg->has_cfg_serial && !add_msg->has_cfg_device) { + WS_DEBUG_PRINTLN( + "[uart] ERROR: No configuration provided for UART device!"); + return false; + } + + // Configure a UART hardware instance using the provided serial configuration + // TODO: Have we already configured this UART hardware instance?! + WS_DEBUG_PRINTLN("[uart] Configuring UART hardware instance..."); + wippersnapper_uart_UartSerialConfig cfg_serial = add_msg->cfg_serial; + // Print out the UART configuration for debugging + WS_DEBUG_PRINTLN("[uart] UART Serial Configuration:"); + WS_DEBUG_PRINT("[uart] RX Pin: "); + WS_DEBUG_PRINTLN(cfg_serial.pin_rx); + WS_DEBUG_PRINT("[uart] TX Pin: "); + WS_DEBUG_PRINTLN(cfg_serial.pin_tx); + WS_DEBUG_PRINT("[uart] UART Number: "); + WS_DEBUG_PRINTLN(cfg_serial.uart_nbr); + WS_DEBUG_PRINT("[uart] Baud Rate: "); + WS_DEBUG_PRINTLN(cfg_serial.baud_rate); + WS_DEBUG_PRINT("[uart] Packet Format: "); + WS_DEBUG_PRINTLN(cfg_serial.format); + WS_DEBUG_PRINT("[uart] Timeout: "); + WS_DEBUG_PRINTLN(cfg_serial.timeout); + WS_DEBUG_PRINT("[uart] Use Software Serial: "); + WS_DEBUG_PRINTLN(cfg_serial.use_sw_serial ? "True" : "False"); + UARTHardware *uart_hardware = new UARTHardware(cfg_serial); + if (!uart_hardware->ConfigureSerial()) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to configure UART hardware!"); + delete uart_hardware; // cleanup + return false; + } + // Add the newly configured hardware instance to the controller's vector of + // UART ports + _uart_ports.push_back(uart_hardware); + WS_DEBUG_PRINTLN("[uart] UART hardware instance configured successfully!"); + + // Create a new UartDevice "driver" on the hardware layer (UARTHardware) + // TODO: Have we already added this UART device?! + drvUartBase *uart_driver = nullptr; + GPSController *drv_uart_gps = nullptr; + bool is_gps_drv = false; + wippersnapper_uart_UartDeviceConfig cfg_device = add_msg->cfg_device; + WS_DEBUG_PRINT("[UART DEBUG] cfg_device.device_type: "); + WS_DEBUG_PRINTLN(cfg_device.device_type); + switch (cfg_device.device_type) { + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_UNSPECIFIED: + WS_DEBUG_PRINTLN("[uart] ERROR: Unspecified device type!"); + return false; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_INPUT: + // check if device_type is "us100" + if (strcmp(cfg_device.device_id, "us100") == 0) { + WS_DEBUG_PRINTLN("[uart] Adding US-100 device.."); + // Create a new US-100 driver instance + WS_DEBUG_PRINT("[uart] Adding US-100 Driver..."); + uart_driver = new drvUartUs100(uart_hardware->GetHardwareSerial(), + cfg_device.device_id, cfg_serial.uart_nbr); + uart_driver->ConfigureDriver(cfg_device); + uart_driver->EnableSensorEvents( + cfg_device.config.generic_uart_input.sensor_types, + cfg_device.config.generic_uart_input.sensor_types_count); + uart_driver->SetSensorPeriod(cfg_device.config.generic_uart_input.period); + WS_DEBUG_PRINT("added!"); + } else { + WS_DEBUG_PRINTLN( + "[uart] Specified generic device type is not implemented!"); + delete uart_hardware; // cleanup + return false; + } + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_OUTPUT: + WS_DEBUG_PRINTLN("[uart] Generic Output device type not implemented!"); + delete uart_hardware; // cleanup + return false; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GPS: + WS_DEBUG_PRINTLN("[uart] Adding GPS device.."); + if (!WsV2._gps_controller->AddGPS(uart_hardware->GetHardwareSerial(), + uart_hardware->GetBaudRate(), + &cfg_device.config.gps)) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to initialize GPS device!"); + delete uart_hardware; // cleanup + return false; + } + WS_DEBUG_PRINTLN("[uart] Added GPS driver!"); + is_gps_drv = true; // mark as GPS driver + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_PM25AQI: + WS_DEBUG_PRINTLN("[uart] Adding PM2.5 AQI device.."); + // Create a new PM2.5 AQI driver instance + // TODO: Support SoftwareSerial as well, currently only HardwareSerial + uart_driver = new drvUartPm25(uart_hardware->GetHardwareSerial(), + cfg_device.device_id, cfg_serial.uart_nbr); + uart_driver->ConfigureDriver(cfg_device); + uart_driver->EnableSensorEvents( + cfg_device.config.pm25aqi.sensor_types, + cfg_device.config.pm25aqi.sensor_types_count); + uart_driver->SetSensorPeriod(cfg_device.config.pm25aqi.period); + WS_DEBUG_PRINTLN("added!"); + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_TM22XX: + WS_DEBUG_PRINTLN("[uart] TM22XX device type not implemented!"); + delete uart_hardware; // cleanup + return false; + default: + WS_DEBUG_PRINTLN("[uart] ERROR: Unknown device type!"); + delete uart_hardware; // cleanup + return false; + } + + // Attempt to initialize the UART driver + bool did_begin = false; + WS_DEBUG_PRINTLN("[uart] Initializing UART driver..."); + if (!is_gps_drv) { + WS_DEBUG_PRINTLN("[uart] STD UART drv..."); + did_begin = uart_driver->begin(); + WS_DEBUG_PRINTLN("[uart] STD UART driver initialized!"); + if (did_begin) { + WS_DEBUG_PRINTLN("[uart] UART driver initialized successfully!"); + _uart_drivers.push_back(uart_driver); + } else { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to initialize UART driver!"); + delete uart_driver; // cleanup + return false; + } + } + + // Are we in offline mode? + if (WsV2._sdCardV2->isModeOffline()) + return true; // Don't publish to IO in offline mode + + // Encode and publish out to Adafruit IO + WS_DEBUG_PRINTLN("[uart] Encoding UartAdded message..."); + if (!_uart_model->EncodeUartAdded(uart_hardware->GetBusNumber(), + cfg_device.device_type, + cfg_device.device_id, did_begin)) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to encode UartAdded message!"); + return false; + } + WS_DEBUG_PRINTLN("[uart] UartAdded message encoded successfully!"); + + WS_DEBUG_PRINTLN("[uart] Publishing UartAdded message to IO..."); + // TODO: Unsure why this is causing a crash on GPS, figure out later + // Currently commented out to prevent crashes + /* if + (!WsV2.PublishSignal(wippersnapper_signal_DeviceToBroker_uart_added_tag, + _uart_model->GetUartAddedMsg())) { + WS_DEBUG_PRINTLN("[uart] ERROR: Unable to publish UartAdded message to + IO!"); return false; + } */ + WS_DEBUG_PRINTLN("[uart] UartAdded message published successfully!"); + + return true; +} + +/*! + @brief Handles a UartRemove message. + @param stream + Pointer to a pb_istream_t object. + @return True if the message was handled successfully, False otherwise. +*/ +bool UARTController::Handle_UartRemove(pb_istream_t *stream) { + if (!_uart_model->DecodeUartDeviceRemove(stream)) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to decode UartRemove message!"); + return false; + } + // Get the UartRemove message from the model + wippersnapper_uart_UartRemove *remove_msg = _uart_model->GetUartRemoveMsg(); + + // Find the corresponding hardware instance for the UART port + uint32_t port_num = remove_msg->uart_nbr; + for (auto it = _uart_ports.begin(); it != _uart_ports.end(); ++it) { + if ((*it)->GetBusNumber() == port_num) { + // Find the corresponding driver for the uart port + for (auto driver_it = _uart_drivers.begin(); + driver_it != _uart_drivers.end(); ++driver_it) { + if ((*driver_it)->GetPortNum() == port_num && + (*driver_it)->GetDeviceType() == remove_msg->type && + strcmp((*driver_it)->GetName(), remove_msg->device_id) == 0) { + // Driver found, remove it + WS_DEBUG_PRINT("[uart] Removing UART driver: " + + String((*driver_it)->GetName()) + "..."); + delete *driver_it; + _uart_drivers.erase(driver_it); + WS_DEBUG_PRINTLN("Removed!"); + return true; + } + } + } + } + + return false; +} + +/*! + @brief Handles a UartWrite message. + @param stream + Pointer to a pb_istream_t object. + @return True if the message was handled successfully, False otherwise. +*/ +bool UARTController::Handle_UartWrite(pb_istream_t *stream) { + // TODO: Needs implementation + // TO ADDRESS: + // 1) Hardware is the uart_nbr + // 2) type is the driver type + // 3) device_id is the unique identifier for the UART device, stored by the + // driver + return false; +} + +/*! + @brief Polls the UARTController for updates, processes any pending events + from the UART drivers and sends data to Adafruit IO. +*/ +void UARTController::update() { + if (_uart_drivers.empty()) + return; // bail-out + + for (drvUartBase *drv : _uart_drivers) { + size_t num_sensors = drv->GetNumSensors(); + if (num_sensors == 0) { + WS_DEBUG_PRINTLN("[uart] No sensors available for driver: " + + String(drv->GetName())); + continue; // No sensors to poll, skip this driver + } + + // Did driver's read period elapse yet? + ulong cur_time = millis(); + if (cur_time - drv->GetSensorPeriodPrv() < drv->GetSensorPeriod()) + continue; + + // Read the events from the drivers + _uart_model->ClearUartInputEventMsg(); + _uart_model->ConfigureUartInputEventMsg( + drv->GetPortNum(), drv->GetDeviceType(), drv->GetName()); + for (size_t i = 0; i < num_sensors; i++) { + // Attempt to read from the driver + sensors_event_t event = {0}; + if (!drv->GetSensorEvent(drv->_sensors[i], &event)) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to read sensor!"); + continue; // skip this sensor if reading failed + } + // Fill the event with the sensor data + _uart_model->AddUartInputEvent(event, drv->_sensors[i]); + } + + // Encode the UART input event message + if (!_uart_model->EncodeUartInputEvent()) { + WS_DEBUG_PRINTLN("[uart] ERROR: Failed to encode UartInputEvent!"); + continue; // skip this driver if encoding failed + } + // Process the UartInputEvent message + if (!WsV2._sdCardV2->isModeOffline()) { + WS_DEBUG_PRINT("[uart] Publishing UartInputEvent to IO..."); + if (!WsV2.PublishSignal( + wippersnapper_signal_DeviceToBroker_uart_input_event_tag, + _uart_model->GetUartInputEventMsg())) { + WS_DEBUG_PRINTLN( + "[uart] ERROR: Unable to publish UartInputEvent to IO!"); + } + WS_DEBUG_PRINTLN("OK!"); + } else { + WS_DEBUG_PRINT("[uart] Logging UartInputEvent to SD card..."); + if (!WsV2._sdCardV2->LogEventUart(_uart_model->GetUartInputEventMsg())) { + WS_DEBUG_PRINTLN( + "[uart] ERROR: Unable to log UartInputEvent to SD card!"); + } + WS_DEBUG_PRINTLN("OK!"); + } + // Update the timestamp + cur_time = millis(); + drv->SetSensorPeriodPrv(cur_time); + } +} \ No newline at end of file diff --git a/src/components/uart/controller.h b/src/components/uart/controller.h new file mode 100644 index 000000000..7d95f3d55 --- /dev/null +++ b/src/components/uart/controller.h @@ -0,0 +1,52 @@ +/*! + * @file src/components/uart/controller.h + * + * Controller for WipperSnapper's UART component, bridges between the UART.proto + * API, the model, and the hardware layer. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_CONTROLLER_H +#define WS_UART_CONTROLLER_H +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" +// drivers +#include "drivers/drvUartBase.h" +#include "drivers/drvUartPm25.h" +#include "drivers/drvUartUs100.h" + +class Wippersnapper_V2; ///< Forward declaration +class UARTModel; ///< Forward declaration +class UARTHardware; ///< Forward declaration + +/*! + @brief Routes messages between the uart.proto API and the hardware. +*/ +class UARTController { +public: + UARTController(); + ~UARTController(); + // Routing + bool Handle_UartAdd(pb_istream_t *stream); + bool Handle_UartRemove(pb_istream_t *stream); + bool Handle_UartWrite(pb_istream_t *stream); + // Polling + void update(); + +private: + UARTModel *_uart_model; ///< UART model + std::vector + _uart_ports; ///< Vector of UART hardware instances + std::vector + _uart_drivers; ///< Vector of UART device drivers (eg: PM2.5, etc.) +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_UART_CONTROLLER_H \ No newline at end of file diff --git a/src/components/uart/drivers/drvUartBase.h b/src/components/uart/drivers/drvUartBase.h new file mode 100644 index 000000000..95bf21e6d --- /dev/null +++ b/src/components/uart/drivers/drvUartBase.h @@ -0,0 +1,340 @@ +/*! + * @file drvUartBase.h + * + * Base implementation for UART device drivers. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_UART_BASE_H +#define DRV_UART_BASE_H +#include "../hardware.h" +#include +#include +#include +#include + +/*! + @brief Base class for UART Drivers. +*/ +class drvUartBase { + +public: + /*! + @brief Instantiates a UART device. + @param HardwareSerial + Pointer to a HardwareSerial instance. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + instance. + */ + drvUartBase(HardwareSerial *hw_serial, const char *driver_name, + uint32_t port_num) { + _hw_serial = hw_serial; + _is_software_serial = false; + strncpy(_name, driver_name, sizeof(_name) - 1); + _name[sizeof(_name) - 1] = '\0'; + _port_num = port_num; + } + +#if HAS_SW_SERIAL + /*! + @brief Instantiates a UART device. + @param SoftwareSerial + Pointer to a SoftwareSerial instance. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + */ + drvUartBase(SoftwareSerial *sw_serial, const char *driver_name, + uint32_t port_num) { + _sw_serial = sw_serial; + _is_software_serial = true; + strncpy(_name, driver_name, sizeof(_name) - 1); + _name[sizeof(_name) - 1] = '\0'; + _port_num = port_num; + } +#endif // HAS_SW_SERIAL + + /*! + @brief Destructor for a UART device. + */ + virtual ~drvUartBase() { + // This is a base class, nothing to clean up! + } + + /*! + @brief Configures the UART driver with device-specific settings. + @param cfg_device + The configuration settings for the UART device. + */ + void ConfigureDriver(wippersnapper_uart_UartDeviceConfig &cfg_device) { + _device_type = cfg_device.device_type; + + switch (_device_type) { + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_INPUT: + // Handle Generic Input device configuration + // TODO! + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_OUTPUT: + // Handle Generic Output device configuration + // TODO! + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GPS: + // Handle GPS device configuration + // TODO! + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_PM25AQI: + // Handle PM2.5 AQI device configuration + WS_DEBUG_PRINTLN("[uart] Configuring PM2.5 AQI device..."); + break; + case wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_TM22XX: + // Handle TM22XX device configuration + // TODO! + break; + default: + WS_DEBUG_PRINTLN("[uart] ERROR: Unknown device type!"); + return; // bail if the device type is unknown + } + } + + /*! + @brief Gets the UART device type. + @returns The UART device type. + */ + wippersnapper_uart_UartDeviceType GetDeviceType() { return _device_type; } + + /*! + @brief Gets the name of the UART driver. + @returns The name of the UART driver. + */ + uint32_t GetPortNum() const { return _port_num; } + + /*! + @brief Gets the name of the UART driver. + @returns The name of the UART driver. + */ + const char *GetName() const { return _name; } + + /*! + @brief Gets the type of Serial used by this driver. + @returns True if this driver uses SoftwareSerial, False if it uses + HardwareSerial. + */ + bool IsSoftwareSerial() const { return _is_software_serial; } + + /*! + @brief Initializes the UART device. + @returns True if initialized successfully, False otherwise. + */ + virtual bool begin() = 0; + + /*! + @brief Configures an UART-input device's sensors for reading. + @param sensor_types + Pointer to an array of SensorType objects. + @param sensor_types_count + The number of active sensors to read from the device. + */ + void EnableSensorEvents(wippersnapper_sensor_SensorType *sensor_types, + size_t sensor_types_count) { + _sensors_count = sensor_types_count; + for (size_t i = 0; i < _sensors_count; i++) { + _sensors[i] = sensor_types[i]; + } + } + + /*! + @brief Gets the number of enabled sensors. + @returns The number of enabled sensors. + */ + size_t GetNumSensors() { return _sensors_count; } + + /*! + @brief Sets the sensor's period and converts from seconds to + milliseconds. + @param period The period for the sensor to return values within, in + seconds. + */ + void SetSensorPeriod(float period) { + if (period < 0) { + _sensor_period = 0; + return; + } + _sensor_period = (unsigned long)(period * 1000.0f); + } + + /*! + @brief Sets the sensor's previous period and converts from seconds + to milliseconds. + @param period The period for the sensor to return values within, in + seconds. + */ + void SetSensorPeriodPrv(ulong period) { _sensor_period_prv = period; } + + /*! + @brief Gets the sensor's period. + @returns The sensor's period, in milliseconds. + */ + ulong GetSensorPeriod() { return _sensor_period; } + + /*! + @brief Gets the sensor's previous period. + @returns The sensor's previous period, in milliseconds. + */ + ulong GetSensorPeriodPrv() { return _sensor_period_prv; } + + /*! + @brief Reads a sensor's event from the i2c driver. + @param sensor_type + The sensor type to read. + @param sensors_event + Pointer to an Adafruit_Sensor event. + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + bool GetSensorEvent(wippersnapper_sensor_SensorType sensor_type, + sensors_event_t *sensors_event) { + auto it = SensorEventHandlers.find(sensor_type); + if (it == SensorEventHandlers.end()) + return false; // Could not find sensor_type + return it->second(sensors_event); + } + + /*! + @brief Base implementation - Reads a object pm10 std. sensor and + converts the reading into the expected SI unit. + @param pm10StdEvent + pm10 std. sensor reading, in ppm. + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + virtual bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { return false; } + + /*! + @brief Base implementation - Reads a object pm25 std. sensor and + converts the reading into the expected SI unit. + @param pm25StdEvent + pm25 std. sensor reading, in ppm. + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + virtual bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { return false; } + + /*! + @brief Base implementation - Reads a object pm100 std. sensor and + converts the reading into the expected SI unit. + @param pm100StdEvent + pm100 std. sensor reading, in ppm. + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + virtual bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { + return false; + } + + /*! + @brief Base implementation - Reads an ambient temperature sensor (°C). + Expects value to return in the proper SI unit. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + virtual bool getEventAmbientTemp(sensors_event_t *tempEvent) { return false; } + + /*! + @brief Helper function to obtain a sensor's ambient temperature value + in °F. Requires `getEventAmbientTemp()` to be fully + implemented by a driver. + @param AmbientTempFEvent + The ambient temperature value, in °F. + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + virtual bool getEventAmbientTempF(sensors_event_t *AmbientTempFEvent) { + // obtain ambient temp. in °C + if (!getEventAmbientTemp(AmbientTempFEvent)) { + return false; + } + // convert event from °C to °F + AmbientTempFEvent->temperature = + (AmbientTempFEvent->temperature * 9.0) / 5.0 + 32; + return true; + } + + /*! + @brief Gets a sensor's Raw value. + @param rawEvent + The Raw value. + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + virtual bool getEventRaw(sensors_event_t *rawEvent) { return false; } + + /*! + @brief Function type for sensor event handlers + @param sensors_event_t* + Pointer to the sensor event structure to be filled + @returns True if event was successfully read, False otherwise + */ + using fnGetEvent = std::function; + + // Maps SensorType to function calls + std::map SensorEventHandlers = { + {wippersnapper_sensor_SensorType_SENSOR_TYPE_PM10_STD, + [this](sensors_event_t *event) -> bool { + return this->getEventPM10_STD(event); + }}, + {wippersnapper_sensor_SensorType_SENSOR_TYPE_PM25_STD, + [this](sensors_event_t *event) -> bool { + return this->getEventPM25_STD(event); + }}, + {wippersnapper_sensor_SensorType_SENSOR_TYPE_PM100_STD, + [this](sensors_event_t *event) -> bool { + return this->getEventPM100_STD(event); + }}, + {wippersnapper_sensor_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT, + [this](sensors_event_t *event) -> bool { + return this->getEventAmbientTempF(event); + }}, + {wippersnapper_sensor_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE, + [this](sensors_event_t *event) -> bool { + return this->getEventAmbientTemp(event); + }}, + {wippersnapper_sensor_SensorType_SENSOR_TYPE_RAW, + [this](sensors_event_t *event) -> bool { + return this->getEventRaw(event); + }}}; ///< SensorType to function call map + + wippersnapper_sensor_SensorType + _sensors[15]; ///< The sensors attached to the device. +protected: + HardwareSerial *_hw_serial; ///< Pointer to a HardwareSerial instance +#if HAS_SW_SERIAL + SoftwareSerial *_sw_serial; ///< Pointer to a SoftwareSerial instance +#endif // HAS_SW_SERIAL + wippersnapper_uart_UartDeviceType _device_type; ///< The UART device type + char _name[15]; ///< The device's name + uint32_t _port_num = 0; ///< The port number for the UART device + bool _is_software_serial = + false; ///< Indicates if this driver uses SoftwareSerial + // Sensor API - UART INPUT + // TODO: This is OK here for testing but should be moved to a base class and + // refactored for drvBase and drvUartBase + + size_t _sensors_count; ///< Number of sensors on the device + ulong _sensor_period; ///< The sensor's period, in milliseconds. + ulong _sensor_period_prv; ///< The sensor's previous period, in milliseconds. +}; +#endif // DRV_UART_BASE_H \ No newline at end of file diff --git a/src/components/uart/drivers/drvUartPm25.h b/src/components/uart/drivers/drvUartPm25.h new file mode 100644 index 000000000..8d4affa48 --- /dev/null +++ b/src/components/uart/drivers/drvUartPm25.h @@ -0,0 +1,151 @@ +/*! + * @file drvUartPm25.h + * + * Interface for the Adafruit_PM25AQI UART driver. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_UART_PM25_H +#define DRV_UART_PM25_H +#include "Adafruit_PM25AQI.h" +#include "drvUartBase.h" + +/*! + @brief Provides an interface for the Adafruit_PM25AQI library over + UART. +*/ +class drvUartPm25 : public drvUartBase { + +public: + /*! + @brief Instantiates a UART device. + @param hw_serial + Pointer to a HardwareSerial instance. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + instance. + */ + drvUartPm25(HardwareSerial *hw_serial, const char *driver_name, + uint32_t port_num) + : drvUartBase(hw_serial, driver_name, port_num) { + // Handled by drvUartBase constructor + } + +#if HAS_SW_SERIAL + /*! + @brief Instantiates a UART device. + @param sw_serial + Pointer to a SoftwareSerial instance. + @param device_type + The type of device connected to the UART port. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + instance. +*/ + drvUartPm25(SoftwareSerial *sw_serial, const char *driver_name, + uint32_t port_num) + : drvUartBase(sw_serial, driver_name, port_num) { + // Handled by drvUartBase constructor + } +#endif // HAS_SW_SERIAL + + /*! + @brief Destructor for a UART device. + */ + ~drvUartPm25() { + if (_pm25) { + delete _pm25; // Clean up the Adafruit_PM25AQI instance + _pm25 = nullptr; + } + } + + /*! + @brief Initializes the Adafruit_PM25AQI instance. + @returns True if initialized successfully, False otherwise. + */ + bool begin() override { +#if HAS_SW_SERIAL + if (_sw_serial == nullptr) + return false; +#else + if (_hw_serial == nullptr) + return false; +#endif // HAS_SW_SERIAL + + _pm25 = new Adafruit_PM25AQI(); + if (_pm25 == nullptr) + return false; + + delay(1000); // Wait for the sensor to boot + +#if HAS_SW_SERIAL + return _pm25->begin_UART(_sw_serial); +#else + return _pm25->begin_UART(_hw_serial); +#endif // HAS_SW_SERIAL + } + + /*! + @brief Gets the PM25 sensor's PM1.0 STD reading. + @param pm10StdEvent + Adafruit Sensor event for PM1.0 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + bool getEventPM10_STD(sensors_event_t *pm10StdEvent) { + PM25_AQI_Data data; + if (!_pm25->read(&data)) + return false; // couldn't read data + + pm10StdEvent->pm10_std = (float)data.pm10_standard; + return true; + } + + /*! + @brief Gets the PM25 sensor's PM2.5 STD reading. + @param pm25StdEvent + Adafruit Sensor event for PM2.5 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + bool getEventPM25_STD(sensors_event_t *pm25StdEvent) { + PM25_AQI_Data data; + if (!_pm25->read(&data)) + return false; // couldn't read data + + pm25StdEvent->pm25_std = (float)data.pm25_standard; + return true; + } + + /*! + @brief Gets the PM25 sensor's PM10.0 STD reading. + @param pm100StdEvent + Adafruit Sensor event for PM10.0 + @returns True if the sensor value was obtained successfully, False + otherwise. + */ + bool getEventPM100_STD(sensors_event_t *pm100StdEvent) { + PM25_AQI_Data data; + if (!_pm25->read(&data)) + return false; // couldn't read data + + pm100StdEvent->pm100_std = (float)data.pm100_standard; + return true; + } + +protected: + Adafruit_PM25AQI *_pm25 = nullptr; ///< Instance of the Adafruit_PM25AQI class +}; +#endif // DRV_UART_PM25_H \ No newline at end of file diff --git a/src/components/uart/drivers/drvUartUs100.h b/src/components/uart/drivers/drvUartUs100.h new file mode 100644 index 000000000..299185db1 --- /dev/null +++ b/src/components/uart/drivers/drvUartUs100.h @@ -0,0 +1,220 @@ +/*! + * @file drvUartUs100.h + * + * UART driver for the US-100 Ultrasonic Distance Sensor + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef DRV_UART_US100_H +#define DRV_UART_US100_H +#include "Adafruit_GenericDevice.h" +#include "drvUartBase.h" + +#define CMD_US100_REQ_TEMP 0x50 ///< Temperature request command +#define CMD_US100_REQ_DISTANCE 0x55 ///< Distance request command +#define US_100_DELAY_TIME 100 ///< Delay time for US-100, in milliseconds + +/** + * Basic UART device class that demonstrates using GenericDevice with a Stream + * interface. This example shows how to wrap a Stream (like HardwareSerial or + * SoftwareSerial) with read/write callbacks that can be used by BusIO's + * register functions. + */ + +class UARTDevice { +public: + UARTDevice(Stream *serial) { _serial_dev = serial; } + ~UARTDevice() { /* no-op destructor */ } + + bool CreateDevice() { + if (_generic_dev != nullptr) { + return false; // already initialized + } + _generic_dev = new Adafruit_GenericDevice(this, uart_read, uart_write); + return _generic_dev->begin(); + } + + static bool uart_write(void *thiz, const uint8_t *buffer, size_t len) { + UARTDevice *dev = (UARTDevice *)thiz; + dev->_serial_dev->write(buffer, len); + return true; + } + + static bool uart_read(void *thiz, uint8_t *buffer, size_t len) { + UARTDevice *dev = (UARTDevice *)thiz; + uint16_t timeout = 100; + while (dev->_serial_dev->available() < len && timeout--) { + delay(1); + } + if (timeout == 0) { + return false; + } + for (size_t i = 0; i < len; i++) { + buffer[i] = dev->_serial_dev->read(); + } + return true; + } + + Adafruit_GenericDevice *getGenericDevice() { return _generic_dev; } + +private: + Adafruit_GenericDevice *_generic_dev = nullptr; + Stream *_serial_dev = nullptr; +}; + +/*! + @brief Provides an interface for the US-100 Ultrasonic Distance Sensor over + UART. +*/ +class drvUartUs100 : public drvUartBase { + +public: + /*! + @brief Instantiates a US-100 UART device. + @param hw_serial + Pointer to a HardwareSerial instance. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + instance. + */ + drvUartUs100(HardwareSerial *hw_serial, const char *driver_name, + uint32_t port_num) + : drvUartBase(hw_serial, driver_name, port_num) { + // Handled by drvUartBase constructor + } + +#if HAS_SW_SERIAL + /*! + @brief Instantiates a US-100 UART device. + @param sw_serial + Pointer to a SoftwareSerial instance. + @param driver_name + The name of the driver. + @param port_num + The port number for the UART device corresponding to the Serial + instance. +*/ + drvUartUs100(SoftwareSerial *sw_serial, const char *driver_name, + uint32_t port_num) + : drvUartBase(sw_serial, driver_name, port_num) { + // Handled by drvUartBase constructor + } +#endif // HAS_SW_SERIAL + + /*! + @brief Destructor for a US-100 UART device. + */ + ~drvUartUs100() { + if (_uart_dev) { + delete _uart_dev; // Clean up the UARTDevice instance + _uart_dev = nullptr; + } + } + + /*! + @brief Initializes the US-100 UART device. + @returns True if initialized successfully, False otherwise. + */ + bool begin() override { +#if HAS_SW_SERIAL + if (_sw_serial == nullptr) + return false; + _uart_dev = new UARTDevice(_sw_serial); +#else + if (_hw_serial == nullptr) + return false; + _uart_dev = new UARTDevice(_hw_serial); +#endif // HAS_SW_SERIAL + + // Create a GenericDevice instance using the UARTDevice + return _uart_dev->CreateDevice(); + return true; + } + + /*! + @brief Gets the US-100 Ultrasonic Distance Sensor's temperature, in + Celsius. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the temperature was obtained successfully, False + otherwise. + */ + bool getEventAmbientTemp(sensors_event_t *tempEvent) { + uint8_t len_write_buf = 1; + // Write the temperature request command to the US-100 + uint8_t write_buf[len_write_buf] = {CMD_US100_REQ_TEMP}; + if (!_uart_dev->getGenericDevice()->write(write_buf, len_write_buf)) + return false; + // Wait for US-100 to respond + delay(US_100_DELAY_TIME); + + // Attempt to read the response from the US-100 + uint8_t len_read_buf = 1; + uint8_t read_buf[len_read_buf] = {0}; + if (!_uart_dev->getGenericDevice()->read(read_buf, len_read_buf)) + return false; + + // Check if the response is within a valid range + if ((read_buf[0] > 1) && (read_buf[0] < 130)) { + // Populate the temperature event + tempEvent->temperature = read_buf[0] - 45; + } else { + return false; + } + + return true; + } + + /*! + @brief Gets the US-100 sensor's distance measurement. + @param rawEvent + The Raw value, contains the distance in centimeters. + @returns True if the distance value was obtained successfully, False + otherwise. + */ + bool getEventRaw(sensors_event_t *rawEvent) { + // Write the distance request command to the US-100 + uint8_t len_write_buf = 1; + uint8_t write_buf[len_write_buf] = {CMD_US100_REQ_DISTANCE}; + WS_DEBUG_PRINTLN("[uart] Requesting distance from US-100..."); + + if (!_uart_dev->getGenericDevice()->write(write_buf, len_write_buf)) + return false; + // Wait for US-100 to respond + delay(US_100_DELAY_TIME); + + // Read the response from the US-100 + uint8_t len_read_buf = 2; + uint8_t read_buf[len_read_buf] = {0}; + if (!_uart_dev->getGenericDevice()->read(read_buf, len_read_buf)) + return false; + + // Calculate distance + uint16_t dist_high_byte = read_buf[0]; + uint16_t dist_low_byte = read_buf[1]; + uint16_t distance = dist_high_byte * 256 + dist_low_byte; + + // Check if the distance is within valid range + if ((distance < 1) || (distance > 10000)) { + return false; + } + + // Populate the raw event with the distance value in cm + rawEvent->data[0] = (float)distance; + return true; + } + +protected: + UARTDevice *_uart_dev = nullptr; ///< Pointer to the UARTDevice instance +}; +#endif // DRV_UART_US100_H \ No newline at end of file diff --git a/src/components/uart/hardware.cpp b/src/components/uart/hardware.cpp new file mode 100644 index 000000000..761a00efb --- /dev/null +++ b/src/components/uart/hardware.cpp @@ -0,0 +1,214 @@ +/*! + * @file src/components/uart/hardware.cpp + * + * Low-level hardware implementation for WipperSnapper's uart component. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" + +/*! + @brief Constructs a new UARTHardware object. + @param config The configuration for the serial. +*/ +UARTHardware::UARTHardware(const wippersnapper_uart_UartSerialConfig &config) { + _config = config; + _uart_nbr = config.uart_nbr; +} + +/*! + @brief Destructs the UARTHardware. +*/ +UARTHardware::~UARTHardware() { + // TODO +} + +/*! + @brief Converts a wippersnapper_uart_UartPacketFormat to a HardwareSerial + config. + @param uart_pkt_fmt + The UART packet format to convert. + @return The corresponding xSerial config value. +*/ +uint16_t UARTHardware::UartPacketFormatToConfig( + const wippersnapper_uart_UartPacketFormat uart_pkt_fmt) { + switch (uart_pkt_fmt) { + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8N1: + return SERIAL_8N1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5N1: + return SERIAL_5N1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6N1: + return SERIAL_6N1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7N1: + return SERIAL_7N1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5N2: + return SERIAL_5N2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6N2: + return SERIAL_6N2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7N2: + return SERIAL_7N2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8N2: + return SERIAL_8N2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5E1: + return SERIAL_5E1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6E1: + return SERIAL_6E1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7E1: + return SERIAL_7E1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8E1: + return SERIAL_8E1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5E2: + return SERIAL_5E2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6E2: + return SERIAL_6E2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7E2: + return SERIAL_7E2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8E2: + return SERIAL_8E2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5O1: + return SERIAL_5O1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6O1: + return SERIAL_6O1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7O1: + return SERIAL_7O1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O1: + return SERIAL_8O1; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5O2: + return SERIAL_5O2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6O2: + return SERIAL_6O2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7O2: + return SERIAL_7O2; + case wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O2: + return SERIAL_8O2; + default: + return SERIAL_8N1; + } +} + +/*! + * @brief Configures the UART serial using a provided configuration. + * @return True if the serial was successfully configured, False otherwise. + */ +bool UARTHardware::ConfigureSerial() { + int8_t rx_pin = -1; + int8_t tx_pin = -1; + if (_config.pin_rx[0] == 'D' || _config.pin_rx[0] == 'A') { + rx_pin = atoi(_config.pin_rx + 1); + } else { + rx_pin = atoi(_config.pin_rx); + } + if (_config.pin_tx[0] == 'D' || _config.pin_tx[0] == 'A') { + tx_pin = atoi(_config.pin_tx + 1); + } else { + tx_pin = atoi(_config.pin_tx); + } + uint16_t cfg = UartPacketFormatToConfig(_config.format); + + if (_config.use_sw_serial) { +// Does this architecture support SoftwareSerial? +#if HAS_SW_SERIAL + // Create a new SoftwareSerial instance + _swSerial = new SoftwareSerial(rx_pin, tx_pin, _config.sw_serial_invert); + if (_swSerial == nullptr) { + WS_DEBUG_PRINTLN( + "[uart] ERROR: Failed to allocate a SoftwareSerial instance!"); + return false; + } + _swSerial->begin((unsigned long)_config.baud_rate); + _baud_rate = _config.baud_rate; +#endif // HAS_SW_SERIAL + } else { +#if ARDUINO_ARCH_ESP32 + // Create a new HardwareSerial instance + _hwSerial = new HardwareSerial(_config.uart_nbr); + if (_hwSerial == nullptr) + return false; + _hwSerial->begin((unsigned long)_config.baud_rate, (uint32_t)cfg, rx_pin, + tx_pin, false, (unsigned long)_config.timeout); +#elif ARDUINO_ARCH_RP2040 + // Create a new SerialUART instance + uart_inst_t *uart_hw = nullptr; + // determine which bus to use (RP2040 only supports 2 hardware UARTs) + if (_config.uart_nbr == 0) { + uart_hw = uart0; + } else if (_config.uart_nbr == 1) { + uart_hw = uart1; + } else { + WS_DEBUG_PRINTLN("[uart] ERROR: Invalid UART bus number specified!"); + return false; + } + _hwSerial = new SerialUART(uart_hw, tx_pin, rx_pin); + if (_hwSerial == nullptr) { + return false; + } + _hwSerial->begin((unsigned long)_config.baud_rate, (uint32_t)cfg); +#else + // ESP8266, SAMD, and other platforms + // take the default Arduino/Wiring API arguments + // Create a new HardwareSerial instance + _hwSerial = new HardwareSerial(_config.uart_nbr); + if (_hwSerial == nullptr) + return false; + _hwSerial->begin((unsigned long)_config.baud_rate, (uint32_t)cfg); +#endif + _baud_rate = _config.baud_rate; + } + return true; +} + +/*! + * @brief Checks if the hardware instance has allocated and is using a + * HardwareSerial. + * @return True if a HardwareSerial instance is in-use by the hardware, False + * otherwise. + */ +bool UARTHardware::isHardwareSerial() const { return _hwSerial != nullptr; } + +/*! + * @brief Checks if the hardware instance has allocated and is using a Software + * Serial. + * @return True if a SoftwareSerial instance is in-use by the hardware, False + * otherwise. + */ +bool UARTHardware::isSoftwareSerial() const { +#if HAS_SW_SERIAL + return _swSerial != nullptr; +#endif + return false; +} + +/*! + * @brief Gets the bus number of the hardware instance. + * @return The bus number of the hardware instance, or -1 if not set. + */ +int UARTHardware::GetBusNumber() { return _uart_nbr; } + +/*! + * @brief Gets the HardwareSerial instance for this port + * @return A pointer to the HardwareSerial instance, or nullptr if not + * constructed. + */ +HardwareSerial *UARTHardware::GetHardwareSerial() { return _hwSerial; } + +#if HAS_SW_SERIAL +/*! + * @brief Gets the SoftwareSerial instance for this port + * @return A pointer to the SoftwareSerial instance, or nullptr if not + * constructed. + */ +SoftwareSerial *UARTHardware::GetSoftwareSerial() { return _swSerial; } +#endif // HAS_SW_SERIAL + +/*! + * @brief Gets the baud rate of the serial instance. + * @return The baud rate of the serial instance. + */ +uint32_t UARTHardware::GetBaudRate() { return _baud_rate; } \ No newline at end of file diff --git a/src/components/uart/hardware.h b/src/components/uart/hardware.h new file mode 100644 index 000000000..51416af66 --- /dev/null +++ b/src/components/uart/hardware.h @@ -0,0 +1,58 @@ +/*! + * @file src/components/uart/hardware.h + * + * Low-level hardware implementation for WipperSnapper's uart component. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_HARDWARE_H +#define WS_UART_HARDWARE_H +#include "Wippersnapper_V2.h" +#include "serial_config.h" +#include +#include +#ifdef ARDUINO_ARCH_RP2040 +#include +#endif + +/*! + @brief Interface for interacting with the UART hardware. +*/ +class UARTHardware { +public: + UARTHardware(const wippersnapper_uart_UartSerialConfig &config); + ~UARTHardware(); + bool ConfigureSerial(); + uint16_t UartPacketFormatToConfig( + const wippersnapper_uart_UartPacketFormat uart_format); + int GetBusNumber(); + bool isHardwareSerial() const; + bool isSoftwareSerial() const; + HardwareSerial *GetHardwareSerial(); +#if HAS_SW_SERIAL + SoftwareSerial *GetSoftwareSerial(); +#endif // HAS_SW_SERIAL + uint32_t GetBaudRate(); + +private: + wippersnapper_uart_UartSerialConfig + _config; ///< The UART serial configuration +#ifdef ARDUINO_ARCH_RP2040 + SerialUART *_hwSerial = nullptr; ///< Pointer to the SerialUART instance +#else + HardwareSerial *_hwSerial = nullptr; ///< HardwareSerial instance for this bus +#endif +#if HAS_SW_SERIAL + SoftwareSerial *_swSerial = nullptr; ///< SoftwareSerial instance for this bus +#endif // HAS_SW_SERIAL + int _uart_nbr = -1; ///< The UART bus number this hardware instance is using + uint32_t _baud_rate; ///< The baud rate for this hardware instance +}; +#endif // WS_UART_HARDWARE_H \ No newline at end of file diff --git a/src/components/uart/model.cpp b/src/components/uart/model.cpp new file mode 100644 index 000000000..d0c8ab158 --- /dev/null +++ b/src/components/uart/model.cpp @@ -0,0 +1,239 @@ +/*! + * @file src/components/uart/model.cpp + * + * Model implementation for the UART.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" + +/*! + @brief Constructs a new UARTModel. +*/ +UARTModel::UARTModel() { + memset(&_msg_UartAdd, 0, sizeof(_msg_UartAdd)); + memset(&_msg_UartAdded, 0, sizeof(_msg_UartAdded)); + memset(&_msg_UartRemove, 0, sizeof(_msg_UartRemove)); + memset(&_msg_UartWrite, 0, sizeof(_msg_UartWrite)); + memset(&_msg_UartWritten, 0, sizeof(_msg_UartWritten)); + memset(&_msg_UartInputEvent, 0, sizeof(_msg_UartInputEvent)); +} + +/*! + @brief Destructs the UARTModel. +*/ +UARTModel::~UARTModel() { + memset(&_msg_UartAdd, 0, sizeof(_msg_UartAdd)); + memset(&_msg_UartAdded, 0, sizeof(_msg_UartAdded)); + memset(&_msg_UartRemove, 0, sizeof(_msg_UartRemove)); + memset(&_msg_UartWrite, 0, sizeof(_msg_UartWrite)); + memset(&_msg_UartWritten, 0, sizeof(_msg_UartWritten)); + memset(&_msg_UartInputEvent, 0, sizeof(_msg_UartInputEvent)); +} + +/*! + @brief Decodes a UartAdd message from a protobuf input stream. + @param stream + Pointer to a pb_istream_t object. + @return True if the message was decoded successfully, False otherwise. +*/ +bool UARTModel::DecodeUartAdd(pb_istream_t *stream) { + WS_DEBUG_PRINTLN("[uart] Decoding UartAdd message..."); + memset(&_msg_UartAdd, 0, sizeof(_msg_UartAdd)); + return pb_decode(stream, wippersnapper_uart_UartAdd_fields, &_msg_UartAdd); +} + +/*! + @brief Gets a pointer to the decoded UartAdd message. + @return Pointer to the decoded UartAdd message. +*/ +wippersnapper_uart_UartAdd *UARTModel::GetUartAddMsg() { return &_msg_UartAdd; } + +/*! + @brief Encodes a UartAdded message. + @param uart_nbr + The UART port number (eg: 0, 1, 2, etc.) that the device was + attached to. + @param type + The category of device attached to the UART port, corresponds to its + driver type. + @param id + The unique identifier string for the UART device. + @param success + True if the device on the UART port was successfully initialized, + False otherwise. + @return True if the message was encoded successfully, False otherwise. +*/ +bool UARTModel::EncodeUartAdded(int32_t uart_nbr, + wippersnapper_uart_UartDeviceType type, + const char *id, bool success) { + _msg_UartAdded.uart_nbr = uart_nbr; + _msg_UartAdded.type = type; + strncpy(_msg_UartAdded.device_id, id, sizeof(_msg_UartAdded.device_id) - 1); + _msg_UartAdded.device_id[sizeof(_msg_UartAdded.device_id) - 1] = '\0'; + _msg_UartAdded.success = success; + // Calculate the size of the encoded message + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_uart_UartAdded_fields, + &_msg_UartAdded)) + return false; + // Attempt to encode the message into a buffer + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_uart_UartAdded_fields, + &_msg_UartAdded); +} + +/*! + @brief Gets a pointer to the encoded UartAdded message. + @return Pointer to the encoded UartAdded message. +*/ +wippersnapper_uart_UartAdded *UARTModel::GetUartAddedMsg() { + return &_msg_UartAdded; +} + +/*! + @brief Decodes a UartDeviceRemove message from an input stream. + @param stream + Pointer to a pb_istream_t object. + @return True if the UartDeviceRemove message was decoded successfully, + False otherwise. +*/ +bool UARTModel::DecodeUartDeviceRemove(pb_istream_t *stream) { + memset(&_msg_UartRemove, 0, sizeof(_msg_UartRemove)); + return pb_decode(stream, wippersnapper_uart_UartRemove_fields, + &_msg_UartRemove); +} + +/*! + @brief Gets a pointer to the UartRemove message. + @return Pointer to the UartRemove message. +*/ +wippersnapper_uart_UartRemove *UARTModel::GetUartRemoveMsg() { + return &_msg_UartRemove; +} + +/*! + @brief Clears an UartInputEvent message. +*/ +void UARTModel::ClearUartInputEventMsg() { + memset(&_msg_UartInputEvent, 0, sizeof(_msg_UartInputEvent)); +} + +/*! + @brief Configures an UartInputEvent message with addressing information. + @param uart_nbr + The UART port number (eg: 0, 1, 2, etc.) that the device is attached + to. + @param type + The category of device attached to the UART port, corresponds to its + driver type. + @param device_id + The unique identifier string for the UART device. +*/ +void UARTModel::ConfigureUartInputEventMsg( + uint32_t uart_nbr, wippersnapper_uart_UartDeviceType type, + const char *device_id) { + // Addressing information + _msg_UartInputEvent.uart_nbr = uart_nbr; + _msg_UartInputEvent.type = type; + strncpy(_msg_UartInputEvent.device_id, device_id, + sizeof(_msg_UartInputEvent.device_id) - 1); + _msg_UartInputEvent.device_id[sizeof(_msg_UartInputEvent.device_id) - 1] = + '\0'; +} + +/*! + @brief Adds a UART input event to the UartInputEvent message. + @param event + Reference to a sensors_event_t structure containing sensor data. + @param sensor_type + The type of sensor that produced the event. + @return True if the event was added successfully, False otherwise. +*/ +bool UARTModel::AddUartInputEvent(sensors_event_t &event, + wippersnapper_sensor_SensorType sensor_type) { + if (_msg_UartInputEvent.events_count >= MAX_UART_INPUT_EVENTS) { + return false; // Maximum number of events reached + } + + // Configure the sensor event + wippersnapper_sensor_SensorEvent &sensor_event = + _msg_UartInputEvent.events[_msg_UartInputEvent.events_count]; + sensor_event.type = sensor_type; + + // Handle different sensor types + switch (sensor_type) { + case wippersnapper_sensor_SensorType_SENSOR_TYPE_PM10_STD: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.pm10_std; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_PM25_STD: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.pm25_std; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_PM100_STD: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.pm100_std; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.temperature; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.temperature; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.temperature; + break; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.temperature; + case wippersnapper_sensor_SensorType_SENSOR_TYPE_PROXIMITY: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.distance; + break; + default: + sensor_event.which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + sensor_event.value.float_value = event.data[0]; + break; + } + + _msg_UartInputEvent.events_count++; + return true; +} + +/*! + @brief Encodes a UartInputEvent message. + @return True if the message was encoded successfully, False otherwise. +*/ +bool UARTModel::EncodeUartInputEvent() { + // Calculate the size of the encoded message + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_uart_UartInputEvent_fields, + &_msg_UartInputEvent)) + return false; + + // Attempt to encode the message into a buffer + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_uart_UartInputEvent_fields, + &_msg_UartInputEvent); +} + +/*! + @brief Gets a pointer to the UartInputEvent message. + @return Pointer to the UartInputEvent message. +*/ +wippersnapper_uart_UartInputEvent *UARTModel::GetUartInputEventMsg() { + return &_msg_UartInputEvent; +} \ No newline at end of file diff --git a/src/components/uart/model.h b/src/components/uart/model.h new file mode 100644 index 000000000..e75bf24be --- /dev/null +++ b/src/components/uart/model.h @@ -0,0 +1,64 @@ +/*! + * @file src/components/uart/model.h + * + * Model interface for the UART.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_MODEL_H +#define WS_UART_MODEL_H +#include "Wippersnapper_V2.h" +#include +#define MAX_UART_INPUT_EVENTS 15 + +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from UART.proto. +*/ +class UARTModel { +public: + UARTModel(); + ~UARTModel(); + // Messaging APIs // + // UartAdd + bool DecodeUartAdd(pb_istream_t *stream); + bool DecodeUartDeviceRemove(pb_istream_t *stream); + wippersnapper_uart_UartAdd *GetUartAddMsg(); + // UartAdded + bool EncodeUartAdded(int32_t uart_nbr, wippersnapper_uart_UartDeviceType type, + const char *id, bool success); + wippersnapper_uart_UartAdded *GetUartAddedMsg(); + // UartRemove + wippersnapper_uart_UartRemove *GetUartRemoveMsg(); + // UartInputEvent + bool AddUartInputEvent(sensors_event_t &event, + wippersnapper_sensor_SensorType sensor_type); + bool EncodeUartInputEvent(); + wippersnapper_uart_UartInputEvent *GetUartInputEventMsg(); + void ClearUartInputEventMsg(); + void ConfigureUartInputEventMsg(uint32_t uart_nbr, + wippersnapper_uart_UartDeviceType type, + const char *device_id); + +private: + wippersnapper_uart_UartAdd + _msg_UartAdd; ///< wippersnapper_uart_UartAdd message + wippersnapper_uart_UartAdded + _msg_UartAdded; ///< wippersnapper_uart_UartAdded message + wippersnapper_uart_UartRemove + _msg_UartRemove; ///< wippersnapper_uart_UartRemove message + wippersnapper_uart_UartWrite + _msg_UartWrite; ///< wippersnapper_uart_UartWrite message + wippersnapper_uart_UartWritten + _msg_UartWritten; ///< wippersnapper_uart_UartWritten message + wippersnapper_uart_UartInputEvent + _msg_UartInputEvent; ///< wippersnapper_uart_UartInputEvent message +}; +#endif // WS_UART_MODEL_H \ No newline at end of file diff --git a/src/components/uart/serial_config.h b/src/components/uart/serial_config.h new file mode 100644 index 000000000..11a2c8fe4 --- /dev/null +++ b/src/components/uart/serial_config.h @@ -0,0 +1,32 @@ +/*! + * @file src/components/uart/serial_config.h + * + * Serial configuration definitions for WipperSnapper components. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2025 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_UART_SERIAL_CONFIG_H +#define WS_UART_SERIAL_CONFIG_H + +#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_SAMD) || \ + defined(ARDUINO_ARCH_ESP8266) +// SAMD supports native Arduino SoftwareSerial API +// ESP8266 supports SoftwareSerial in the ESP8266 Arduino core +// RP2040/RP2350 supports a wrapper around SoftwareSerial and emulation via +// PIOUART (see: +// https://arduino-pico.readthedocs.io/en/latest/piouart.html#softwareserial-emulation) +#include +#define HAS_SW_SERIAL 1 ///< Indicates that the board supports SoftwareSerial +#else +#define HAS_SW_SERIAL \ + 0 ///< Indicates that the board DOES NOT support SoftwareSerial +#endif + +#endif // WS_UART_SERIAL_CONFIG_H \ No newline at end of file diff --git a/src/display/ws_display_driver.cpp b/src/display/ws_display_driver.cpp index a901b1954..cd4bb5128 100644 --- a/src/display/ws_display_driver.cpp +++ b/src/display/ws_display_driver.cpp @@ -15,14 +15,12 @@ #ifdef ARDUINO_FUNHOUSE_ESP32S2 #include "ws_display_driver.h" -/**************************************************************************/ /*! @brief Creates a new WipperSnapper display driver object from a configuration struct. @param config Configuration struct., from FS.ParseFileDisplayCfg(); */ -/**************************************************************************/ ws_display_driver::ws_display_driver(displayConfig config) { // dynamically create the display driver from the configuration file if (strcmp(config.driver, "ST7789") == 0) { @@ -40,37 +38,30 @@ ws_display_driver::ws_display_driver(displayConfig config) { setRotation(config.rotation); } -/**************************************************************************/ /*! @brief Deletes a new WipperSnapper display driver object. */ -/**************************************************************************/ ws_display_driver::~ws_display_driver() { if (_tft_st7789 != nullptr) { delete _tft_st7789; } } -/**************************************************************************/ /*! @brief Enables LVGL logging using the usb serial. Must be called AFTER calling Serial.begin(). */ -/**************************************************************************/ void ws_display_driver::enableLogging() {} -/**************************************************************************/ /*! @brief Sets the display's rotation mode. @param rotationMode The index for rotation (0-3 inclusive). */ -/**************************************************************************/ void ws_display_driver::setRotation(uint8_t rotationMode) { _displayRotationMode = rotationMode; } -/**************************************************************************/ /*! @brief Sets the display resolution, must be called BEFORE begin()! @param displayWidth @@ -78,19 +69,16 @@ void ws_display_driver::setRotation(uint8_t rotationMode) { @param displayHeight The height of the display, in pixels. */ -/**************************************************************************/ void ws_display_driver::setResolution(uint16_t displayWidth, uint16_t displayHeight) { _displayWidth = displayWidth; _displayHeight = displayHeight; } -/**************************************************************************/ /*! @brief Initializes the display and the lvgl_glue driver. @returns True if LVGL_Glue began successfully, False otherwise. */ -/**************************************************************************/ bool ws_display_driver::begin() { // initialize display driver if (_tft_st7789 != nullptr) { @@ -126,18 +114,14 @@ bool ws_display_driver::begin() { return true; } -/**************************************************************************/ /*! @brief Acquires the LVGL_Glue lock. */ -/**************************************************************************/ void ws_display_driver::esp32_lvgl_acquire() { _glue->lvgl_acquire(); } -/**************************************************************************/ /*! @brief Releases the LVGL_Glue lock. */ -/**************************************************************************/ void ws_display_driver::esp32_lvgl_release() { _glue->lvgl_release(); } #endif \ No newline at end of file diff --git a/src/display/ws_display_driver.h b/src/display/ws_display_driver.h index 80f149df7..968bd5339 100644 --- a/src/display/ws_display_driver.h +++ b/src/display/ws_display_driver.h @@ -15,7 +15,7 @@ #ifndef WIPPERSNAPPER_DISPLAY_H #define WIPPERSNAPPER_DISPLAY_H -#include "Wippersnapper.h" +#include "Wippersnapper_V2.h" #include "provisioning/Config.h" #include // Always include this BEFORE lvgl.h! #include @@ -23,13 +23,11 @@ LV_FONT_DECLARE(errorTriangle); ///< Error triangle symbol/font -class Wippersnapper; // fwd decl +class Wippersnapper_V2; // fwd decl -/***************************************************************************/ /*! @brief Display driver for LVGL and LVGL_Glue in WipperSnapper. */ -/***************************************************************************/ class ws_display_driver { public: ws_display_driver(){}; @@ -51,6 +49,6 @@ class ws_display_driver { uint8_t _displayRotationMode; ///< Display rotation (mode, not number in degrees) }; -extern Wippersnapper WS; ///< Global Wippersnapper instance +extern Wippersnapper_V2 WsV2; ///< Global Wippersnapper instance #endif // WIPPERSNAPPER_DISPLAY_H \ No newline at end of file diff --git a/src/display/ws_display_ui_helper.cpp b/src/display/ws_display_ui_helper.cpp index bfc9fc11b..daa177cb2 100644 --- a/src/display/ws_display_ui_helper.cpp +++ b/src/display/ws_display_ui_helper.cpp @@ -57,26 +57,22 @@ static const char *loading_tips[4] = { WS_LOADING_TIP_1, WS_LOADING_TIP_2, WS_LOADING_TIP_3, WS_LOADING_TIP_4}; ///< Holds the loading "tips" -/**************************************************************************/ /*! @brief Changes a label every 2 seconds to a new, random, tip. @param timer The lv_timer tied to this callback, timerLoadTips. */ -/**************************************************************************/ void lv_timer_tips_cb(lv_timer_t *timer) { Serial.println("Timer tips cb called"); long tipNum = random(0, sizeof(loading_tips) / sizeof(loading_tips[0])); lv_label_set_text(lblTipText, loading_tips[tipNum]); } -/**************************************************************************/ /*! @brief Callback for updating the status label on the loading screen. @param event Callback data. */ -/**************************************************************************/ static void label_status_cb(lv_event_t *event) { Serial.println("eventcb called!"); const char **charPtr{static_cast(lv_event_get_param(event))}; @@ -85,13 +81,11 @@ static void label_status_cb(lv_event_t *event) { lv_label_set_text(lblStatusText, *charPtr); } -/**************************************************************************/ /*! @brief Sets the text of the status label on the loading screen. @param text Desired text to write to the status label. */ -/**************************************************************************/ void ws_display_ui_helper::set_label_status(const char *text) { Serial.print("set_label_status (text): "); Serial.println(text); @@ -100,34 +94,28 @@ void ws_display_ui_helper::set_label_status(const char *text) { _dispDriver->esp32_lvgl_release(); } -/**************************************************************************/ /*! @brief Pauses and deletes the loading tip callback timer. */ -/**************************************************************************/ void ws_display_ui_helper::remove_tip_timer() { lv_timer_pause(timerLoadTips); lv_timer_del(timerLoadTips); } -/**************************************************************************/ /*! @brief Sets the screen's background to a black color. */ -/**************************************************************************/ void ws_display_ui_helper::set_bg_black() { _dispDriver->esp32_lvgl_acquire(); lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_STATE_DEFAULT); _dispDriver->esp32_lvgl_release(); } -/**************************************************************************/ /*! @brief Sets the color of an icon on the loading screen to green. @param iconType Desired icon. */ -/**************************************************************************/ void ws_display_ui_helper::set_load_bar_icon_complete(loadBarIcons iconType) { static lv_style_t *styleIcon; static lv_obj_t *objIcon; @@ -160,20 +148,16 @@ void ws_display_ui_helper::set_load_bar_icon_complete(loadBarIcons iconType) { _dispDriver->esp32_lvgl_release(); } -/**************************************************************************/ /*! @brief Returns the loading screen's state. @returns The loading state, True if loading screen is active, False otherwise. */ -/**************************************************************************/ bool ws_display_ui_helper::getLoadingState() { return _loadingState; } -/**************************************************************************/ /*! @brief Builds and displays the loading screen. */ -/**************************************************************************/ void ws_display_ui_helper::show_scr_load() { _dispDriver->esp32_lvgl_acquire(); // Icon bar @@ -248,12 +232,10 @@ void ws_display_ui_helper::show_scr_load() { _loadingState = true; // using the loading screen state } -/**************************************************************************/ /*! @brief Deletes all objects/styles off the load screen and frees their resources. */ -/**************************************************************************/ void ws_display_ui_helper::clear_scr_load() { Serial.println("clear_scr_load"); _dispDriver->esp32_lvgl_acquire(); @@ -275,7 +257,6 @@ void ws_display_ui_helper::clear_scr_load() { _loadingState = false; // no longer using the loading screen state } -/**************************************************************************/ /*! @brief Build and display an error screen. @param lblError @@ -283,7 +264,6 @@ void ws_display_ui_helper::clear_scr_load() { @param lblDesc Instructions or steps to resolve the error. */ -/**************************************************************************/ void ws_display_ui_helper::show_scr_error(const char *lblError, const char *lblDesc) { Serial.println("show_scr_error"); @@ -329,11 +309,9 @@ void ws_display_ui_helper::show_scr_error(const char *lblError, _dispDriver->esp32_lvgl_release(); } -/**************************************************************************/ /*! @brief Build and display the monitor screen */ -/**************************************************************************/ void ws_display_ui_helper::build_scr_monitor() { _dispDriver->esp32_lvgl_acquire(); @@ -395,21 +373,18 @@ void ws_display_ui_helper::build_scr_monitor() { _dispDriver->esp32_lvgl_release(); } -/**************************************************************************/ /*! @brief Add text on the terminal label and displays it. @param text Text to display on the terminal, should end in "\n" */ -/**************************************************************************/ void ws_display_ui_helper::add_text_to_terminal(const char *text) { Serial.println("add_text_to_terminal"); - char txtBuffer[256]; // temporary text buffer for snprintf - snprintf(txtBuffer, 256, text); - addToTerminal(txtBuffer); + /* char txtBuffer[256]; // temporary text buffer for snprintf + snprintf(txtBuffer, 256, text); + addToTerminal(txtBuffer); */ } -/**************************************************************************/ /*! @brief Adds a line of text on the terminal label and displays it. @param text @@ -417,7 +392,6 @@ void ws_display_ui_helper::add_text_to_terminal(const char *text) { @note Reference: https://github.com/lvgl/lv_demos/blob/release/v6/lv_apps/terminal/terminal.c */ -/**************************************************************************/ void ws_display_ui_helper::addToTerminal(const char *txt_in) { // Calculate text size size_t txt_len = strlen(txt_in); diff --git a/src/display/ws_display_ui_helper.h b/src/display/ws_display_ui_helper.h index 909d6c73a..8ad56a6e4 100644 --- a/src/display/ws_display_ui_helper.h +++ b/src/display/ws_display_ui_helper.h @@ -16,7 +16,7 @@ #ifndef WS_DISPLAY_UI_HELPER_H #define WS_DISPLAY_UI_HELPER_H -#include "Wippersnapper.h" +#include "Wippersnapper_V2.h" #include "ws_display_driver.h" #include "ws_display_tooltips.h" #include @@ -54,21 +54,17 @@ enum loadBarIcons { class ws_display_driver; -/**************************************************************************/ /*! @brief Helps build and manage the LVGL objects and screens for the application code. */ -/**************************************************************************/ class ws_display_ui_helper { public: - /**************************************************************************/ /*! @brief Constructor for the ws_display_ui_helper class. @param drv Pointer to the display driver. */ - /**************************************************************************/ ws_display_ui_helper(ws_display_driver *drv) { _dispDriver = drv; }; ~ws_display_ui_helper(){}; diff --git a/src/helpers/ws_helper_esp.h b/src/helpers/ws_helper_esp.h index 8f18c3f53..a08c189a0 100644 --- a/src/helpers/ws_helper_esp.h +++ b/src/helpers/ws_helper_esp.h @@ -17,7 +17,6 @@ #include "esp_task_wdt.h" #include -/*******************************************************************************************************************************/ /*! @brief Converts reset reason type to a C string.. This uses the mechanism in IDF that persists crash reasons across a reset. see: @@ -26,7 +25,6 @@ The reset reason, esp_reset_reason_t @return A C string representing the reset reason. */ -/*******************************************************************************************************************************/ inline const char *resetReasonName(esp_reset_reason_t r) { switch (r) { case ESP_RST_UNKNOWN: diff --git a/src/helpers/ws_helper_macros.h b/src/helpers/ws_helper_macros.h index e82393a28..cfd6d9722 100644 --- a/src/helpers/ws_helper_macros.h +++ b/src/helpers/ws_helper_macros.h @@ -25,14 +25,13 @@ while (millis() - start < timeout) { \ delay(10); \ yield(); \ - feedWDT(); \ + WsV2.feedWDTV2(); \ if (millis() < start) { \ start = millis(); /* if rollover */ \ } \ } \ } ///< Delay function -/**************************************************************************/ /*! @brief Retry a function until a condition is met or a timeout is reached. @param func @@ -50,13 +49,12 @@ @param ... The arguments to pass to the function. */ -/**************************************************************************/ -#define RETRY_FUNCTION_UNTIL_TIMEOUT(func, result_type, result_var, condition, \ - timeout, interval, ...) \ +#define RETRY_FUNCTION_UNTIL_TIMEOUT(func, result_var, condition, timeout, \ + interval, ...) \ { \ unsigned long startTime = millis(); \ while (millis() - startTime < timeout) { \ - result_type result_var = func(__VA_ARGS__); \ + result_var = func(__VA_ARGS__); \ if (condition(result_var)) { \ break; \ } \ diff --git a/src/pb.h b/src/pb.h index f5c9c6f34..fa7e0d893 100644 --- a/src/pb.h +++ b/src/pb.h @@ -16,7 +16,7 @@ /* Define this if your CPU / compiler combination does not support * unaligned memory access to packed structures. Note that packed * structures are only used when requested in .proto options. */ -/* #define PB_NO_PACKED_STRUCTS 1 */ +#define PB_NO_PACKED_STRUCTS 1 /* Increase the number of required fields that are tracked. * A compiler warning will tell if you need this. */ @@ -26,14 +26,14 @@ /* #define PB_FIELD_32BIT 1 */ /* Disable support for error messages in order to save some code space. */ -/* #define PB_NO_ERRMSG 1 */ +#define PB_NO_ERRMSG 1 /* Disable support for custom streams (support only memory buffers). */ -/* #define PB_BUFFER_ONLY 1 */ +#define PB_BUFFER_ONLY 1 /* Disable support for 64-bit datatypes, for compilers without int64_t or to save some code space. */ -/* #define PB_WITHOUT_64BIT 1 */ +#define PB_WITHOUT_64BIT 1 /* Don't encode scalar arrays as packed. This is only to be used when * the decoder on the receiving side cannot process packed scalar arrays. diff --git a/src/protos/gps.pb.c b/src/protos/gps.pb.c new file mode 100644 index 000000000..2ab34eafe --- /dev/null +++ b/src/protos/gps.pb.c @@ -0,0 +1,24 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "gps.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(wippersnapper_gps_GPSConfig, wippersnapper_gps_GPSConfig, 2) + + +PB_BIND(wippersnapper_gps_GPSDateTime, wippersnapper_gps_GPSDateTime, AUTO) + + +PB_BIND(wippersnapper_gps_GPSRMCResponse, wippersnapper_gps_GPSRMCResponse, AUTO) + + +PB_BIND(wippersnapper_gps_GPGGAResponse, wippersnapper_gps_GPGGAResponse, AUTO) + + +PB_BIND(wippersnapper_gps_GPSEvent, wippersnapper_gps_GPSEvent, 2) + + + diff --git a/src/protos/gps.pb.h b/src/protos/gps.pb.h new file mode 100644 index 000000000..28f54630c --- /dev/null +++ b/src/protos/gps.pb.h @@ -0,0 +1,203 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_WIPPERSNAPPER_GPS_GPS_PB_H_INCLUDED +#define PB_WIPPERSNAPPER_GPS_GPS_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Struct definitions */ +typedef PB_BYTES_ARRAY_T(128) wippersnapper_gps_GPSConfig_commands_ubxes_t; +/* * + GPSConfig 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. */ +typedef struct _wippersnapper_gps_GPSConfig { + /* NOTE: Baud rate is not included here as it is included in the UartAdd->UartSerialConfig message. */ + pb_size_t commands_pmtks_count; + char commands_pmtks[8][90]; /* * List of PMTK commands in string format. * */ + pb_size_t commands_ubxes_count; + wippersnapper_gps_GPSConfig_commands_ubxes_t commands_ubxes[8]; /* * List of UBX commands in bytes format. * */ + int32_t period; /* * Desired period to poll the GPS module, in milliseconds */ +} wippersnapper_gps_GPSConfig; + +/* * GPSDateTime represents the date and time information from a GPRMC/GPGGA string * */ +typedef struct _wippersnapper_gps_GPSDateTime { + int32_t hour; /* * Hour of the day (0-23) * */ + int32_t minute; /* * Minute of the hour (0-59) * */ + int32_t seconds; /* * Seconds of the minute (0-59) * */ + int32_t milliseconds; /* * Milliseconds (0-999) * */ + int32_t day; /* * Day of the month (1-31) * */ + int32_t month; /* * Month of the year (1-12) * */ + int32_t year; /* * Year (e.g., 2023) * */ +} wippersnapper_gps_GPSDateTime; + +/* * GPSRMCResponse represents the response from a GPS RMC (Recommended Minimum Specific GPS/Transit Data) message. * */ +typedef struct _wippersnapper_gps_GPSRMCResponse { + bool has_datetime; + wippersnapper_gps_GPSDateTime datetime; /* * Date and time of the GPS data * */ + char fix_status[2]; /* * Fix status: 'A' for active, 'V' for void * */ + char lat[12]; /* * Latitude in decimal degrees * */ + char lat_dir[6]; /* * Latitude direction: 'North' or 'South' * */ + char lon[12]; /* * Longitude in decimal degrees * */ + char lon_dir[6]; /* * Longitude direction: 'East' or 'West' * */ + char speed[8]; /* * Speed, in knots * */ + char angle[7]; /* * Course/heading angle, in degrees * */ +} wippersnapper_gps_GPSRMCResponse; + +/* * GPGGAResponse represents the response from a GPS GGA (Global Positioning System Fix Data) message. * */ +typedef struct _wippersnapper_gps_GPGGAResponse { + bool has_datetime; + wippersnapper_gps_GPSDateTime datetime; /* * Date and time of the GPS data * */ + char lat[12]; /* * Latitude in decimal degrees * */ + char lat_dir[6]; /* * Latitude direction: 'N' or 'S' * */ + char lon[12]; /* * Longitude in decimal degrees * */ + char lon_dir[6]; /* * Longitude direction: 'E' or 'W' * */ + int32_t fix_quality; /* * Quality of the GPS fix (0-3) * */ + int32_t num_satellites; /* * Number of satellites in use * */ + char hdop[6]; /* * HDOP- horizontal dilution of precision * */ + char altitude[8]; /* * Altitude in meters above MSL * */ + char geoid_height[10]; /* * Diff between geoid height and WGS84 height * */ +} wippersnapper_gps_GPGGAResponse; + +/* * GPSEvent represents a collection of GPS event responses, including RMC and GGA data. * */ +typedef struct _wippersnapper_gps_GPSEvent { + pb_size_t rmc_responses_count; + wippersnapper_gps_GPSRMCResponse rmc_responses[10]; /* * List of RMC responses * */ + pb_size_t gga_responses_count; + wippersnapper_gps_GPGGAResponse gga_responses[10]; /* * List of GGA responses * */ +} wippersnapper_gps_GPSEvent; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define wippersnapper_gps_GPSConfig_init_default {0, {"", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0} +#define wippersnapper_gps_GPSDateTime_init_default {0, 0, 0, 0, 0, 0, 0} +#define wippersnapper_gps_GPSRMCResponse_init_default {false, wippersnapper_gps_GPSDateTime_init_default, "", "", "", "", "", "", ""} +#define wippersnapper_gps_GPGGAResponse_init_default {false, wippersnapper_gps_GPSDateTime_init_default, "", "", "", "", 0, 0, "", "", ""} +#define wippersnapper_gps_GPSEvent_init_default {0, {wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default, wippersnapper_gps_GPSRMCResponse_init_default}, 0, {wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default, wippersnapper_gps_GPGGAResponse_init_default}} +#define wippersnapper_gps_GPSConfig_init_zero {0, {"", "", "", "", "", "", "", ""}, 0, {{0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}}, 0} +#define wippersnapper_gps_GPSDateTime_init_zero {0, 0, 0, 0, 0, 0, 0} +#define wippersnapper_gps_GPSRMCResponse_init_zero {false, wippersnapper_gps_GPSDateTime_init_zero, "", "", "", "", "", "", ""} +#define wippersnapper_gps_GPGGAResponse_init_zero {false, wippersnapper_gps_GPSDateTime_init_zero, "", "", "", "", 0, 0, "", "", ""} +#define wippersnapper_gps_GPSEvent_init_zero {0, {wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero, wippersnapper_gps_GPSRMCResponse_init_zero}, 0, {wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero, wippersnapper_gps_GPGGAResponse_init_zero}} + +/* Field tags (for use in manual encoding/decoding) */ +#define wippersnapper_gps_GPSConfig_commands_pmtks_tag 1 +#define wippersnapper_gps_GPSConfig_commands_ubxes_tag 2 +#define wippersnapper_gps_GPSConfig_period_tag 3 +#define wippersnapper_gps_GPSDateTime_hour_tag 1 +#define wippersnapper_gps_GPSDateTime_minute_tag 2 +#define wippersnapper_gps_GPSDateTime_seconds_tag 3 +#define wippersnapper_gps_GPSDateTime_milliseconds_tag 4 +#define wippersnapper_gps_GPSDateTime_day_tag 5 +#define wippersnapper_gps_GPSDateTime_month_tag 6 +#define wippersnapper_gps_GPSDateTime_year_tag 7 +#define wippersnapper_gps_GPSRMCResponse_datetime_tag 1 +#define wippersnapper_gps_GPSRMCResponse_fix_status_tag 2 +#define wippersnapper_gps_GPSRMCResponse_lat_tag 3 +#define wippersnapper_gps_GPSRMCResponse_lat_dir_tag 4 +#define wippersnapper_gps_GPSRMCResponse_lon_tag 5 +#define wippersnapper_gps_GPSRMCResponse_lon_dir_tag 6 +#define wippersnapper_gps_GPSRMCResponse_speed_tag 7 +#define wippersnapper_gps_GPSRMCResponse_angle_tag 8 +#define wippersnapper_gps_GPGGAResponse_datetime_tag 1 +#define wippersnapper_gps_GPGGAResponse_lat_tag 2 +#define wippersnapper_gps_GPGGAResponse_lat_dir_tag 3 +#define wippersnapper_gps_GPGGAResponse_lon_tag 4 +#define wippersnapper_gps_GPGGAResponse_lon_dir_tag 5 +#define wippersnapper_gps_GPGGAResponse_fix_quality_tag 6 +#define wippersnapper_gps_GPGGAResponse_num_satellites_tag 7 +#define wippersnapper_gps_GPGGAResponse_hdop_tag 8 +#define wippersnapper_gps_GPGGAResponse_altitude_tag 9 +#define wippersnapper_gps_GPGGAResponse_geoid_height_tag 10 +#define wippersnapper_gps_GPSEvent_rmc_responses_tag 1 +#define wippersnapper_gps_GPSEvent_gga_responses_tag 2 + +/* Struct field encoding specification for nanopb */ +#define wippersnapper_gps_GPSConfig_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, STRING, commands_pmtks, 1) \ +X(a, STATIC, REPEATED, BYTES, commands_ubxes, 2) \ +X(a, STATIC, SINGULAR, INT32, period, 3) +#define wippersnapper_gps_GPSConfig_CALLBACK NULL +#define wippersnapper_gps_GPSConfig_DEFAULT NULL + +#define wippersnapper_gps_GPSDateTime_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, hour, 1) \ +X(a, STATIC, SINGULAR, INT32, minute, 2) \ +X(a, STATIC, SINGULAR, INT32, seconds, 3) \ +X(a, STATIC, SINGULAR, INT32, milliseconds, 4) \ +X(a, STATIC, SINGULAR, INT32, day, 5) \ +X(a, STATIC, SINGULAR, INT32, month, 6) \ +X(a, STATIC, SINGULAR, INT32, year, 7) +#define wippersnapper_gps_GPSDateTime_CALLBACK NULL +#define wippersnapper_gps_GPSDateTime_DEFAULT NULL + +#define wippersnapper_gps_GPSRMCResponse_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, datetime, 1) \ +X(a, STATIC, SINGULAR, STRING, fix_status, 2) \ +X(a, STATIC, SINGULAR, STRING, lat, 3) \ +X(a, STATIC, SINGULAR, STRING, lat_dir, 4) \ +X(a, STATIC, SINGULAR, STRING, lon, 5) \ +X(a, STATIC, SINGULAR, STRING, lon_dir, 6) \ +X(a, STATIC, SINGULAR, STRING, speed, 7) \ +X(a, STATIC, SINGULAR, STRING, angle, 8) +#define wippersnapper_gps_GPSRMCResponse_CALLBACK NULL +#define wippersnapper_gps_GPSRMCResponse_DEFAULT NULL +#define wippersnapper_gps_GPSRMCResponse_datetime_MSGTYPE wippersnapper_gps_GPSDateTime + +#define wippersnapper_gps_GPGGAResponse_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, datetime, 1) \ +X(a, STATIC, SINGULAR, STRING, lat, 2) \ +X(a, STATIC, SINGULAR, STRING, lat_dir, 3) \ +X(a, STATIC, SINGULAR, STRING, lon, 4) \ +X(a, STATIC, SINGULAR, STRING, lon_dir, 5) \ +X(a, STATIC, SINGULAR, INT32, fix_quality, 6) \ +X(a, STATIC, SINGULAR, INT32, num_satellites, 7) \ +X(a, STATIC, SINGULAR, STRING, hdop, 8) \ +X(a, STATIC, SINGULAR, STRING, altitude, 9) \ +X(a, STATIC, SINGULAR, STRING, geoid_height, 10) +#define wippersnapper_gps_GPGGAResponse_CALLBACK NULL +#define wippersnapper_gps_GPGGAResponse_DEFAULT NULL +#define wippersnapper_gps_GPGGAResponse_datetime_MSGTYPE wippersnapper_gps_GPSDateTime + +#define wippersnapper_gps_GPSEvent_FIELDLIST(X, a) \ +X(a, STATIC, REPEATED, MESSAGE, rmc_responses, 1) \ +X(a, STATIC, REPEATED, MESSAGE, gga_responses, 2) +#define wippersnapper_gps_GPSEvent_CALLBACK NULL +#define wippersnapper_gps_GPSEvent_DEFAULT NULL +#define wippersnapper_gps_GPSEvent_rmc_responses_MSGTYPE wippersnapper_gps_GPSRMCResponse +#define wippersnapper_gps_GPSEvent_gga_responses_MSGTYPE wippersnapper_gps_GPGGAResponse + +extern const pb_msgdesc_t wippersnapper_gps_GPSConfig_msg; +extern const pb_msgdesc_t wippersnapper_gps_GPSDateTime_msg; +extern const pb_msgdesc_t wippersnapper_gps_GPSRMCResponse_msg; +extern const pb_msgdesc_t wippersnapper_gps_GPGGAResponse_msg; +extern const pb_msgdesc_t wippersnapper_gps_GPSEvent_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define wippersnapper_gps_GPSConfig_fields &wippersnapper_gps_GPSConfig_msg +#define wippersnapper_gps_GPSDateTime_fields &wippersnapper_gps_GPSDateTime_msg +#define wippersnapper_gps_GPSRMCResponse_fields &wippersnapper_gps_GPSRMCResponse_msg +#define wippersnapper_gps_GPGGAResponse_fields &wippersnapper_gps_GPGGAResponse_msg +#define wippersnapper_gps_GPSEvent_fields &wippersnapper_gps_GPSEvent_msg + +/* Maximum encoded size of messages (where known) */ +#define WIPPERSNAPPER_GPS_GPS_PB_H_MAX_SIZE wippersnapper_gps_GPSEvent_size +#define wippersnapper_gps_GPGGAResponse_size 168 +#define wippersnapper_gps_GPSConfig_size 1787 +#define wippersnapper_gps_GPSDateTime_size 77 +#define wippersnapper_gps_GPSEvent_size 3130 +#define wippersnapper_gps_GPSRMCResponse_size 139 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/protos/i2c.pb.c b/src/protos/i2c.pb.c index 88655b887..f3abb4134 100644 --- a/src/protos/i2c.pb.c +++ b/src/protos/i2c.pb.c @@ -18,7 +18,7 @@ PB_BIND(wippersnapper_i2c_I2cBusScan, wippersnapper_i2c_I2cBusScan, AUTO) PB_BIND(wippersnapper_i2c_I2cBusScanned, wippersnapper_i2c_I2cBusScanned, 4) -PB_BIND(wippersnapper_i2c_I2cDeviceAddOrReplace, wippersnapper_i2c_I2cDeviceAddOrReplace, AUTO) +PB_BIND(wippersnapper_i2c_I2cDeviceAddOrReplace, wippersnapper_i2c_I2cDeviceAddOrReplace, 2) PB_BIND(wippersnapper_i2c_I2cDeviceAddedOrReplaced, wippersnapper_i2c_I2cDeviceAddedOrReplaced, AUTO) @@ -33,6 +33,9 @@ PB_BIND(wippersnapper_i2c_I2cDeviceRemoved, wippersnapper_i2c_I2cDeviceRemoved, PB_BIND(wippersnapper_i2c_I2cDeviceEvent, wippersnapper_i2c_I2cDeviceEvent, 2) +PB_BIND(wippersnapper_i2c_I2cDeviceOutputWrite, wippersnapper_i2c_I2cDeviceOutputWrite, 2) + + diff --git a/src/protos/i2c.pb.h b/src/protos/i2c.pb.h index 8d410131d..78992ac52 100644 --- a/src/protos/i2c.pb.h +++ b/src/protos/i2c.pb.h @@ -4,6 +4,8 @@ #ifndef PB_WIPPERSNAPPER_I2C_I2C_PB_H_INCLUDED #define PB_WIPPERSNAPPER_I2C_I2C_PB_H_INCLUDED #include +#include "gps.pb.h" +#include "i2c_output.pb.h" #include "sensor.pb.h" #if PB_PROTO_HEADER_VERSION != 40 @@ -82,6 +84,12 @@ typedef struct _wippersnapper_i2c_I2cDeviceAddOrReplace { pb_size_t i2c_device_sensor_types_count; wippersnapper_sensor_SensorType i2c_device_sensor_types[15]; /* * SI Types for each sensor on the I2c device. */ bool is_persistent; /* * Offline-Mode ONLY - True if the device exits in the config file, False otherwise. * */ + bool is_output; /* * Required by the device to determine if the component is an output.* */ + bool has_i2c_output_add; + wippersnapper_i2c_output_I2cOutputAdd i2c_output_add; /* * Optional - If the I2C device is an output device, fill this field. * */ + bool is_gps; /* * Required by the device to determine if the component is a GPS.* */ + bool has_gps_config; + wippersnapper_gps_GPSConfig gps_config; /* * Optional - If the I2C device is a GPS driver, fill this field with the GPS config. * */ } wippersnapper_i2c_I2cDeviceAddOrReplace; /* * @@ -98,6 +106,7 @@ typedef struct _wippersnapper_i2c_I2cDeviceAddedOrReplaced { typedef struct _wippersnapper_i2c_I2cDeviceRemove { bool has_i2c_device_description; wippersnapper_i2c_I2cDeviceDescriptor i2c_device_description; /* * The I2c device's address and metadata. */ + bool is_output_device; /* * Determines if the device is an output device.* */ } wippersnapper_i2c_I2cDeviceRemove; /* * @@ -116,9 +125,22 @@ typedef struct _wippersnapper_i2c_I2cDeviceEvent { bool has_i2c_device_description; wippersnapper_i2c_I2cDeviceDescriptor i2c_device_description; /* * The I2c device's address and metadata. */ pb_size_t i2c_device_events_count; - wippersnapper_sensor_SensorEvent i2c_device_events[15]; /* * A, optionally repeated, SensorEvent from a sensor. */ + wippersnapper_sensor_SensorEvent i2c_device_events[15]; /* * Required, but optionally repeated, SensorEvent from a sensor. */ } wippersnapper_i2c_I2cDeviceEvent; +/* * + I2cDeviceOutputWrite represents a request to write to an I2C output device. */ +typedef struct _wippersnapper_i2c_I2cDeviceOutputWrite { + bool has_i2c_device_description; + wippersnapper_i2c_I2cDeviceDescriptor i2c_device_description; /* * Required - The I2c device's address and metadata. */ + pb_size_t which_output_msg; + union { + wippersnapper_i2c_output_LedBackpackWrite write_led_backpack; /* * Optional - If the I2C device is a LED backpack, fill this field. * */ + wippersnapper_i2c_output_CharLCDWrite write_char_lcd; /* * Optional - If the I2C device is a character LCD, fill this field. * */ + wippersnapper_i2c_output_OLEDWrite write_oled; /* * Optional - If the I2C device is an OLED display, fill this field. * */ + } output_msg; +} wippersnapper_i2c_I2cDeviceOutputWrite; + #ifdef __cplusplus extern "C" { @@ -147,25 +169,28 @@ extern "C" { + /* Initializer values for message structs */ #define wippersnapper_i2c_I2cDeviceDescriptor_init_default {"", "", 0, 0, 0} #define wippersnapper_i2c_I2cBusDescriptor_init_default {"", ""} #define wippersnapper_i2c_I2cBusScan_init_default {0, 0, false, wippersnapper_i2c_I2cBusDescriptor_init_default, 0, 0} #define wippersnapper_i2c_I2cBusScanned_init_default {0, {wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default, wippersnapper_i2c_I2cDeviceDescriptor_init_default}, _wippersnapper_i2c_I2cBusStatus_MIN} -#define wippersnapper_i2c_I2cDeviceAddOrReplace_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, "", 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}, 0} +#define wippersnapper_i2c_I2cDeviceAddOrReplace_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, "", 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}, 0, 0, false, wippersnapper_i2c_output_I2cOutputAdd_init_default, 0, false, wippersnapper_gps_GPSConfig_init_default} #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, _wippersnapper_i2c_I2cBusStatus_MIN, _wippersnapper_i2c_I2cDeviceStatus_MIN} -#define wippersnapper_i2c_I2cDeviceRemove_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default} +#define wippersnapper_i2c_I2cDeviceRemove_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, 0} #define wippersnapper_i2c_I2cDeviceRemoved_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, 0} #define wippersnapper_i2c_I2cDeviceEvent_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, 0, {wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default}} +#define wippersnapper_i2c_I2cDeviceOutputWrite_init_default {false, wippersnapper_i2c_I2cDeviceDescriptor_init_default, 0, {wippersnapper_i2c_output_LedBackpackWrite_init_default}} #define wippersnapper_i2c_I2cDeviceDescriptor_init_zero {"", "", 0, 0, 0} #define wippersnapper_i2c_I2cBusDescriptor_init_zero {"", ""} #define wippersnapper_i2c_I2cBusScan_init_zero {0, 0, false, wippersnapper_i2c_I2cBusDescriptor_init_zero, 0, 0} #define wippersnapper_i2c_I2cBusScanned_init_zero {0, {wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, wippersnapper_i2c_I2cDeviceDescriptor_init_zero}, _wippersnapper_i2c_I2cBusStatus_MIN} -#define wippersnapper_i2c_I2cDeviceAddOrReplace_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, "", 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}, 0} +#define wippersnapper_i2c_I2cDeviceAddOrReplace_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, "", 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}, 0, 0, false, wippersnapper_i2c_output_I2cOutputAdd_init_zero, 0, false, wippersnapper_gps_GPSConfig_init_zero} #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, _wippersnapper_i2c_I2cBusStatus_MIN, _wippersnapper_i2c_I2cDeviceStatus_MIN} -#define wippersnapper_i2c_I2cDeviceRemove_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero} +#define wippersnapper_i2c_I2cDeviceRemove_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, 0} #define wippersnapper_i2c_I2cDeviceRemoved_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, 0} #define wippersnapper_i2c_I2cDeviceEvent_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, 0, {wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero}} +#define wippersnapper_i2c_I2cDeviceOutputWrite_init_zero {false, wippersnapper_i2c_I2cDeviceDescriptor_init_zero, 0, {wippersnapper_i2c_output_LedBackpackWrite_init_zero}} /* Field tags (for use in manual encoding/decoding) */ #define wippersnapper_i2c_I2cDeviceDescriptor_i2c_bus_sda_tag 1 @@ -187,14 +212,23 @@ extern "C" { #define wippersnapper_i2c_I2cDeviceAddOrReplace_i2c_device_period_tag 3 #define wippersnapper_i2c_I2cDeviceAddOrReplace_i2c_device_sensor_types_tag 4 #define wippersnapper_i2c_I2cDeviceAddOrReplace_is_persistent_tag 5 +#define wippersnapper_i2c_I2cDeviceAddOrReplace_is_output_tag 6 +#define wippersnapper_i2c_I2cDeviceAddOrReplace_i2c_output_add_tag 7 +#define wippersnapper_i2c_I2cDeviceAddOrReplace_is_gps_tag 8 +#define wippersnapper_i2c_I2cDeviceAddOrReplace_gps_config_tag 9 #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_i2c_device_description_tag 1 #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_i2c_bus_status_tag 2 #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_i2c_device_status_tag 3 #define wippersnapper_i2c_I2cDeviceRemove_i2c_device_description_tag 1 +#define wippersnapper_i2c_I2cDeviceRemove_is_output_device_tag 2 #define wippersnapper_i2c_I2cDeviceRemoved_i2c_device_description_tag 1 #define wippersnapper_i2c_I2cDeviceRemoved_did_remove_tag 2 #define wippersnapper_i2c_I2cDeviceEvent_i2c_device_description_tag 1 #define wippersnapper_i2c_I2cDeviceEvent_i2c_device_events_tag 2 +#define wippersnapper_i2c_I2cDeviceOutputWrite_i2c_device_description_tag 1 +#define wippersnapper_i2c_I2cDeviceOutputWrite_write_led_backpack_tag 2 +#define wippersnapper_i2c_I2cDeviceOutputWrite_write_char_lcd_tag 3 +#define wippersnapper_i2c_I2cDeviceOutputWrite_write_oled_tag 4 /* Struct field encoding specification for nanopb */ #define wippersnapper_i2c_I2cDeviceDescriptor_FIELDLIST(X, a) \ @@ -234,10 +268,16 @@ X(a, STATIC, OPTIONAL, MESSAGE, i2c_device_description, 1) \ X(a, STATIC, SINGULAR, STRING, i2c_device_name, 2) \ X(a, STATIC, SINGULAR, FLOAT, i2c_device_period, 3) \ X(a, STATIC, REPEATED, UENUM, i2c_device_sensor_types, 4) \ -X(a, STATIC, SINGULAR, BOOL, is_persistent, 5) +X(a, STATIC, SINGULAR, BOOL, is_persistent, 5) \ +X(a, STATIC, SINGULAR, BOOL, is_output, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, i2c_output_add, 7) \ +X(a, STATIC, SINGULAR, BOOL, is_gps, 8) \ +X(a, STATIC, OPTIONAL, MESSAGE, gps_config, 9) #define wippersnapper_i2c_I2cDeviceAddOrReplace_CALLBACK NULL #define wippersnapper_i2c_I2cDeviceAddOrReplace_DEFAULT NULL #define wippersnapper_i2c_I2cDeviceAddOrReplace_i2c_device_description_MSGTYPE wippersnapper_i2c_I2cDeviceDescriptor +#define wippersnapper_i2c_I2cDeviceAddOrReplace_i2c_output_add_MSGTYPE wippersnapper_i2c_output_I2cOutputAdd +#define wippersnapper_i2c_I2cDeviceAddOrReplace_gps_config_MSGTYPE wippersnapper_gps_GPSConfig #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, i2c_device_description, 1) \ @@ -248,7 +288,8 @@ X(a, STATIC, SINGULAR, UENUM, i2c_device_status, 3) #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_i2c_device_description_MSGTYPE wippersnapper_i2c_I2cDeviceDescriptor #define wippersnapper_i2c_I2cDeviceRemove_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, i2c_device_description, 1) +X(a, STATIC, OPTIONAL, MESSAGE, i2c_device_description, 1) \ +X(a, STATIC, SINGULAR, BOOL, is_output_device, 2) #define wippersnapper_i2c_I2cDeviceRemove_CALLBACK NULL #define wippersnapper_i2c_I2cDeviceRemove_DEFAULT NULL #define wippersnapper_i2c_I2cDeviceRemove_i2c_device_description_MSGTYPE wippersnapper_i2c_I2cDeviceDescriptor @@ -268,6 +309,18 @@ X(a, STATIC, REPEATED, MESSAGE, i2c_device_events, 2) #define wippersnapper_i2c_I2cDeviceEvent_i2c_device_description_MSGTYPE wippersnapper_i2c_I2cDeviceDescriptor #define wippersnapper_i2c_I2cDeviceEvent_i2c_device_events_MSGTYPE wippersnapper_sensor_SensorEvent +#define wippersnapper_i2c_I2cDeviceOutputWrite_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, i2c_device_description, 1) \ +X(a, STATIC, ONEOF, MESSAGE, (output_msg,write_led_backpack,output_msg.write_led_backpack), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (output_msg,write_char_lcd,output_msg.write_char_lcd), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (output_msg,write_oled,output_msg.write_oled), 4) +#define wippersnapper_i2c_I2cDeviceOutputWrite_CALLBACK NULL +#define wippersnapper_i2c_I2cDeviceOutputWrite_DEFAULT NULL +#define wippersnapper_i2c_I2cDeviceOutputWrite_i2c_device_description_MSGTYPE wippersnapper_i2c_I2cDeviceDescriptor +#define wippersnapper_i2c_I2cDeviceOutputWrite_output_msg_write_led_backpack_MSGTYPE wippersnapper_i2c_output_LedBackpackWrite +#define wippersnapper_i2c_I2cDeviceOutputWrite_output_msg_write_char_lcd_MSGTYPE wippersnapper_i2c_output_CharLCDWrite +#define wippersnapper_i2c_I2cDeviceOutputWrite_output_msg_write_oled_MSGTYPE wippersnapper_i2c_output_OLEDWrite + extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceDescriptor_msg; extern const pb_msgdesc_t wippersnapper_i2c_I2cBusDescriptor_msg; extern const pb_msgdesc_t wippersnapper_i2c_I2cBusScan_msg; @@ -277,6 +330,7 @@ extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceAddedOrReplaced_msg; extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceRemove_msg; extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceRemoved_msg; extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceEvent_msg; +extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceOutputWrite_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define wippersnapper_i2c_I2cDeviceDescriptor_fields &wippersnapper_i2c_I2cDeviceDescriptor_msg @@ -288,16 +342,18 @@ extern const pb_msgdesc_t wippersnapper_i2c_I2cDeviceEvent_msg; #define wippersnapper_i2c_I2cDeviceRemove_fields &wippersnapper_i2c_I2cDeviceRemove_msg #define wippersnapper_i2c_I2cDeviceRemoved_fields &wippersnapper_i2c_I2cDeviceRemoved_msg #define wippersnapper_i2c_I2cDeviceEvent_fields &wippersnapper_i2c_I2cDeviceEvent_msg +#define wippersnapper_i2c_I2cDeviceOutputWrite_fields &wippersnapper_i2c_I2cDeviceOutputWrite_msg /* Maximum encoded size of messages (where known) */ #define WIPPERSNAPPER_I2C_I2C_PB_H_MAX_SIZE wippersnapper_i2c_I2cBusScanned_size #define wippersnapper_i2c_I2cBusDescriptor_size 32 #define wippersnapper_i2c_I2cBusScan_size 42 #define wippersnapper_i2c_I2cBusScanned_size 6242 -#define wippersnapper_i2c_I2cDeviceAddOrReplace_size 105 +#define wippersnapper_i2c_I2cDeviceAddOrReplace_size 1917 #define wippersnapper_i2c_I2cDeviceAddedOrReplaced_size 56 #define wippersnapper_i2c_I2cDeviceDescriptor_size 50 -#define wippersnapper_i2c_I2cDeviceRemove_size 52 +#define wippersnapper_i2c_I2cDeviceOutputWrite_size 569 +#define wippersnapper_i2c_I2cDeviceRemove_size 54 #define wippersnapper_i2c_I2cDeviceRemoved_size 54 #if defined(wippersnapper_sensor_SensorEvent_size) #define wippersnapper_i2c_I2cDeviceEvent_size (142 + 15*wippersnapper_sensor_SensorEvent_size) diff --git a/src/protos/i2c_output.pb.c b/src/protos/i2c_output.pb.c new file mode 100644 index 000000000..d8474b88b --- /dev/null +++ b/src/protos/i2c_output.pb.c @@ -0,0 +1,32 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "i2c_output.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(wippersnapper_i2c_output_LedBackpackConfig, wippersnapper_i2c_output_LedBackpackConfig, AUTO) + + +PB_BIND(wippersnapper_i2c_output_CharLCDConfig, wippersnapper_i2c_output_CharLCDConfig, AUTO) + + +PB_BIND(wippersnapper_i2c_output_OledConfig, wippersnapper_i2c_output_OledConfig, AUTO) + + +PB_BIND(wippersnapper_i2c_output_I2cOutputAdd, wippersnapper_i2c_output_I2cOutputAdd, AUTO) + + +PB_BIND(wippersnapper_i2c_output_LedBackpackWrite, wippersnapper_i2c_output_LedBackpackWrite, AUTO) + + +PB_BIND(wippersnapper_i2c_output_CharLCDWrite, wippersnapper_i2c_output_CharLCDWrite, AUTO) + + +PB_BIND(wippersnapper_i2c_output_OLEDWrite, wippersnapper_i2c_output_OLEDWrite, 2) + + + + + diff --git a/src/protos/i2c_output.pb.h b/src/protos/i2c_output.pb.h new file mode 100644 index 000000000..e14e12dfd --- /dev/null +++ b/src/protos/i2c_output.pb.h @@ -0,0 +1,215 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_WIPPERSNAPPER_I2C_OUTPUT_I2C_OUTPUT_PB_H_INCLUDED +#define PB_WIPPERSNAPPER_I2C_OUTPUT_I2C_OUTPUT_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +/* * + LedBackpackAlignment represents all text alignment option for LED backpack displays */ +typedef enum _wippersnapper_i2c_output_LedBackpackAlignment { + wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_UNSPECIFIED = 0, /* * Unspecified alignment option. * */ + wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_LEFT = 1, /* * (Default) Left-aligned. * */ + wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_RIGHT = 2 /* * Right-aligned. * */ +} wippersnapper_i2c_output_LedBackpackAlignment; + +/* * + Desired OLED display text 'magnification' size. */ +typedef enum _wippersnapper_i2c_output_OledTextSize { + wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_UNSPECIFIED = 0, /* * Unspecified text size. * */ + wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_DEFAULT = 1, /* * Default text size, 6x8px. * */ + wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_LARGE = 2 /* * Larger text size option, 12x16px. * */ +} wippersnapper_i2c_output_OledTextSize; + +/* Struct definitions */ +/* * + LedBackpackConfig represents the configuration for a LED backpack display. */ +typedef struct _wippersnapper_i2c_output_LedBackpackConfig { + int32_t brightness; /* * Desired brightness of the LED backpack, from 0 (off) to 15 (full brightness). * */ + wippersnapper_i2c_output_LedBackpackAlignment alignment; /* * Desired text alignment for the LED backpack. * */ +} wippersnapper_i2c_output_LedBackpackConfig; + +/* * + CharLCDConfig represents the configuration for a character LCD display. */ +typedef struct _wippersnapper_i2c_output_CharLCDConfig { + uint32_t rows; /* * Number of rows for the character LCD. * */ + uint32_t columns; /* * Number of columns for the character LCD. * */ +} wippersnapper_i2c_output_CharLCDConfig; + +/* * + OledConfig represents the configuration for a OLED display. */ +typedef struct _wippersnapper_i2c_output_OledConfig { + uint32_t width; /* * Width of the OLED display in pixels. * */ + uint32_t height; /* * Height of the OLED display in pixels. * */ + wippersnapper_i2c_output_OledTextSize font_size; /* * Desired font magnification for the OLED display. Defaults to OLED_TEXT_SIZE_DEFAULT. * */ +} wippersnapper_i2c_output_OledConfig; + +/* * + I2cOutputAdd represents a request from the broker to add an I2C output device to a device. */ +typedef struct _wippersnapper_i2c_output_I2cOutputAdd { + pb_size_t which_config; + union { + wippersnapper_i2c_output_LedBackpackConfig led_backpack_config; /* * Configuration for a LED backpack. * */ + wippersnapper_i2c_output_CharLCDConfig char_lcd_config; /* * Configuration for a character LCD. * */ + wippersnapper_i2c_output_OledConfig oled_config; /* * Configuration for an OLED display. * */ + } config; +} wippersnapper_i2c_output_I2cOutputAdd; + +/* * + LedBackpackWrite represents a request from the broker to write a message to a LED backpack. */ +typedef struct _wippersnapper_i2c_output_LedBackpackWrite { + char message[8]; /* * Message to write to the LED backpack. * */ +} wippersnapper_i2c_output_LedBackpackWrite; + +/* * + CharLCDWrite represents a request from the broker to write to a character LCD. */ +typedef struct _wippersnapper_i2c_output_CharLCDWrite { + char message[100]; /* * Message to write to the character LCD. * */ + bool enable_backlight; /* * Whether to enable the backlight. Defaults to True. * */ +} wippersnapper_i2c_output_CharLCDWrite; + +/* * + OLEDWrite represents a request from the broker to write to a OLED display. */ +typedef struct _wippersnapper_i2c_output_OLEDWrite { + char message[512]; /* * Message to write to an OLED display. * */ +} wippersnapper_i2c_output_OLEDWrite; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _wippersnapper_i2c_output_LedBackpackAlignment_MIN wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_UNSPECIFIED +#define _wippersnapper_i2c_output_LedBackpackAlignment_MAX wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_RIGHT +#define _wippersnapper_i2c_output_LedBackpackAlignment_ARRAYSIZE ((wippersnapper_i2c_output_LedBackpackAlignment)(wippersnapper_i2c_output_LedBackpackAlignment_LED_BACKPACK_ALIGNMENT_RIGHT+1)) + +#define _wippersnapper_i2c_output_OledTextSize_MIN wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_UNSPECIFIED +#define _wippersnapper_i2c_output_OledTextSize_MAX wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_LARGE +#define _wippersnapper_i2c_output_OledTextSize_ARRAYSIZE ((wippersnapper_i2c_output_OledTextSize)(wippersnapper_i2c_output_OledTextSize_OLED_TEXT_SIZE_LARGE+1)) + +#define wippersnapper_i2c_output_LedBackpackConfig_alignment_ENUMTYPE wippersnapper_i2c_output_LedBackpackAlignment + + +#define wippersnapper_i2c_output_OledConfig_font_size_ENUMTYPE wippersnapper_i2c_output_OledTextSize + + + + + + +/* Initializer values for message structs */ +#define wippersnapper_i2c_output_LedBackpackConfig_init_default {0, _wippersnapper_i2c_output_LedBackpackAlignment_MIN} +#define wippersnapper_i2c_output_CharLCDConfig_init_default {0, 0} +#define wippersnapper_i2c_output_OledConfig_init_default {0, 0, _wippersnapper_i2c_output_OledTextSize_MIN} +#define wippersnapper_i2c_output_I2cOutputAdd_init_default {0, {wippersnapper_i2c_output_LedBackpackConfig_init_default}} +#define wippersnapper_i2c_output_LedBackpackWrite_init_default {""} +#define wippersnapper_i2c_output_CharLCDWrite_init_default {"", 0} +#define wippersnapper_i2c_output_OLEDWrite_init_default {""} +#define wippersnapper_i2c_output_LedBackpackConfig_init_zero {0, _wippersnapper_i2c_output_LedBackpackAlignment_MIN} +#define wippersnapper_i2c_output_CharLCDConfig_init_zero {0, 0} +#define wippersnapper_i2c_output_OledConfig_init_zero {0, 0, _wippersnapper_i2c_output_OledTextSize_MIN} +#define wippersnapper_i2c_output_I2cOutputAdd_init_zero {0, {wippersnapper_i2c_output_LedBackpackConfig_init_zero}} +#define wippersnapper_i2c_output_LedBackpackWrite_init_zero {""} +#define wippersnapper_i2c_output_CharLCDWrite_init_zero {"", 0} +#define wippersnapper_i2c_output_OLEDWrite_init_zero {""} + +/* Field tags (for use in manual encoding/decoding) */ +#define wippersnapper_i2c_output_LedBackpackConfig_brightness_tag 1 +#define wippersnapper_i2c_output_LedBackpackConfig_alignment_tag 2 +#define wippersnapper_i2c_output_CharLCDConfig_rows_tag 1 +#define wippersnapper_i2c_output_CharLCDConfig_columns_tag 2 +#define wippersnapper_i2c_output_OledConfig_width_tag 1 +#define wippersnapper_i2c_output_OledConfig_height_tag 2 +#define wippersnapper_i2c_output_OledConfig_font_size_tag 3 +#define wippersnapper_i2c_output_I2cOutputAdd_led_backpack_config_tag 1 +#define wippersnapper_i2c_output_I2cOutputAdd_char_lcd_config_tag 2 +#define wippersnapper_i2c_output_I2cOutputAdd_oled_config_tag 3 +#define wippersnapper_i2c_output_LedBackpackWrite_message_tag 1 +#define wippersnapper_i2c_output_CharLCDWrite_message_tag 1 +#define wippersnapper_i2c_output_CharLCDWrite_enable_backlight_tag 2 +#define wippersnapper_i2c_output_OLEDWrite_message_tag 1 + +/* Struct field encoding specification for nanopb */ +#define wippersnapper_i2c_output_LedBackpackConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, INT32, brightness, 1) \ +X(a, STATIC, SINGULAR, UENUM, alignment, 2) +#define wippersnapper_i2c_output_LedBackpackConfig_CALLBACK NULL +#define wippersnapper_i2c_output_LedBackpackConfig_DEFAULT NULL + +#define wippersnapper_i2c_output_CharLCDConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, rows, 1) \ +X(a, STATIC, SINGULAR, UINT32, columns, 2) +#define wippersnapper_i2c_output_CharLCDConfig_CALLBACK NULL +#define wippersnapper_i2c_output_CharLCDConfig_DEFAULT NULL + +#define wippersnapper_i2c_output_OledConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, width, 1) \ +X(a, STATIC, SINGULAR, UINT32, height, 2) \ +X(a, STATIC, SINGULAR, UENUM, font_size, 3) +#define wippersnapper_i2c_output_OledConfig_CALLBACK NULL +#define wippersnapper_i2c_output_OledConfig_DEFAULT NULL + +#define wippersnapper_i2c_output_I2cOutputAdd_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (config,led_backpack_config,config.led_backpack_config), 1) \ +X(a, STATIC, ONEOF, MESSAGE, (config,char_lcd_config,config.char_lcd_config), 2) \ +X(a, STATIC, ONEOF, MESSAGE, (config,oled_config,config.oled_config), 3) +#define wippersnapper_i2c_output_I2cOutputAdd_CALLBACK NULL +#define wippersnapper_i2c_output_I2cOutputAdd_DEFAULT NULL +#define wippersnapper_i2c_output_I2cOutputAdd_config_led_backpack_config_MSGTYPE wippersnapper_i2c_output_LedBackpackConfig +#define wippersnapper_i2c_output_I2cOutputAdd_config_char_lcd_config_MSGTYPE wippersnapper_i2c_output_CharLCDConfig +#define wippersnapper_i2c_output_I2cOutputAdd_config_oled_config_MSGTYPE wippersnapper_i2c_output_OledConfig + +#define wippersnapper_i2c_output_LedBackpackWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, message, 1) +#define wippersnapper_i2c_output_LedBackpackWrite_CALLBACK NULL +#define wippersnapper_i2c_output_LedBackpackWrite_DEFAULT NULL + +#define wippersnapper_i2c_output_CharLCDWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, message, 1) \ +X(a, STATIC, SINGULAR, BOOL, enable_backlight, 2) +#define wippersnapper_i2c_output_CharLCDWrite_CALLBACK NULL +#define wippersnapper_i2c_output_CharLCDWrite_DEFAULT NULL + +#define wippersnapper_i2c_output_OLEDWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, message, 1) +#define wippersnapper_i2c_output_OLEDWrite_CALLBACK NULL +#define wippersnapper_i2c_output_OLEDWrite_DEFAULT NULL + +extern const pb_msgdesc_t wippersnapper_i2c_output_LedBackpackConfig_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_CharLCDConfig_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_OledConfig_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_I2cOutputAdd_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_LedBackpackWrite_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_CharLCDWrite_msg; +extern const pb_msgdesc_t wippersnapper_i2c_output_OLEDWrite_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define wippersnapper_i2c_output_LedBackpackConfig_fields &wippersnapper_i2c_output_LedBackpackConfig_msg +#define wippersnapper_i2c_output_CharLCDConfig_fields &wippersnapper_i2c_output_CharLCDConfig_msg +#define wippersnapper_i2c_output_OledConfig_fields &wippersnapper_i2c_output_OledConfig_msg +#define wippersnapper_i2c_output_I2cOutputAdd_fields &wippersnapper_i2c_output_I2cOutputAdd_msg +#define wippersnapper_i2c_output_LedBackpackWrite_fields &wippersnapper_i2c_output_LedBackpackWrite_msg +#define wippersnapper_i2c_output_CharLCDWrite_fields &wippersnapper_i2c_output_CharLCDWrite_msg +#define wippersnapper_i2c_output_OLEDWrite_fields &wippersnapper_i2c_output_OLEDWrite_msg + +/* Maximum encoded size of messages (where known) */ +#define WIPPERSNAPPER_I2C_OUTPUT_I2C_OUTPUT_PB_H_MAX_SIZE wippersnapper_i2c_output_OLEDWrite_size +#define wippersnapper_i2c_output_CharLCDConfig_size 12 +#define wippersnapper_i2c_output_CharLCDWrite_size 103 +#define wippersnapper_i2c_output_I2cOutputAdd_size 16 +#define wippersnapper_i2c_output_LedBackpackConfig_size 13 +#define wippersnapper_i2c_output_LedBackpackWrite_size 9 +#define wippersnapper_i2c_output_OLEDWrite_size 514 +#define wippersnapper_i2c_output_OledConfig_size 14 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/protos/signal.pb.h b/src/protos/signal.pb.h index 1f187c687..c328eea49 100644 --- a/src/protos/signal.pb.h +++ b/src/protos/signal.pb.h @@ -9,7 +9,9 @@ #include "digitalio.pb.h" #include "ds18x20.pb.h" #include "error.pb.h" +#include "gps.pb.h" #include "i2c.pb.h" +#include "i2c_output.pb.h" #include "pixels.pb.h" #include "pwm.pb.h" #include "servo.pb.h" @@ -56,12 +58,14 @@ typedef struct _wippersnapper_signal_BrokerToDevice { wippersnapper_ds18x20_Ds18x20Add ds18x20_add; wippersnapper_ds18x20_Ds18x20Remove ds18x20_remove; /* uart.proto */ - wippersnapper_uart_UARTAdd uart_add; - wippersnapper_uart_UARTRemove uart_remove; + wippersnapper_uart_UartAdd uart_add; + wippersnapper_uart_UartRemove uart_remove; + wippersnapper_uart_UartWrite uart_write; /* i2c.proto */ wippersnapper_i2c_I2cBusScan i2c_bus_scan; wippersnapper_i2c_I2cDeviceAddOrReplace i2c_device_add_replace; wippersnapper_i2c_I2cDeviceRemove i2c_device_remove; + wippersnapper_i2c_I2cDeviceOutputWrite i2c_device_output_write; /* error.proto */ wippersnapper_error_Error error; } payload; @@ -91,13 +95,16 @@ typedef struct _wippersnapper_signal_DeviceToBroker { wippersnapper_ds18x20_Ds18x20Added ds18x20_added; wippersnapper_ds18x20_Ds18x20Event ds18x20_event; /* uart.proto */ - wippersnapper_uart_UARTAdded uart_added; - wippersnapper_uart_UARTEvent uart_event; + wippersnapper_uart_UartAdded uart_added; + wippersnapper_uart_UartWritten uart_written; + wippersnapper_uart_UartInputEvent uart_input_event; /* i2c.proto */ wippersnapper_i2c_I2cBusScanned i2c_bus_scanned; wippersnapper_i2c_I2cDeviceAddedOrReplaced i2c_device_added_replaced; wippersnapper_i2c_I2cDeviceRemoved i2c_device_removed; wippersnapper_i2c_I2cDeviceEvent i2c_device_event; + /* gps.proto */ + wippersnapper_gps_GPSEvent gps_event; } payload; } wippersnapper_signal_DeviceToBroker; @@ -135,9 +142,11 @@ extern "C" { #define wippersnapper_signal_BrokerToDevice_ds18x20_remove_tag 71 #define wippersnapper_signal_BrokerToDevice_uart_add_tag 80 #define wippersnapper_signal_BrokerToDevice_uart_remove_tag 81 +#define wippersnapper_signal_BrokerToDevice_uart_write_tag 82 #define wippersnapper_signal_BrokerToDevice_i2c_bus_scan_tag 90 #define wippersnapper_signal_BrokerToDevice_i2c_device_add_replace_tag 91 #define wippersnapper_signal_BrokerToDevice_i2c_device_remove_tag 92 +#define wippersnapper_signal_BrokerToDevice_i2c_device_output_write_tag 93 #define wippersnapper_signal_BrokerToDevice_error_tag 100 #define wippersnapper_signal_DeviceToBroker_digitalio_event_tag 10 #define wippersnapper_signal_DeviceToBroker_analogio_event_tag 20 @@ -148,11 +157,13 @@ extern "C" { #define wippersnapper_signal_DeviceToBroker_ds18x20_added_tag 70 #define wippersnapper_signal_DeviceToBroker_ds18x20_event_tag 80 #define wippersnapper_signal_DeviceToBroker_uart_added_tag 90 -#define wippersnapper_signal_DeviceToBroker_uart_event_tag 100 +#define wippersnapper_signal_DeviceToBroker_uart_written_tag 91 +#define wippersnapper_signal_DeviceToBroker_uart_input_event_tag 92 #define wippersnapper_signal_DeviceToBroker_i2c_bus_scanned_tag 110 #define wippersnapper_signal_DeviceToBroker_i2c_device_added_replaced_tag 111 #define wippersnapper_signal_DeviceToBroker_i2c_device_removed_tag 112 #define wippersnapper_signal_DeviceToBroker_i2c_device_event_tag 113 +#define wippersnapper_signal_DeviceToBroker_gps_event_tag 120 /* Struct field encoding specification for nanopb */ #define wippersnapper_signal_BrokerToDevice_FIELDLIST(X, a) \ @@ -178,9 +189,11 @@ X(a, STATIC, ONEOF, MSG_W_CB, (payload,ds18x20_add,payload.ds18x20_add), 7 X(a, STATIC, ONEOF, MSG_W_CB, (payload,ds18x20_remove,payload.ds18x20_remove), 71) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_add,payload.uart_add), 80) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_remove,payload.uart_remove), 81) \ +X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_write,payload.uart_write), 82) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_bus_scan,payload.i2c_bus_scan), 90) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_add_replace,payload.i2c_device_add_replace), 91) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_remove,payload.i2c_device_remove), 92) \ +X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_output_write,payload.i2c_device_output_write), 93) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,error,payload.error), 100) #define wippersnapper_signal_BrokerToDevice_CALLBACK NULL #define wippersnapper_signal_BrokerToDevice_DEFAULT NULL @@ -204,11 +217,13 @@ X(a, STATIC, ONEOF, MSG_W_CB, (payload,error,payload.error), 100) #define wippersnapper_signal_BrokerToDevice_payload_pixels_write_MSGTYPE wippersnapper_pixels_PixelsWrite #define wippersnapper_signal_BrokerToDevice_payload_ds18x20_add_MSGTYPE wippersnapper_ds18x20_Ds18x20Add #define wippersnapper_signal_BrokerToDevice_payload_ds18x20_remove_MSGTYPE wippersnapper_ds18x20_Ds18x20Remove -#define wippersnapper_signal_BrokerToDevice_payload_uart_add_MSGTYPE wippersnapper_uart_UARTAdd -#define wippersnapper_signal_BrokerToDevice_payload_uart_remove_MSGTYPE wippersnapper_uart_UARTRemove +#define wippersnapper_signal_BrokerToDevice_payload_uart_add_MSGTYPE wippersnapper_uart_UartAdd +#define wippersnapper_signal_BrokerToDevice_payload_uart_remove_MSGTYPE wippersnapper_uart_UartRemove +#define wippersnapper_signal_BrokerToDevice_payload_uart_write_MSGTYPE wippersnapper_uart_UartWrite #define wippersnapper_signal_BrokerToDevice_payload_i2c_bus_scan_MSGTYPE wippersnapper_i2c_I2cBusScan #define wippersnapper_signal_BrokerToDevice_payload_i2c_device_add_replace_MSGTYPE wippersnapper_i2c_I2cDeviceAddOrReplace #define wippersnapper_signal_BrokerToDevice_payload_i2c_device_remove_MSGTYPE wippersnapper_i2c_I2cDeviceRemove +#define wippersnapper_signal_BrokerToDevice_payload_i2c_device_output_write_MSGTYPE wippersnapper_i2c_I2cDeviceOutputWrite #define wippersnapper_signal_BrokerToDevice_payload_error_MSGTYPE wippersnapper_error_Error #define wippersnapper_signal_DeviceToBroker_FIELDLIST(X, a) \ @@ -221,11 +236,13 @@ X(a, STATIC, ONEOF, MSG_W_CB, (payload,pixels_added,payload.pixels_added), X(a, STATIC, ONEOF, MSG_W_CB, (payload,ds18x20_added,payload.ds18x20_added), 70) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,ds18x20_event,payload.ds18x20_event), 80) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_added,payload.uart_added), 90) \ -X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_event,payload.uart_event), 100) \ +X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_written,payload.uart_written), 91) \ +X(a, STATIC, ONEOF, MSG_W_CB, (payload,uart_input_event,payload.uart_input_event), 92) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_bus_scanned,payload.i2c_bus_scanned), 110) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_added_replaced,payload.i2c_device_added_replaced), 111) \ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_removed,payload.i2c_device_removed), 112) \ -X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_event,payload.i2c_device_event), 113) +X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_event,payload.i2c_device_event), 113) \ +X(a, STATIC, ONEOF, MSG_W_CB, (payload,gps_event,payload.gps_event), 120) #define wippersnapper_signal_DeviceToBroker_CALLBACK NULL #define wippersnapper_signal_DeviceToBroker_DEFAULT NULL #define wippersnapper_signal_DeviceToBroker_payload_digitalio_event_MSGTYPE wippersnapper_digitalio_DigitalIOEvent @@ -236,12 +253,14 @@ X(a, STATIC, ONEOF, MSG_W_CB, (payload,i2c_device_event,payload.i2c_device_ #define wippersnapper_signal_DeviceToBroker_payload_pixels_added_MSGTYPE wippersnapper_pixels_PixelsAdded #define wippersnapper_signal_DeviceToBroker_payload_ds18x20_added_MSGTYPE wippersnapper_ds18x20_Ds18x20Added #define wippersnapper_signal_DeviceToBroker_payload_ds18x20_event_MSGTYPE wippersnapper_ds18x20_Ds18x20Event -#define wippersnapper_signal_DeviceToBroker_payload_uart_added_MSGTYPE wippersnapper_uart_UARTAdded -#define wippersnapper_signal_DeviceToBroker_payload_uart_event_MSGTYPE wippersnapper_uart_UARTEvent +#define wippersnapper_signal_DeviceToBroker_payload_uart_added_MSGTYPE wippersnapper_uart_UartAdded +#define wippersnapper_signal_DeviceToBroker_payload_uart_written_MSGTYPE wippersnapper_uart_UartWritten +#define wippersnapper_signal_DeviceToBroker_payload_uart_input_event_MSGTYPE wippersnapper_uart_UartInputEvent #define wippersnapper_signal_DeviceToBroker_payload_i2c_bus_scanned_MSGTYPE wippersnapper_i2c_I2cBusScanned #define wippersnapper_signal_DeviceToBroker_payload_i2c_device_added_replaced_MSGTYPE wippersnapper_i2c_I2cDeviceAddedOrReplaced #define wippersnapper_signal_DeviceToBroker_payload_i2c_device_removed_MSGTYPE wippersnapper_i2c_I2cDeviceRemoved #define wippersnapper_signal_DeviceToBroker_payload_i2c_device_event_MSGTYPE wippersnapper_i2c_I2cDeviceEvent +#define wippersnapper_signal_DeviceToBroker_payload_gps_event_MSGTYPE wippersnapper_gps_GPSEvent extern const pb_msgdesc_t wippersnapper_signal_BrokerToDevice_msg; extern const pb_msgdesc_t wippersnapper_signal_DeviceToBroker_msg; @@ -251,17 +270,17 @@ extern const pb_msgdesc_t wippersnapper_signal_DeviceToBroker_msg; #define wippersnapper_signal_DeviceToBroker_fields &wippersnapper_signal_DeviceToBroker_msg /* Maximum encoded size of messages (where known) */ -#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_digitalio_DigitalIOWrite_size) && defined(wippersnapper_uart_UARTAdd_size) && defined(wippersnapper_uart_UARTRemove_size) -union wippersnapper_signal_BrokerToDevice_payload_size_union {char f12[(6 + wippersnapper_digitalio_DigitalIOEvent_size)]; char f13[(6 + wippersnapper_digitalio_DigitalIOWrite_size)]; char f80[(7 + wippersnapper_uart_UARTAdd_size)]; char f81[(7 + wippersnapper_uart_UARTRemove_size)]; char f0[108];}; +#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_digitalio_DigitalIOWrite_size) && defined(wippersnapper_uart_UartAdd_size) && defined(wippersnapper_uart_UartWrite_size) +union wippersnapper_signal_BrokerToDevice_payload_size_union {char f12[(6 + wippersnapper_digitalio_DigitalIOEvent_size)]; char f13[(6 + wippersnapper_digitalio_DigitalIOWrite_size)]; char f80[(7 + wippersnapper_uart_UartAdd_size)]; char f82[(7 + wippersnapper_uart_UartWrite_size)]; char f0[1921];}; #endif -#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_analogio_AnalogIOEvent_size) && defined(wippersnapper_ds18x20_Ds18x20Event_size) && defined(wippersnapper_uart_UARTAdded_size) && defined(wippersnapper_uart_UARTEvent_size) && defined(wippersnapper_i2c_I2cDeviceEvent_size) -union wippersnapper_signal_DeviceToBroker_payload_size_union {char f10[(6 + wippersnapper_digitalio_DigitalIOEvent_size)]; char f20[(7 + wippersnapper_analogio_AnalogIOEvent_size)]; char f80[(7 + wippersnapper_ds18x20_Ds18x20Event_size)]; char f90[(7 + wippersnapper_uart_UARTAdded_size)]; char f100[(7 + wippersnapper_uart_UARTEvent_size)]; char f113[(7 + wippersnapper_i2c_I2cDeviceEvent_size)]; char f0[6246];}; +#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_analogio_AnalogIOEvent_size) && defined(wippersnapper_ds18x20_Ds18x20Event_size) && defined(wippersnapper_uart_UartInputEvent_size) && defined(wippersnapper_i2c_I2cDeviceEvent_size) +union wippersnapper_signal_DeviceToBroker_payload_size_union {char f10[(6 + wippersnapper_digitalio_DigitalIOEvent_size)]; char f20[(7 + wippersnapper_analogio_AnalogIOEvent_size)]; char f80[(7 + wippersnapper_ds18x20_Ds18x20Event_size)]; char f92[(7 + wippersnapper_uart_UartInputEvent_size)]; char f113[(7 + wippersnapper_i2c_I2cDeviceEvent_size)]; char f0[6246];}; #endif -#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_digitalio_DigitalIOWrite_size) && defined(wippersnapper_uart_UARTAdd_size) && defined(wippersnapper_uart_UARTRemove_size) +#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_digitalio_DigitalIOWrite_size) && defined(wippersnapper_uart_UartAdd_size) && defined(wippersnapper_uart_UartWrite_size) #define WIPPERSNAPPER_SIGNAL_SIGNAL_PB_H_MAX_SIZE wippersnapper_signal_BrokerToDevice_size #define wippersnapper_signal_BrokerToDevice_size (0 + sizeof(union wippersnapper_signal_BrokerToDevice_payload_size_union)) #endif -#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_analogio_AnalogIOEvent_size) && defined(wippersnapper_ds18x20_Ds18x20Event_size) && defined(wippersnapper_uart_UARTAdded_size) && defined(wippersnapper_uart_UARTEvent_size) && defined(wippersnapper_i2c_I2cDeviceEvent_size) +#if defined(wippersnapper_digitalio_DigitalIOEvent_size) && defined(wippersnapper_analogio_AnalogIOEvent_size) && defined(wippersnapper_ds18x20_Ds18x20Event_size) && defined(wippersnapper_uart_UartInputEvent_size) && defined(wippersnapper_i2c_I2cDeviceEvent_size) #define wippersnapper_signal_DeviceToBroker_size (0 + sizeof(union wippersnapper_signal_DeviceToBroker_payload_size_union)) #endif diff --git a/src/protos/uart.pb.c b/src/protos/uart.pb.c index 827e9c1d6..4490968a3 100644 --- a/src/protos/uart.pb.c +++ b/src/protos/uart.pb.c @@ -6,19 +6,40 @@ #error Regenerate this file with the current version of nanopb generator. #endif -PB_BIND(wippersnapper_uart_UARTBusData, wippersnapper_uart_UARTBusData, AUTO) +PB_BIND(wippersnapper_uart_UartSerialConfig, wippersnapper_uart_UartSerialConfig, AUTO) -PB_BIND(wippersnapper_uart_UARTAdd, wippersnapper_uart_UARTAdd, AUTO) +PB_BIND(wippersnapper_uart_GenericUartInputConfig, wippersnapper_uart_GenericUartInputConfig, AUTO) -PB_BIND(wippersnapper_uart_UARTAdded, wippersnapper_uart_UARTAdded, AUTO) +PB_BIND(wippersnapper_uart_TrinamicDynamixelConfig, wippersnapper_uart_TrinamicDynamixelConfig, AUTO) -PB_BIND(wippersnapper_uart_UARTRemove, wippersnapper_uart_UARTRemove, AUTO) +PB_BIND(wippersnapper_uart_PM25AQIConfig, wippersnapper_uart_PM25AQIConfig, AUTO) + + +PB_BIND(wippersnapper_uart_UartDeviceConfig, wippersnapper_uart_UartDeviceConfig, 2) + + +PB_BIND(wippersnapper_uart_UartAdd, wippersnapper_uart_UartAdd, 2) + + +PB_BIND(wippersnapper_uart_UartAdded, wippersnapper_uart_UartAdded, AUTO) + + +PB_BIND(wippersnapper_uart_UartRemove, wippersnapper_uart_UartRemove, AUTO) + + +PB_BIND(wippersnapper_uart_UartWrite, wippersnapper_uart_UartWrite, AUTO) + + +PB_BIND(wippersnapper_uart_UartWritten, wippersnapper_uart_UartWritten, AUTO) + + +PB_BIND(wippersnapper_uart_UartInputEvent, wippersnapper_uart_UartInputEvent, 2) + -PB_BIND(wippersnapper_uart_UARTEvent, wippersnapper_uart_UARTEvent, AUTO) diff --git a/src/protos/uart.pb.h b/src/protos/uart.pb.h index 6c4e07eaa..11200fd24 100644 --- a/src/protos/uart.pb.h +++ b/src/protos/uart.pb.h @@ -4,141 +4,445 @@ #ifndef PB_WIPPERSNAPPER_UART_UART_PB_H_INCLUDED #define PB_WIPPERSNAPPER_UART_UART_PB_H_INCLUDED #include +#include "gps.pb.h" #include "sensor.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. #endif +/* Enum definitions */ +/* * + UartPacketFormat contains the configuration data, parity, and stop bits for a serial port. */ +typedef enum _wippersnapper_uart_UartPacketFormat { + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_UNSPECIFIED = 0, /* Format was not specified by IO. */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8N1 = 1, /* 8 data bits, no parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5N1 = 2, /* 5 data bits, no parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6N1 = 3, /* 6 data bits, no parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7N1 = 4, /* 7 data bits, no parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5N2 = 5, /* 5 data bits, no parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6N2 = 6, /* 6 data bits, no parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7N2 = 7, /* 7 data bits, no parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8N2 = 8, /* 8 data bits, no parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5E1 = 9, /* 5 data bits, even parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6E1 = 10, /* 6 data bits, even parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7E1 = 11, /* 7 data bits, even parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8E1 = 12, /* 8 data bits, even parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5E2 = 13, /* 5 data bits, even parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6E2 = 14, /* 6 data bits, even parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7E2 = 15, /* 7 data bits, even parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8E2 = 16, /* 8 data bits, even parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5O1 = 17, /* 5 data bits, odd parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6O1 = 18, /* 6 data bits, odd parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7O1 = 19, /* 7 data bits, odd parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O1 = 20, /* 8 data bits, odd parity, 1 stop bit */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_5O2 = 21, /* 5 data bits, odd parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_6O2 = 22, /* 6 data bits, odd parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_7O2 = 23, /* 7 data bits, odd parity, 2 stop bits */ + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O2 = 24 /* 8 data bits, odd parity, 2 stop bits */ +} wippersnapper_uart_UartPacketFormat; + +/* * + UartDeviceType represents the type of device connected to the UART port. + This is used to determine the driver to use for the device. */ +typedef enum _wippersnapper_uart_UartDeviceType { + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_UNSPECIFIED = 0, /* * Unspecified device type. */ + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_INPUT = 1, /* * Use UART input. */ + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_OUTPUT = 2, /* * Use the Generic UART output driver. */ + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GPS = 3, /* * Use the GPS driver. */ + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_PM25AQI = 4, /* * Use the PM2.5 driver. */ + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_TM22XX = 5 /* * Use the TM22XX stepper driver. */ +} wippersnapper_uart_UartDeviceType; + +/* * + GenericDeviceLineEnding represents the line ending used by the device. + This is used to determine how to parse the incoming data. */ +typedef enum _wippersnapper_uart_GenericDeviceLineEnding { + wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_UNSPECIFIED = 0, /* * Unspecified line ending. */ + wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_LF = 1, /* * Newline (LF). */ + wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_CRLF = 2, /* * Carriage return (CR) and newline (LF). */ + wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_100MS = 3, /* * 100ms timeout - sensor returns every 100ms with a new Event. */ + wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_1000MS = 4 /* * 1s timeout - device returns every 1s with a new Event. */ +} wippersnapper_uart_GenericDeviceLineEnding; + /* Struct definitions */ /* * - 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 UARTAdd. */ -typedef struct _wippersnapper_uart_UARTBusData { - int32_t baudrate; /* * The baudrate to use for UART communication (may be a common baud rate such as: -1200bps, 2400bps, 4800bps, 19200bps, 38400bps, 57600bps, or 115200bps). */ - char pin_rx[6]; /* * The pin on which to receive UART stream data. */ - char pin_tx[6]; /* * The pin on which to transmit UART stream data. */ - bool is_invert; /* * Inverts the UART signal on RX and TX pins. Defaults to False. */ -} wippersnapper_uart_UARTBusData; + UartSerialConfig represents a message to configure the Serial (eg: HardwareSerial, SoftwareSerial). + This message is never sent directly, it is packed inside UartAdd. */ +typedef struct _wippersnapper_uart_UartSerialConfig { + char pin_rx[16]; /* * The pin on which to receive on. */ + char pin_tx[16]; /* * The pin on which to transmit with. */ + uint32_t uart_nbr; /* * The UART port number to use, eg: 0, 1, 2, etc. */ + uint32_t baud_rate; /* * The desired baudrate, in bits per second. */ + wippersnapper_uart_UartPacketFormat format; /* * The data, parity, and stop bits. */ + float timeout; /* * Maximum milliseconds to wait for serial data. Defaults to 1000 ms. */ + bool use_sw_serial; /* * Use software serial instead of hardware serial. Defaults to False. */ + bool sw_serial_invert; /* * Optional: Inverts the UART signal on RX and TX pins. Defaults to False. */ +} wippersnapper_uart_UartSerialConfig; + +/* * + GenericUartInputConfig represents a message sent from IO to a device + containing device-specific configuration info for generic UART input devices. */ +typedef struct _wippersnapper_uart_GenericUartInputConfig { + pb_callback_t name; /* * The name used to identify the device. */ + wippersnapper_uart_GenericDeviceLineEnding line_ending; /* * The line ending used by the device. */ + int32_t period; /* * The period to poll the device, in milliseconds */ + pb_size_t sensor_types_count; + wippersnapper_sensor_SensorType sensor_types[15]; /* * SI Types for each sensor on the UART device. */ +} wippersnapper_uart_GenericUartInputConfig; + +/* * + TrinamicDynamixelConfig represents a message sent from IO to a device + containing device-specific configuration info for Trinamic stepper or DYNAMIXEL servos. */ +typedef struct _wippersnapper_uart_TrinamicDynamixelConfig { + uint32_t device_id; /* * The device identifier, used for sub-addressing (multiple servos on one UART). */ +} wippersnapper_uart_TrinamicDynamixelConfig; + +/* * + PM25AQIConfig represents a message sent from IO to a device + containing device-specific configuration info for PM2.5 AQI sensors. */ +typedef struct _wippersnapper_uart_PM25AQIConfig { + bool is_pm1006; /* * True if the device is a PM1006 AQ sensor, Defaults to False. */ + int32_t period; /* * The period to poll the device, in milliseconds */ + pb_size_t sensor_types_count; + wippersnapper_sensor_SensorType sensor_types[15]; /* * SI Types for each sensor on the I2c device. */ +} wippersnapper_uart_PM25AQIConfig; /* * - UARTAdd represents a message sent from IO to a device - to configure the UART bus (if not already configured) and attach a device. */ -typedef struct _wippersnapper_uart_UARTAdd { - bool has_bus_info; - wippersnapper_uart_UARTBusData bus_info; /* * The UART bus configuration. */ - pb_callback_t device_id; /* * The unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - int32_t polling_interval; /* * The polling interval, in milliseconds, to use for the device. */ -} wippersnapper_uart_UARTAdd; + UartDeviceConfig represents a message sent from IO to a device + containing device-specific configuration data. + This message is never sent directly, it is packed inside UartAdd. */ +typedef struct _wippersnapper_uart_UartDeviceConfig { + wippersnapper_uart_UartDeviceType device_type; /* * The type of device connected to the UART port. */ + char device_id[32]; /* * The unique identifier string for the UART device. */ + pb_size_t which_config; + union { + wippersnapper_uart_GenericUartInputConfig generic_uart_input; /* * OPTIONAL configuration for a generic UART input device. */ + wippersnapper_uart_TrinamicDynamixelConfig trinamic_dynamixel; /* * OPTIONAL configuration for a Trinamic stepper or DYNAMIXEL servo. */ + wippersnapper_uart_PM25AQIConfig pm25aqi; /* * OPTIONAL configuration for a PM2.5 AQI sensor. */ + wippersnapper_gps_GPSConfig gps; /* * OPTIONAL configuration for a GPS RMC response. */ + } config; +} wippersnapper_uart_UartDeviceConfig; /* * - UARTAdded represents a message sent from a device to IO to - confirm that a device has been attached to the UART bus. */ -typedef struct _wippersnapper_uart_UARTAdded { - pb_callback_t device_id; /* * The unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - bool is_success; /* * True if the UARTInit was successful, False otherwise. */ -} wippersnapper_uart_UARTAdded; - -/* UARTRemove represents a message sent from IO to a device - to detach a device from the UART bus. */ -typedef struct _wippersnapper_uart_UARTRemove { - pb_callback_t device_id; /* * The unique identifier of the device to detach from the UART bus. */ -} wippersnapper_uart_UARTRemove; + UartAdd represents a message sent from IO to a device + to configure a device on a UART port for communication. */ +typedef struct _wippersnapper_uart_UartAdd { + bool has_cfg_serial; + wippersnapper_uart_UartSerialConfig cfg_serial; /* * The Serial configuration. */ + bool has_cfg_device; + wippersnapper_uart_UartDeviceConfig cfg_device; /* * The device-specific configuration. */ +} wippersnapper_uart_UartAdd; /* * - UARTEvent represents incoming data from a UART sensor. */ -typedef struct _wippersnapper_uart_UARTEvent { - pb_callback_t device_id; /* * Unique identifier of the device to attach to the UART bus, from Adafruit_WipperSnapper_Components. */ - pb_callback_t sensor_events; /* * An optionally repeated event from a sensor. */ -} wippersnapper_uart_UARTEvent; + UartAdded represents a message sent from a device to IO to + confirm that a device has been attached to the UART port. */ +typedef struct _wippersnapper_uart_UartAdded { + /* Addressing */ + uint32_t uart_nbr; /* * The UART port number (eg: 0, 1, 2, etc.) that the device was attached to. */ + wippersnapper_uart_UartDeviceType type; /* * The category of device attached to the UART port, corresponds to its driver type. */ + char device_id[32]; /* * The unique identifier string for the UART device. */ + /* Payload */ + bool success; /* * True if the device on the UART port was successfully initialized, False otherwise. */ +} wippersnapper_uart_UartAdded; + +/* UartRemove represents a message sent from IO to a device + to detach a driver from the UART port and deinitialize the port. */ +typedef struct _wippersnapper_uart_UartRemove { + /* Addressing */ + uint32_t uart_nbr; /* * The UART port number (eg: 0, 1, 2, etc.) that the device is attached to. */ + wippersnapper_uart_UartDeviceType type; /* * The category of device attached to the UART port, corresponds to its driver type. */ + char device_id[32]; /* * The unique identifier string for the UART device. */ +} wippersnapper_uart_UartRemove; + +/* * + UartWrite represents a message sent from IO to a device + to write data to a device. */ +typedef struct _wippersnapper_uart_UartWrite { + /* Addressing */ + uint32_t uart_nbr; /* * The UART port number (eg: 0, 1, 2, etc.) that the device is attached to. */ + wippersnapper_uart_UartDeviceType type; /* * The category of device attached to the UART port, corresponds to its driver type. */ + char device_id[32]; /* * The unique identifier string for the UART device. */ + pb_size_t which_payload; + union { + pb_callback_t bytes_data; /* * Raw data to send to the device, corresponds to the Wiring API Serial.write(). */ + pb_callback_t text_data; /* * String to send to the device, corresponds to the Wiring API Serial.print(). */ + } payload; +} wippersnapper_uart_UartWrite; + +/* * + UartWritten 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. */ +typedef struct _wippersnapper_uart_UartWritten { + /* Addressing */ + uint32_t uart_nbr; /* * The UART port number (eg: 0, 1, 2, etc.) that the device is attached to. */ + wippersnapper_uart_UartDeviceType type; /* * The category of device attached to the UART port, corresponds to its driver type. */ + char device_id[32]; /* * The unique identifier string for the UART device. */ + /* Payload */ + uint32_t bytes_written; /* * The number of bytes written to the device. */ +} wippersnapper_uart_UartWritten; + +/* * + UartInputEvent 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. */ +typedef struct _wippersnapper_uart_UartInputEvent { + /* Addressing */ + uint32_t uart_nbr; /* * The UART port number (eg: 0, 1, 2, etc.) that the device is attached to. */ + wippersnapper_uart_UartDeviceType type; /* * The category of device attached to the UART port, corresponds to its driver type. */ + char device_id[16]; /* * The unique identifier string for the UART device. */ + /* Payload */ + pb_size_t events_count; + wippersnapper_sensor_SensorEvent events[15]; /* * Required, but optionally repeated, SensorEvent from a sensor. */ +} wippersnapper_uart_UartInputEvent; #ifdef __cplusplus extern "C" { #endif +/* Helper constants for enums */ +#define _wippersnapper_uart_UartPacketFormat_MIN wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_UNSPECIFIED +#define _wippersnapper_uart_UartPacketFormat_MAX wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O2 +#define _wippersnapper_uart_UartPacketFormat_ARRAYSIZE ((wippersnapper_uart_UartPacketFormat)(wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8O2+1)) + +#define _wippersnapper_uart_UartDeviceType_MIN wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_UNSPECIFIED +#define _wippersnapper_uart_UartDeviceType_MAX wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_TM22XX +#define _wippersnapper_uart_UartDeviceType_ARRAYSIZE ((wippersnapper_uart_UartDeviceType)(wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_TM22XX+1)) + +#define _wippersnapper_uart_GenericDeviceLineEnding_MIN wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_UNSPECIFIED +#define _wippersnapper_uart_GenericDeviceLineEnding_MAX wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_1000MS +#define _wippersnapper_uart_GenericDeviceLineEnding_ARRAYSIZE ((wippersnapper_uart_GenericDeviceLineEnding)(wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_1000MS+1)) + +#define wippersnapper_uart_UartSerialConfig_format_ENUMTYPE wippersnapper_uart_UartPacketFormat + +#define wippersnapper_uart_GenericUartInputConfig_line_ending_ENUMTYPE wippersnapper_uart_GenericDeviceLineEnding +#define wippersnapper_uart_GenericUartInputConfig_sensor_types_ENUMTYPE wippersnapper_sensor_SensorType + + +#define wippersnapper_uart_PM25AQIConfig_sensor_types_ENUMTYPE wippersnapper_sensor_SensorType + +#define wippersnapper_uart_UartDeviceConfig_device_type_ENUMTYPE wippersnapper_uart_UartDeviceType + + +#define wippersnapper_uart_UartAdded_type_ENUMTYPE wippersnapper_uart_UartDeviceType + +#define wippersnapper_uart_UartRemove_type_ENUMTYPE wippersnapper_uart_UartDeviceType + +#define wippersnapper_uart_UartWrite_type_ENUMTYPE wippersnapper_uart_UartDeviceType + +#define wippersnapper_uart_UartWritten_type_ENUMTYPE wippersnapper_uart_UartDeviceType + +#define wippersnapper_uart_UartInputEvent_type_ENUMTYPE wippersnapper_uart_UartDeviceType + + /* Initializer values for message structs */ -#define wippersnapper_uart_UARTBusData_init_default {0, "", "", 0} -#define wippersnapper_uart_UARTAdd_init_default {false, wippersnapper_uart_UARTBusData_init_default, {{NULL}, NULL}, 0} -#define wippersnapper_uart_UARTAdded_init_default {{{NULL}, NULL}, 0} -#define wippersnapper_uart_UARTRemove_init_default {{{NULL}, NULL}} -#define wippersnapper_uart_UARTEvent_init_default {{{NULL}, NULL}, {{NULL}, NULL}} -#define wippersnapper_uart_UARTBusData_init_zero {0, "", "", 0} -#define wippersnapper_uart_UARTAdd_init_zero {false, wippersnapper_uart_UARTBusData_init_zero, {{NULL}, NULL}, 0} -#define wippersnapper_uart_UARTAdded_init_zero {{{NULL}, NULL}, 0} -#define wippersnapper_uart_UARTRemove_init_zero {{{NULL}, NULL}} -#define wippersnapper_uart_UARTEvent_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define wippersnapper_uart_UartSerialConfig_init_default {"", "", 0, 0, _wippersnapper_uart_UartPacketFormat_MIN, 0, 0, 0} +#define wippersnapper_uart_GenericUartInputConfig_init_default {{{NULL}, NULL}, _wippersnapper_uart_GenericDeviceLineEnding_MIN, 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}} +#define wippersnapper_uart_TrinamicDynamixelConfig_init_default {0} +#define wippersnapper_uart_PM25AQIConfig_init_default {0, 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}} +#define wippersnapper_uart_UartDeviceConfig_init_default {_wippersnapper_uart_UartDeviceType_MIN, "", 0, {wippersnapper_uart_GenericUartInputConfig_init_default}} +#define wippersnapper_uart_UartAdd_init_default {false, wippersnapper_uart_UartSerialConfig_init_default, false, wippersnapper_uart_UartDeviceConfig_init_default} +#define wippersnapper_uart_UartAdded_init_default {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0} +#define wippersnapper_uart_UartRemove_init_default {0, _wippersnapper_uart_UartDeviceType_MIN, ""} +#define wippersnapper_uart_UartWrite_init_default {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0, {{{NULL}, NULL}}} +#define wippersnapper_uart_UartWritten_init_default {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0} +#define wippersnapper_uart_UartInputEvent_init_default {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0, {wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default, wippersnapper_sensor_SensorEvent_init_default}} +#define wippersnapper_uart_UartSerialConfig_init_zero {"", "", 0, 0, _wippersnapper_uart_UartPacketFormat_MIN, 0, 0, 0} +#define wippersnapper_uart_GenericUartInputConfig_init_zero {{{NULL}, NULL}, _wippersnapper_uart_GenericDeviceLineEnding_MIN, 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}} +#define wippersnapper_uart_TrinamicDynamixelConfig_init_zero {0} +#define wippersnapper_uart_PM25AQIConfig_init_zero {0, 0, 0, {_wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN, _wippersnapper_sensor_SensorType_MIN}} +#define wippersnapper_uart_UartDeviceConfig_init_zero {_wippersnapper_uart_UartDeviceType_MIN, "", 0, {wippersnapper_uart_GenericUartInputConfig_init_zero}} +#define wippersnapper_uart_UartAdd_init_zero {false, wippersnapper_uart_UartSerialConfig_init_zero, false, wippersnapper_uart_UartDeviceConfig_init_zero} +#define wippersnapper_uart_UartAdded_init_zero {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0} +#define wippersnapper_uart_UartRemove_init_zero {0, _wippersnapper_uart_UartDeviceType_MIN, ""} +#define wippersnapper_uart_UartWrite_init_zero {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0, {{{NULL}, NULL}}} +#define wippersnapper_uart_UartWritten_init_zero {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0} +#define wippersnapper_uart_UartInputEvent_init_zero {0, _wippersnapper_uart_UartDeviceType_MIN, "", 0, {wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero, wippersnapper_sensor_SensorEvent_init_zero}} /* Field tags (for use in manual encoding/decoding) */ -#define wippersnapper_uart_UARTBusData_baudrate_tag 1 -#define wippersnapper_uart_UARTBusData_pin_rx_tag 2 -#define wippersnapper_uart_UARTBusData_pin_tx_tag 3 -#define wippersnapper_uart_UARTBusData_is_invert_tag 4 -#define wippersnapper_uart_UARTAdd_bus_info_tag 1 -#define wippersnapper_uart_UARTAdd_device_id_tag 2 -#define wippersnapper_uart_UARTAdd_polling_interval_tag 3 -#define wippersnapper_uart_UARTAdded_device_id_tag 1 -#define wippersnapper_uart_UARTAdded_is_success_tag 2 -#define wippersnapper_uart_UARTRemove_device_id_tag 1 -#define wippersnapper_uart_UARTEvent_device_id_tag 1 -#define wippersnapper_uart_UARTEvent_sensor_events_tag 2 +#define wippersnapper_uart_UartSerialConfig_pin_rx_tag 1 +#define wippersnapper_uart_UartSerialConfig_pin_tx_tag 2 +#define wippersnapper_uart_UartSerialConfig_uart_nbr_tag 3 +#define wippersnapper_uart_UartSerialConfig_baud_rate_tag 4 +#define wippersnapper_uart_UartSerialConfig_format_tag 5 +#define wippersnapper_uart_UartSerialConfig_timeout_tag 6 +#define wippersnapper_uart_UartSerialConfig_use_sw_serial_tag 7 +#define wippersnapper_uart_UartSerialConfig_sw_serial_invert_tag 8 +#define wippersnapper_uart_GenericUartInputConfig_name_tag 1 +#define wippersnapper_uart_GenericUartInputConfig_line_ending_tag 2 +#define wippersnapper_uart_GenericUartInputConfig_period_tag 3 +#define wippersnapper_uart_GenericUartInputConfig_sensor_types_tag 4 +#define wippersnapper_uart_TrinamicDynamixelConfig_device_id_tag 1 +#define wippersnapper_uart_PM25AQIConfig_is_pm1006_tag 1 +#define wippersnapper_uart_PM25AQIConfig_period_tag 2 +#define wippersnapper_uart_PM25AQIConfig_sensor_types_tag 3 +#define wippersnapper_uart_UartDeviceConfig_device_type_tag 1 +#define wippersnapper_uart_UartDeviceConfig_device_id_tag 2 +#define wippersnapper_uart_UartDeviceConfig_generic_uart_input_tag 3 +#define wippersnapper_uart_UartDeviceConfig_trinamic_dynamixel_tag 4 +#define wippersnapper_uart_UartDeviceConfig_pm25aqi_tag 5 +#define wippersnapper_uart_UartDeviceConfig_gps_tag 6 +#define wippersnapper_uart_UartAdd_cfg_serial_tag 1 +#define wippersnapper_uart_UartAdd_cfg_device_tag 2 +#define wippersnapper_uart_UartAdded_uart_nbr_tag 1 +#define wippersnapper_uart_UartAdded_type_tag 2 +#define wippersnapper_uart_UartAdded_device_id_tag 3 +#define wippersnapper_uart_UartAdded_success_tag 4 +#define wippersnapper_uart_UartRemove_uart_nbr_tag 1 +#define wippersnapper_uart_UartRemove_type_tag 2 +#define wippersnapper_uart_UartRemove_device_id_tag 3 +#define wippersnapper_uart_UartWrite_uart_nbr_tag 1 +#define wippersnapper_uart_UartWrite_type_tag 2 +#define wippersnapper_uart_UartWrite_device_id_tag 3 +#define wippersnapper_uart_UartWrite_bytes_data_tag 4 +#define wippersnapper_uart_UartWrite_text_data_tag 5 +#define wippersnapper_uart_UartWritten_uart_nbr_tag 1 +#define wippersnapper_uart_UartWritten_type_tag 2 +#define wippersnapper_uart_UartWritten_device_id_tag 3 +#define wippersnapper_uart_UartWritten_bytes_written_tag 4 +#define wippersnapper_uart_UartInputEvent_uart_nbr_tag 1 +#define wippersnapper_uart_UartInputEvent_type_tag 2 +#define wippersnapper_uart_UartInputEvent_device_id_tag 3 +#define wippersnapper_uart_UartInputEvent_events_tag 4 /* Struct field encoding specification for nanopb */ -#define wippersnapper_uart_UARTBusData_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, INT32, baudrate, 1) \ -X(a, STATIC, SINGULAR, STRING, pin_rx, 2) \ -X(a, STATIC, SINGULAR, STRING, pin_tx, 3) \ -X(a, STATIC, SINGULAR, BOOL, is_invert, 4) -#define wippersnapper_uart_UARTBusData_CALLBACK NULL -#define wippersnapper_uart_UARTBusData_DEFAULT NULL - -#define wippersnapper_uart_UARTAdd_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, bus_info, 1) \ -X(a, CALLBACK, SINGULAR, STRING, device_id, 2) \ -X(a, STATIC, SINGULAR, INT32, polling_interval, 3) -#define wippersnapper_uart_UARTAdd_CALLBACK pb_default_field_callback -#define wippersnapper_uart_UARTAdd_DEFAULT NULL -#define wippersnapper_uart_UARTAdd_bus_info_MSGTYPE wippersnapper_uart_UARTBusData - -#define wippersnapper_uart_UARTAdded_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, device_id, 1) \ -X(a, STATIC, SINGULAR, BOOL, is_success, 2) -#define wippersnapper_uart_UARTAdded_CALLBACK pb_default_field_callback -#define wippersnapper_uart_UARTAdded_DEFAULT NULL - -#define wippersnapper_uart_UARTRemove_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, device_id, 1) -#define wippersnapper_uart_UARTRemove_CALLBACK pb_default_field_callback -#define wippersnapper_uart_UARTRemove_DEFAULT NULL - -#define wippersnapper_uart_UARTEvent_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, STRING, device_id, 1) \ -X(a, CALLBACK, REPEATED, MESSAGE, sensor_events, 2) -#define wippersnapper_uart_UARTEvent_CALLBACK pb_default_field_callback -#define wippersnapper_uart_UARTEvent_DEFAULT NULL -#define wippersnapper_uart_UARTEvent_sensor_events_MSGTYPE wippersnapper_sensor_SensorEvent - -extern const pb_msgdesc_t wippersnapper_uart_UARTBusData_msg; -extern const pb_msgdesc_t wippersnapper_uart_UARTAdd_msg; -extern const pb_msgdesc_t wippersnapper_uart_UARTAdded_msg; -extern const pb_msgdesc_t wippersnapper_uart_UARTRemove_msg; -extern const pb_msgdesc_t wippersnapper_uart_UARTEvent_msg; +#define wippersnapper_uart_UartSerialConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, STRING, pin_rx, 1) \ +X(a, STATIC, SINGULAR, STRING, pin_tx, 2) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 3) \ +X(a, STATIC, SINGULAR, UINT32, baud_rate, 4) \ +X(a, STATIC, SINGULAR, UENUM, format, 5) \ +X(a, STATIC, SINGULAR, FLOAT, timeout, 6) \ +X(a, STATIC, SINGULAR, BOOL, use_sw_serial, 7) \ +X(a, STATIC, SINGULAR, BOOL, sw_serial_invert, 8) +#define wippersnapper_uart_UartSerialConfig_CALLBACK NULL +#define wippersnapper_uart_UartSerialConfig_DEFAULT NULL + +#define wippersnapper_uart_GenericUartInputConfig_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, STRING, name, 1) \ +X(a, STATIC, SINGULAR, UENUM, line_ending, 2) \ +X(a, STATIC, SINGULAR, INT32, period, 3) \ +X(a, STATIC, REPEATED, UENUM, sensor_types, 4) +#define wippersnapper_uart_GenericUartInputConfig_CALLBACK pb_default_field_callback +#define wippersnapper_uart_GenericUartInputConfig_DEFAULT NULL + +#define wippersnapper_uart_TrinamicDynamixelConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, device_id, 1) +#define wippersnapper_uart_TrinamicDynamixelConfig_CALLBACK NULL +#define wippersnapper_uart_TrinamicDynamixelConfig_DEFAULT NULL + +#define wippersnapper_uart_PM25AQIConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, is_pm1006, 1) \ +X(a, STATIC, SINGULAR, INT32, period, 2) \ +X(a, STATIC, REPEATED, UENUM, sensor_types, 3) +#define wippersnapper_uart_PM25AQIConfig_CALLBACK NULL +#define wippersnapper_uart_PM25AQIConfig_DEFAULT NULL + +#define wippersnapper_uart_UartDeviceConfig_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, device_type, 1) \ +X(a, STATIC, SINGULAR, STRING, device_id, 2) \ +X(a, STATIC, ONEOF, MESSAGE, (config,generic_uart_input,config.generic_uart_input), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (config,trinamic_dynamixel,config.trinamic_dynamixel), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (config,pm25aqi,config.pm25aqi), 5) \ +X(a, STATIC, ONEOF, MESSAGE, (config,gps,config.gps), 6) +#define wippersnapper_uart_UartDeviceConfig_CALLBACK NULL +#define wippersnapper_uart_UartDeviceConfig_DEFAULT NULL +#define wippersnapper_uart_UartDeviceConfig_config_generic_uart_input_MSGTYPE wippersnapper_uart_GenericUartInputConfig +#define wippersnapper_uart_UartDeviceConfig_config_trinamic_dynamixel_MSGTYPE wippersnapper_uart_TrinamicDynamixelConfig +#define wippersnapper_uart_UartDeviceConfig_config_pm25aqi_MSGTYPE wippersnapper_uart_PM25AQIConfig +#define wippersnapper_uart_UartDeviceConfig_config_gps_MSGTYPE wippersnapper_gps_GPSConfig + +#define wippersnapper_uart_UartAdd_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, cfg_serial, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, cfg_device, 2) +#define wippersnapper_uart_UartAdd_CALLBACK NULL +#define wippersnapper_uart_UartAdd_DEFAULT NULL +#define wippersnapper_uart_UartAdd_cfg_serial_MSGTYPE wippersnapper_uart_UartSerialConfig +#define wippersnapper_uart_UartAdd_cfg_device_MSGTYPE wippersnapper_uart_UartDeviceConfig + +#define wippersnapper_uart_UartAdded_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, STRING, device_id, 3) \ +X(a, STATIC, SINGULAR, BOOL, success, 4) +#define wippersnapper_uart_UartAdded_CALLBACK NULL +#define wippersnapper_uart_UartAdded_DEFAULT NULL + +#define wippersnapper_uart_UartRemove_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, STRING, device_id, 3) +#define wippersnapper_uart_UartRemove_CALLBACK NULL +#define wippersnapper_uart_UartRemove_DEFAULT NULL + +#define wippersnapper_uart_UartWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, STRING, device_id, 3) \ +X(a, CALLBACK, ONEOF, BYTES, (payload,bytes_data,payload.bytes_data), 4) \ +X(a, CALLBACK, ONEOF, STRING, (payload,text_data,payload.text_data), 5) +#define wippersnapper_uart_UartWrite_CALLBACK pb_default_field_callback +#define wippersnapper_uart_UartWrite_DEFAULT NULL + +#define wippersnapper_uart_UartWritten_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, STRING, device_id, 3) \ +X(a, STATIC, SINGULAR, UINT32, bytes_written, 4) +#define wippersnapper_uart_UartWritten_CALLBACK NULL +#define wippersnapper_uart_UartWritten_DEFAULT NULL + +#define wippersnapper_uart_UartInputEvent_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, uart_nbr, 1) \ +X(a, STATIC, SINGULAR, UENUM, type, 2) \ +X(a, STATIC, SINGULAR, STRING, device_id, 3) \ +X(a, STATIC, REPEATED, MESSAGE, events, 4) +#define wippersnapper_uart_UartInputEvent_CALLBACK NULL +#define wippersnapper_uart_UartInputEvent_DEFAULT NULL +#define wippersnapper_uart_UartInputEvent_events_MSGTYPE wippersnapper_sensor_SensorEvent + +extern const pb_msgdesc_t wippersnapper_uart_UartSerialConfig_msg; +extern const pb_msgdesc_t wippersnapper_uart_GenericUartInputConfig_msg; +extern const pb_msgdesc_t wippersnapper_uart_TrinamicDynamixelConfig_msg; +extern const pb_msgdesc_t wippersnapper_uart_PM25AQIConfig_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartDeviceConfig_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartAdd_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartAdded_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartRemove_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartWrite_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartWritten_msg; +extern const pb_msgdesc_t wippersnapper_uart_UartInputEvent_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define wippersnapper_uart_UARTBusData_fields &wippersnapper_uart_UARTBusData_msg -#define wippersnapper_uart_UARTAdd_fields &wippersnapper_uart_UARTAdd_msg -#define wippersnapper_uart_UARTAdded_fields &wippersnapper_uart_UARTAdded_msg -#define wippersnapper_uart_UARTRemove_fields &wippersnapper_uart_UARTRemove_msg -#define wippersnapper_uart_UARTEvent_fields &wippersnapper_uart_UARTEvent_msg +#define wippersnapper_uart_UartSerialConfig_fields &wippersnapper_uart_UartSerialConfig_msg +#define wippersnapper_uart_GenericUartInputConfig_fields &wippersnapper_uart_GenericUartInputConfig_msg +#define wippersnapper_uart_TrinamicDynamixelConfig_fields &wippersnapper_uart_TrinamicDynamixelConfig_msg +#define wippersnapper_uart_PM25AQIConfig_fields &wippersnapper_uart_PM25AQIConfig_msg +#define wippersnapper_uart_UartDeviceConfig_fields &wippersnapper_uart_UartDeviceConfig_msg +#define wippersnapper_uart_UartAdd_fields &wippersnapper_uart_UartAdd_msg +#define wippersnapper_uart_UartAdded_fields &wippersnapper_uart_UartAdded_msg +#define wippersnapper_uart_UartRemove_fields &wippersnapper_uart_UartRemove_msg +#define wippersnapper_uart_UartWrite_fields &wippersnapper_uart_UartWrite_msg +#define wippersnapper_uart_UartWritten_fields &wippersnapper_uart_UartWritten_msg +#define wippersnapper_uart_UartInputEvent_fields &wippersnapper_uart_UartInputEvent_msg /* Maximum encoded size of messages (where known) */ -/* wippersnapper_uart_UARTAdd_size depends on runtime parameters */ -/* wippersnapper_uart_UARTAdded_size depends on runtime parameters */ -/* wippersnapper_uart_UARTRemove_size depends on runtime parameters */ -/* wippersnapper_uart_UARTEvent_size depends on runtime parameters */ -#define WIPPERSNAPPER_UART_UART_PB_H_MAX_SIZE wippersnapper_uart_UARTBusData_size -#define wippersnapper_uart_UARTBusData_size 27 +/* wippersnapper_uart_GenericUartInputConfig_size depends on runtime parameters */ +/* wippersnapper_uart_UartDeviceConfig_size depends on runtime parameters */ +/* wippersnapper_uart_UartAdd_size depends on runtime parameters */ +/* wippersnapper_uart_UartWrite_size depends on runtime parameters */ +#define wippersnapper_uart_PM25AQIConfig_size 43 +#define wippersnapper_uart_TrinamicDynamixelConfig_size 6 +#define wippersnapper_uart_UartAdded_size 43 +#define wippersnapper_uart_UartRemove_size 41 +#define wippersnapper_uart_UartSerialConfig_size 57 +#define wippersnapper_uart_UartWritten_size 47 +#if defined(wippersnapper_sensor_SensorEvent_size) +#define WIPPERSNAPPER_UART_UART_PB_H_MAX_SIZE wippersnapper_uart_UartInputEvent_size +#define wippersnapper_uart_UartInputEvent_size (115 + 15*wippersnapper_sensor_SensorEvent_size) +#endif #ifdef __cplusplus } /* extern "C" */ diff --git a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp index bdfcfa4bb..ee84da8d4 100644 --- a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp +++ b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp @@ -18,56 +18,49 @@ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) || \ defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) || \ defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) || defined(ARDUINO_ESP32_DEV) || \ - defined(ESP32_DEV) + defined(ESP32_DEV) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) #include "WipperSnapper_LittleFS.h" -/**************************************************************************/ /*! @brief Attempts to set up and initialize a pre-existing LittleFS filesystem. */ -/**************************************************************************/ WipperSnapper_LittleFS::WipperSnapper_LittleFS() { // Attempt to initialize filesystem if (!LittleFS.begin()) { - HaltFilesystem("ERROR: Failure initializing LittleFS!", - WS_LED_STATUS_WAITING_FOR_REG_MSG); + fsHalt("ERROR: Failure initializing LittleFS!", + WS_LED_STATUS_WAITING_FOR_REG_MSG); } } -/**************************************************************************/ /*! @brief Destructor for LittleFS */ -/**************************************************************************/ WipperSnapper_LittleFS::~WipperSnapper_LittleFS() { LittleFS.end(); } -/**************************************************************************/ /*! @brief Locates, opens and parses the WipperSnapper secrets file on the LittleFS filesystem. */ -/**************************************************************************/ void WipperSnapper_LittleFS::ParseFileSecrets() { // Check if `secrets.json` file exists on FS if (!LittleFS.exists("/secrets.json")) { - HaltFilesystem( - "ERROR: No secrets.json found on filesystem - did you upload " - "credentials?"); + fsHalt("ERROR: No secrets.json found on filesystem - did you upload " + "credentials?"); } // Attempt to open secrets.json file for reading File secretsFile = LittleFS.open("/secrets.json", "r"); if (!secretsFile) { - HaltFilesystem("ERROR: Could not open secrets.json file for reading!"); + fsHalt("ERROR: Could not open secrets.json file for reading!"); } // Attempt to deserialize the file's JSON document JsonDocument doc; DeserializationError error = deserializeJson(doc, secretsFile); if (error) { - HaltFilesystem(String("ERROR: deserializeJson() failed with code ") + - error.c_str()); + fsHalt(String("ERROR: deserializeJson() failed with code ") + + error.c_str()); } if (doc.containsKey("network_type_wifi")) { // set default network config @@ -87,9 +80,8 @@ void WipperSnapper_LittleFS::ParseFileSecrets() { WS_DEBUG_PRINT("Network count: "); WS_DEBUG_PRINTLN(altNetworkCount); if (altNetworkCount == 0) { - HaltFilesystem( - "ERROR: No alternative network entries found under " - "network_type_wifi.alternative_networks in secrets.json!"); + fsHalt("ERROR: No alternative network entries found under " + "network_type_wifi.alternative_networks in secrets.json!"); } // check if over 3, warn user and take first three for (int i = 0; i < altNetworkCount; i++) { @@ -107,11 +99,11 @@ void WipperSnapper_LittleFS::ParseFileSecrets() { } WsV2._isWiFiMultiV2 = true; } else { - HaltFilesystem("ERROR: Unrecognised value type for " - "network_type_wifi.alternative_networks in secrets.json!"); + fsHalt("ERROR: Unrecognised value type for " + "network_type_wifi.alternative_networks in secrets.json!"); } } else { - HaltFilesystem("ERROR: Could not find network_type_wifi in secrets.json!"); + fsHalt("ERROR: Could not find network_type_wifi in secrets.json!"); } // Extract a config struct from the JSON document @@ -120,17 +112,16 @@ void WipperSnapper_LittleFS::ParseFileSecrets() { // Validate the config struct is not filled with default values if (strcmp(WsV2._configV2.aio_user, "YOUR_IO_USERNAME_HERE") == 0 || strcmp(WsV2._configV2.aio_key, "YOUR_IO_KEY_HERE") == 0) { - HaltFilesystem( + fsHalt( "ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change " "io_username and io_key to match your Adafruit IO credentials!\n"); } if (strcmp(WsV2._configV2.network.ssid, "YOUR_WIFI_SSID_HERE") == 0 || strcmp(WsV2._configV2.network.pass, "YOUR_WIFI_PASS_HERE") == 0) { - HaltFilesystem( - "ERROR: Invalid network credentials in secrets.json! TO FIX: Please " - "change network_ssid and network_password to match your Adafruit IO " - "credentials!\n"); + fsHalt("ERROR: Invalid network credentials in secrets.json! TO FIX: Please " + "change network_ssid and network_password to match your Adafruit IO " + "credentials!\n"); } // Close the file @@ -140,15 +131,12 @@ void WipperSnapper_LittleFS::ParseFileSecrets() { LittleFS.end(); } -/**************************************************************************/ /*! @brief Halts execution and blinks the status LEDs yellow. @param msg Error message to print to serial console. */ -/**************************************************************************/ -void WipperSnapper_LittleFS::HaltFilesystem(String msg, - ws_led_status_t status_state) { +void WipperSnapper_LittleFS::fsHalt(String msg, ws_led_status_t status_state) { statusLEDSolid(status_state); while (1) { WS_DEBUG_PRINTLN("Fatal Error: Halted execution!"); @@ -158,15 +146,17 @@ void WipperSnapper_LittleFS::HaltFilesystem(String msg, } } -/**************************************************************************/ /*! @brief Attempts to obtain the hardware's CS pin from the config.json file. */ -/**************************************************************************/ void WipperSnapper_LittleFS::GetPinSDCS() { // Attempt to open and deserialize the config.json file +#if defined(ARDUINO_ESP8266_ADAFRUIT_HUZZAH) + File file_cfg = LittleFS.open("/config.json", "r"); +#else File file_cfg = LittleFS.open("/config.json"); +#endif if (!file_cfg) WsV2.pin_sd_cs = 255; diff --git a/src/provisioning/littlefs/WipperSnapper_LittleFS.h b/src/provisioning/littlefs/WipperSnapper_LittleFS.h index e150af3fe..845489d13 100644 --- a/src/provisioning/littlefs/WipperSnapper_LittleFS.h +++ b/src/provisioning/littlefs/WipperSnapper_LittleFS.h @@ -21,19 +21,16 @@ // forward decl. class Wippersnapper_V2; -/***************************************************************************/ /*! @brief Class that handles WipperSnapper's LittleFS filesystem. */ -/***************************************************************************/ class WipperSnapper_LittleFS { public: WipperSnapper_LittleFS(); ~WipperSnapper_LittleFS(); void ParseFileSecrets(); - void - HaltFilesystem(String msg, - ws_led_status_t status_state = WS_LED_STATUS_ERROR_RUNTIME); + void fsHalt(String msg, + ws_led_status_t status_state = WS_LED_STATUS_ERROR_RUNTIME); void GetPinSDCS(); }; extern Wippersnapper_V2 WsV2; diff --git a/src/provisioning/sdcard/ws_sdcard.cpp b/src/provisioning/sdcard/ws_sdcard.cpp index 66881f9af..090fb8008 100644 --- a/src/provisioning/sdcard/ws_sdcard.cpp +++ b/src/provisioning/sdcard/ws_sdcard.cpp @@ -14,7 +14,6 @@ */ #include "ws_sdcard.h" -/**************************************************************************/ /*! @brief Initializes the SD card. @param pin_cs @@ -22,7 +21,6 @@ @returns True if the SD card was successfully initialized, False otherwise. */ -/**************************************************************************/ bool ws_sdcard::InitSdCard(uint8_t pin_cs) { WsV2.pin_sd_cs = pin_cs; #ifdef SD_USE_SPI_1 @@ -346,6 +344,29 @@ ws_sdcard::ParseSensorType(const char *sensor_type) { } } +/*! + @brief Parses a UART device's generic device line ending option + from the JSON configuration file. + @param line_ending + The line ending string to parse. + @returns The corresponding GenericDeviceLineEnding enum value +*/ +wippersnapper_uart_GenericDeviceLineEnding +ws_sdcard::ParseUartLineEnding(const char *line_ending) { + if (strcmp(line_ending, "LF") == 0) { + return wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_LF; + } else if (strcmp(line_ending, "CRLF") == 0) { + return wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_CRLF; + } else if (strcmp(line_ending, "TIMEOUT_100MS") == 0) { + return wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_100MS; + } else if (strcmp(line_ending, "TIMEOUT_1000MS") == 0) { + return wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_TIMEOUT_1000MS; + } + WS_DEBUG_PRINT("[SD] ERROR: Found unspecified LineEnding - "); + WS_DEBUG_PRINTLN(line_ending); + return wippersnapper_uart_GenericDeviceLineEnding_GENERIC_DEVICE_LINE_ENDING_UNSPECIFIED; +} + bool ws_sdcard::ParseDigitalIOAdd( JsonObject &component, wippersnapper_digitalio_DigitalIOAdd &msg_DigitalIOAdd) { @@ -443,6 +464,100 @@ uint32_t ws_sdcard::HexStrToInt(const char *hex_str) { return std::stoi(hex_str, nullptr, 16); } +/*! + @brief Parses a Uartadd message from the JSON configuration file. + @param component + The JSON object to parse. + @param msg_uart_add + The UartAdd message to populate. + @returns True if the message was successfully populated, False otherwise. +*/ +bool ws_sdcard::ParseUartAdd(JsonObject &component, + wippersnapper_uart_UartAdd &msg_uart_add) { + // Configure the UART Serial + msg_uart_add.has_cfg_serial = true; + snprintf(msg_uart_add.cfg_serial.pin_rx, + sizeof(msg_uart_add.cfg_serial.pin_rx), "%d", + component["pinRx"] | -1); + snprintf(msg_uart_add.cfg_serial.pin_tx, + sizeof(msg_uart_add.cfg_serial.pin_tx), "%d", + component["pinTx"] | -1); + msg_uart_add.cfg_serial.uart_nbr = component["uartNbr"] | 0; + msg_uart_add.cfg_serial.baud_rate = component["baudRate"] | 9600; + msg_uart_add.cfg_serial.format = + wippersnapper_uart_UartPacketFormat_UART_PACKET_FORMAT_8N1; // Stick to + // default 8N1 + // for now + msg_uart_add.cfg_serial.timeout = + component["timeout"] | 1000; // Use a default UART timeout of 1000ms + +#ifdef HAS_SOFTWARE_SERIAL + msg_uart_add.cfg_serial.use_sw_serial = component["useSwSerial"] | false; + msg_uart_add.cfg_serial.sw_serial_invert = + component["swSerialInvert"] | false; +#endif // HAS_SOFTWARE_SERIAL + + // Configure the UART Device + msg_uart_add.has_cfg_device = true; + strncpy(msg_uart_add.cfg_device.device_id, + component["deviceId"] | UNKNOWN_VALUE, + sizeof(msg_uart_add.cfg_device.device_id) - 1); + const char *device_type = component["deviceType"] | "UNKNOWN"; + if (strcmp(device_type, "gps") == 0) { + msg_uart_add.cfg_device.device_type = + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GPS; + msg_uart_add.cfg_device.which_config = + wippersnapper_uart_UartDeviceConfig_gps_tag; + msg_uart_add.cfg_device.config.gps.period = + component["gps"]["period"] | 30.0; + // TODO: We do not have parsing for GPS PMTK or UBX implemented yet + // This is a minimum possible implementation + } else if (strcmp(device_type, "pm25aqi") == 0) { + msg_uart_add.cfg_device.device_type = + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_PM25AQI; + msg_uart_add.cfg_device.which_config = + wippersnapper_uart_UartDeviceConfig_pm25aqi_tag; + msg_uart_add.cfg_device.config.pm25aqi.is_pm1006 = + component["isPm1006"] | false; + msg_uart_add.cfg_device.config.pm25aqi.period = + component["pm25aqi"]["period"] | 30.0; + // Fill sensor types + pb_size_t sensor_type_count = 0; + for (JsonObject sensor_type : component["sensorTypes"].as()) { + msg_uart_add.cfg_device.config.pm25aqi.sensor_types[sensor_type_count] = + ParseSensorType(sensor_type["type"]); + sensor_type_count++; + } + msg_uart_add.cfg_device.config.pm25aqi.sensor_types_count = + sensor_type_count; + } else if (strcmp(device_type, "generic_input") == 0) { + msg_uart_add.cfg_device.device_type = + wippersnapper_uart_UartDeviceType_UART_DEVICE_TYPE_GENERIC_INPUT; + msg_uart_add.cfg_device.which_config = + wippersnapper_uart_UartDeviceConfig_generic_uart_input_tag; + msg_uart_add.cfg_device.config.generic_uart_input.line_ending = + ParseUartLineEnding(component["lineEnding"] | "LF"); + msg_uart_add.cfg_device.config.generic_uart_input.period = + component["generic_input"]["period"] | 30.0; + // Fill sensor types + pb_size_t sensor_type_count = 0; + for (JsonObject sensor_type : component["sensorTypes"].as()) { + msg_uart_add.cfg_device.config.generic_uart_input + .sensor_types[sensor_type_count] = + ParseSensorType(sensor_type["type"]); + sensor_type_count++; + } + msg_uart_add.cfg_device.config.generic_uart_input.sensor_types_count = + sensor_type_count; + } else { + WS_DEBUG_PRINTLN("[SD] Parsing Error: Unknown UART device type found: " + + String(device_type)); + return false; + } + + return true; +} + /**************************************************************************/ /*! @brief Parses a DS18x20Add message from the JSON configuration file. @@ -486,11 +601,6 @@ bool ws_sdcard::ParseI2cDeviceAddReplace( wippersnapper_i2c_I2cDeviceAddOrReplace &msg_i2c_add) { strcpy(msg_i2c_add.i2c_device_name, component["i2cDeviceName"] | UNKNOWN_VALUE); - msg_i2c_add.i2c_device_period = component["period"] | 0.0; - if (msg_i2c_add.i2c_device_period < 0.0) { - WS_DEBUG_PRINTLN("[SD] Parsing Error: Invalid I2C device period!"); - return false; - } msg_i2c_add.has_i2c_device_description = true; strcpy(msg_i2c_add.i2c_device_description.i2c_bus_scl, @@ -517,6 +627,17 @@ bool ws_sdcard::ParseI2cDeviceAddReplace( const char *mux_channel = component["i2cMuxChannel"] | "0xFFFF"; msg_i2c_add.i2c_device_description.i2c_mux_channel = HexStrToInt(mux_channel); + // Set the period + bool is_gps = component["isGps"] | false; + if (is_gps) { + msg_i2c_add.is_gps = true; + msg_i2c_add.gps_config.period = component["gps"]["period"] | 15.0; + msg_i2c_add.has_gps_config = true; + return true; // early-out, we don't need to set sensor types for GPS + } else { + msg_i2c_add.i2c_device_period = component["period"] | 0.0; + } + msg_i2c_add.i2c_device_sensor_types_count = 0; for (JsonObject components_0_i2cDeviceSensorType : component["i2cDeviceSensorTypes"].as()) { @@ -558,7 +679,10 @@ bool ws_sdcard::AddI2cScanResultsToBuffer() { } if (skip_device) { - WS_DEBUG_PRINTLN("[SD] Skipping I2C device - already in config file"); + WS_DEBUG_PRINT("[SD] Skipping I2C device - already in config file: "); + // print address + WS_DEBUG_PRINT("0x"); + WS_DEBUG_PRINTLN(WsV2._i2c_controller->GetScanDeviceAddress(i), HEX); continue; } @@ -577,7 +701,7 @@ bool ws_sdcard::AddI2cScanResultsToBuffer() { "to shared buffer!"); return false; } - WS_DEBUG_PRINT("[SD] Added I2C Device to shared buffer: 0x"); + WS_DEBUG_PRINT("[SD] Added Scanned I2C Device to shared buffer: 0x"); WS_DEBUG_PRINTLN(WsV2._i2c_controller->GetScanDeviceAddress(i), HEX); } return true; @@ -721,7 +845,11 @@ bool ws_sdcard::ParseFileConfig() { #ifndef OFFLINE_MODE_DEBUG WS_DEBUG_PRINTLN("[SD] Deserializing config.json..."); - JsonDocument &doc = WsV2._fileSystemV2->GetDocCfg(); + // JsonDocument &doc = WsV2._fileSystemV2->GetDocCfg(); + JsonDocument &doc = WsV2._config_doc; + // print config.json using ArduinoJson's pretty print + WS_DEBUG_PRINTLN("[SD] Config JSON:"); + serializeJsonPretty(doc, Serial); #else JsonDocument doc; // Use test data, not data from the filesystem @@ -839,6 +967,18 @@ bool ws_sdcard::ParseComponents(JsonArray &components) { _cfg_i2c_addresses.push_back( msg_add.i2c_device_description.i2c_device_address); } + } else if (strcmp(component_api_type, "uart") == 0) { + WS_DEBUG_PRINTLN("[SD] UART component found in cfg"); + wippersnapper_uart_UartAdd msg_uart_add = + wippersnapper_uart_UartAdd_init_default; + success = ParseUartAdd(component, msg_uart_add); + if (success) { + WS_DEBUG_PRINTLN("[SD] UART component parsed successfully, adding to " + "shared buffer.."); + msg_signal_b2d.which_payload = + wippersnapper_signal_BrokerToDevice_uart_add_tag; + msg_signal_b2d.payload.uart_add = msg_uart_add; + } } else { WS_DEBUG_PRINTLN("[SD] Error: Unknown Component API: " + String(component_api_type)); @@ -879,6 +1019,8 @@ uint32_t ws_sdcard::GetTimestamp() { TickSoftRTC(); return cur_time; // early-return as we are not converting this "soft rtc" to // unixtime + } else if (WsV2._gps_controller->has_gps) { + now = WsV2._gps_controller->GetGPSDateTime(); } else { // we're either using a simulator or have undefined behavior return 0; } @@ -1095,8 +1237,8 @@ bool ws_sdcard::LogJSONDoc(JsonDocument &doc) { @returns True if the event was successfully logged, False otherwise. */ /**************************************************************************/ -bool ws_sdcard::LogGPIOSensorEventToSD( - uint8_t pin, float value, wippersnapper_sensor_SensorType read_type) { +bool ws_sdcard::LogEventGpio(uint8_t pin, float value, + wippersnapper_sensor_SensorType read_type) { JsonDocument doc; BuildJSONDoc(doc, pin, value, read_type); if (!LogJSONDoc(doc)) @@ -1116,8 +1258,8 @@ bool ws_sdcard::LogGPIOSensorEventToSD( @returns True if the event was successfully logged, False otherwise. */ /**************************************************************************/ -bool ws_sdcard::LogGPIOSensorEventToSD( - uint8_t pin, uint16_t value, wippersnapper_sensor_SensorType read_type) { +bool ws_sdcard::LogEventGpio(uint8_t pin, uint16_t value, + wippersnapper_sensor_SensorType read_type) { JsonDocument doc; BuildJSONDoc(doc, pin, value, read_type); if (!LogJSONDoc(doc)) @@ -1137,8 +1279,8 @@ bool ws_sdcard::LogGPIOSensorEventToSD( @returns True if the event was successfully logged, False otherwise. */ /**************************************************************************/ -bool ws_sdcard::LogGPIOSensorEventToSD( - uint8_t pin, bool value, wippersnapper_sensor_SensorType read_type) { +bool ws_sdcard::LogEventGpio(uint8_t pin, bool value, + wippersnapper_sensor_SensorType read_type) { JsonDocument doc; BuildJSONDoc(doc, pin, value, read_type); if (!LogJSONDoc(doc)) @@ -1158,8 +1300,7 @@ bool ws_sdcard::LogGPIOSensorEventToSD( @returns True if the event was successfully logged, False otherwise. */ /**************************************************************************/ -bool ws_sdcard::LogDS18xSensorEventToSD( - wippersnapper_ds18x20_Ds18x20Event *event_msg) { +bool ws_sdcard::LogEventDs18x(wippersnapper_ds18x20_Ds18x20Event *event_msg) { JsonDocument doc; // Iterate over the event message's sensor events // TODO: Standardize this Event with I2C @@ -1174,15 +1315,13 @@ bool ws_sdcard::LogDS18xSensorEventToSD( return true; } -/**************************************************************************/ /*! @brief Logs an I2C sensor event to the SD card. @param msg_device_event The I2cDeviceEvent message to log. @returns True if the event was successfully logged, False otherwise. */ -/**************************************************************************/ -bool ws_sdcard::LogI2cDeviceEvent( +bool ws_sdcard::LogEventI2c( wippersnapper_i2c_I2cDeviceEvent *msg_device_event) { JsonDocument doc; // Pull the DeviceDescriptor out @@ -1211,14 +1350,101 @@ bool ws_sdcard::LogI2cDeviceEvent( return true; } +/*! + @brief Logs a GPS event to the SD card. + @param msg_gps_event + The GPSEvent message to log. + @returns True if the event was successfully logged, False otherwise. +*/ +bool ws_sdcard::LogEventGps(wippersnapper_gps_GPSEvent *msg_gps_event) { + JsonDocument doc; + + // Log RMC responses + for (pb_size_t rmc_resp = 0; rmc_resp < msg_gps_event->rmc_responses_count; + rmc_resp++) { + // Log GPS DateTime + if (msg_gps_event->rmc_responses[rmc_resp].has_datetime) { + wippersnapper_gps_GPSDateTime gps_dt = + msg_gps_event->rmc_responses[rmc_resp].datetime; + DateTime gps_datetime(gps_dt.year, gps_dt.month, gps_dt.day, gps_dt.hour, + gps_dt.minute, gps_dt.seconds); + doc["timestamp"] = gps_datetime.unixtime(); + } + // Log GPS data + doc["fix_status"] = msg_gps_event->rmc_responses[rmc_resp].fix_status; + doc["latitude"] = msg_gps_event->rmc_responses[rmc_resp].lat; + doc["lat_dir"] = msg_gps_event->rmc_responses[rmc_resp].lat_dir; + doc["longitude"] = msg_gps_event->rmc_responses[rmc_resp].lon; + doc["lon_dir"] = msg_gps_event->rmc_responses[rmc_resp].lon_dir; + doc["speed"] = msg_gps_event->rmc_responses[rmc_resp].speed; + doc["angle"] = msg_gps_event->rmc_responses[rmc_resp].angle; + if (!LogJSONDoc(doc)) { + WS_DEBUG_PRINTLN("[SD] Error: Unable to log RMC response!"); + return false; + } + } + + // Log GGA responses + for (pb_size_t gga_resp = 0; gga_resp < msg_gps_event->gga_responses_count; + gga_resp++) { + // Log GPS DateTime + if (msg_gps_event->gga_responses[gga_resp].has_datetime) { + wippersnapper_gps_GPSDateTime gps_dt = + msg_gps_event->gga_responses[gga_resp].datetime; + DateTime gps_datetime(gps_dt.year, gps_dt.month, gps_dt.day, gps_dt.hour, + gps_dt.minute, gps_dt.seconds); + doc["timestamp"] = gps_datetime.unixtime(); + } + // Log GPS data + doc["latitude"] = msg_gps_event->gga_responses[gga_resp].lat; + doc["lat_dir"] = msg_gps_event->gga_responses[gga_resp].lat_dir; + doc["longitude"] = msg_gps_event->gga_responses[gga_resp].lon; + doc["lon_dir"] = msg_gps_event->gga_responses[gga_resp].lon_dir; + doc["fix_quality"] = msg_gps_event->gga_responses[gga_resp].fix_quality; + doc["num_satellites"] = + msg_gps_event->gga_responses[gga_resp].num_satellites; + doc["hdop"] = msg_gps_event->gga_responses[gga_resp].hdop; + doc["altitude"] = msg_gps_event->gga_responses[gga_resp].altitude; + doc["geoid_height"] = msg_gps_event->gga_responses[gga_resp].geoid_height; + if (!LogJSONDoc(doc)) { + WS_DEBUG_PRINTLN("[SD] Error: Unable to log GGA response!"); + return false; + } + } + + return true; +} + +/*! + @brief Logs a UART input event to the SD card. + @param msg_uart_input_event + The UartInputEvent message to log. + @returns True if the event was successfully logged, False otherwise. +*/ +bool ws_sdcard::LogEventUart( + wippersnapper_uart_UartInputEvent *msg_uart_input_event) { + JsonDocument doc; + doc["timestamp"] = GetTimestamp(); + doc["uart_device_id"] = msg_uart_input_event->device_id; + doc["uart_port"] = msg_uart_input_event->uart_nbr; + + // Log each event + for (pb_size_t i = 0; i < msg_uart_input_event->events_count; i++) { + doc["value"] = msg_uart_input_event->events[i].value.float_value; + doc["si_unit"] = SensorTypeToSIUnit(msg_uart_input_event->events[i].type); + } + + if (!LogJSONDoc(doc)) + return false; + return true; +} + #ifdef OFFLINE_MODE_DEBUG -/**************************************************************************/ /*! @brief Waits for a valid JSON string to be received via the hardware's serial input or from a hardcoded test JSON string. @returns True if a valid JSON string was received, False otherwise. */ -/**************************************************************************/ void ws_sdcard::waitForSerialConfig() { json_test_data = "{" "\"exportVersion\": \"1.0.0\"," diff --git a/src/provisioning/sdcard/ws_sdcard.h b/src/provisioning/sdcard/ws_sdcard.h index d9293711e..e66c19cc8 100644 --- a/src/provisioning/sdcard/ws_sdcard.h +++ b/src/provisioning/sdcard/ws_sdcard.h @@ -14,7 +14,6 @@ */ #ifndef WS_SDCARD_H #define WS_SDCARD_H -#include "RTClib.h" #include "SdFat_Adafruit_Fork.h" #include "StreamUtils.h" #include "Wippersnapper_V2.h" @@ -62,14 +61,16 @@ class ws_sdcard { bool CreateNewLogFile(); bool isModeOffline() { return is_mode_offline; } void waitForSerialConfig(); - bool LogGPIOSensorEventToSD(uint8_t pin, float value, - wippersnapper_sensor_SensorType read_type); - bool LogGPIOSensorEventToSD(uint8_t pin, bool value, - wippersnapper_sensor_SensorType read_type); - bool LogGPIOSensorEventToSD(uint8_t pin, uint16_t value, - wippersnapper_sensor_SensorType read_type); - bool LogDS18xSensorEventToSD(wippersnapper_ds18x20_Ds18x20Event *event_msg); - bool LogI2cDeviceEvent(wippersnapper_i2c_I2cDeviceEvent *msg_device_event); + bool LogEventGpio(uint8_t pin, float value, + wippersnapper_sensor_SensorType read_type); + bool LogEventGpio(uint8_t pin, bool value, + wippersnapper_sensor_SensorType read_type); + bool LogEventGpio(uint8_t pin, uint16_t value, + wippersnapper_sensor_SensorType read_type); + bool LogEventDs18x(wippersnapper_ds18x20_Ds18x20Event *event_msg); + bool LogEventI2c(wippersnapper_i2c_I2cDeviceEvent *msg_device_event); + bool LogEventGps(wippersnapper_gps_GPSEvent *msg_gps_event); + bool LogEventUart(wippersnapper_uart_UartInputEvent *msg_uart_input_event); private: bool ParseExportedFromDevice(JsonDocument &doc); @@ -86,6 +87,8 @@ class ws_sdcard { void TickSoftRTC(); uint32_t GetSoftRTCTime(); wippersnapper_sensor_SensorType ParseSensorType(const char *sensor_type); + wippersnapper_uart_GenericDeviceLineEnding + ParseUartLineEnding(const char *line_ending); bool ParseComponents(JsonArray &components); bool ParseDigitalIOAdd(JsonObject &component, @@ -94,6 +97,10 @@ class ws_sdcard { wippersnapper_analogio_AnalogIOAdd &msg_AnalogIOAdd); bool ParseDS18xAdd(JsonObject &component, wippersnapper_ds18x20_Ds18x20Add &msg_ds18x20_add); + bool ParseUartAdd(JsonObject &component, + wippersnapper_uart_UartAdd &msg_uart_add); + bool ParseGPSConfig(JsonObject &gps_config, + wippersnapper_gps_GPSConfig *cfg_gps); bool ParseI2cDeviceAddReplace( JsonObject &component, wippersnapper_i2c_I2cDeviceAddOrReplace &msg_i2c_device_add_replace); diff --git a/src/provisioning/tinyusb/Wippersnapper_FS.cpp b/src/provisioning/tinyusb/Wippersnapper_FS.cpp index b1e4d1608..5d20aa906 100644 --- a/src/provisioning/tinyusb/Wippersnapper_FS.cpp +++ b/src/provisioning/tinyusb/Wippersnapper_FS.cpp @@ -7,7 +7,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2024-2025 for Adafruit Industries. + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. * * BSD license, all text here must be included in any redistribution. * @@ -30,7 +30,8 @@ defined(ARDUINO_RASPBERRY_PI_PICO) || \ defined(ARDUINO_RASPBERRY_PI_PICO_2) || \ defined(ARDUINO_ADAFRUIT_FEATHER_RP2040_ADALOGGER) || \ - defined(ARDUINO_ADAFRUIT_METRO_RP2350) + defined(ARDUINO_ADAFRUIT_METRO_RP2350) || \ + defined(ARDUINO_RASPBERRY_PI_PICO_2W) #include "Wippersnapper_FS.h" // On-board external flash (QSPI or SPI) macros should already // defined in your board variant if supported @@ -58,16 +59,13 @@ Adafruit_FlashTransport_RP2040 flashTransport_v2; Adafruit_SPIFlash flash_v2(&flashTransport_v2); ///< SPIFlash object FatVolume wipperFatFs_v2; ///< File system object from Adafruit SDFat library Adafruit_USBD_MSC usb_msc_v2; /*!< USB mass storage object */ -static bool _fs_changed = false; -static bool _did_init_msc = false; +bool _fs_changed = false; ///< Flag to indicate filesystem changes -/**************************************************************************/ /*! @brief Formats the flash filesystem as FAT12. @returns FR_OK if filesystem formatted correctly, otherwise any FRESULT enum. */ -/**************************************************************************/ FRESULT format_fs_fat12(void) { FATFS elmchamFatfs_v2; uint8_t workbuf_v2[4096]; @@ -98,67 +96,83 @@ FRESULT format_fs_fat12(void) { return r; } -/**************************************************************************/ /*! @brief Initializes USB-MSC and the QSPI flash filesystem. */ -/**************************************************************************/ Wippersnapper_FS::Wippersnapper_FS() { - _fs_changed = false; - _did_init_msc = false; - - // Re-enumerate to allow cdc class begin() to take effect - if (TinyUSBDevice.mounted()) { - TinyUSBDevice.detach(); - delay(10); - TinyUSBDevice.attach(); - } + // Detach USB device during init. + TinyUSBDevice.detach(); + // Wait for detach + delay(500); // Attempt to initialize the flash chip if (!flash_v2.begin()) { setStatusLEDColor(RED); - HaltFilesystem("Failed to initialize the flash chip!"); + fsHalt("Failed to initialize the flash chip!"); } + // Attempt to initialize the filesystem + bool is_fs_formatted = wipperFatFs_v2.begin(&flash_v2); + // If we are not formatted, attempt to format the filesystem as fat12 - if (!wipperFatFs_v2.begin(&flash_v2)) { + if (!is_fs_formatted) { FRESULT rc = format_fs_fat12(); + if (rc != FR_OK) { setStatusLEDColor(RED); - HaltFilesystem("FATAL ERROR: Failed to format the filesystem!"); + fsHalt("FATAL ERROR: Failed to format the filesystem!"); } // Now that we have a formatted filesystem, we need to inititalize it if (!wipperFatFs_v2.begin(&flash_v2)) { setStatusLEDColor(RED); - HaltFilesystem("FATAL ERROR: Failed to mount newly created filesystem!"); + fsHalt("FATAL ERROR: Failed to mount newly created filesystem!"); } } // Write contents to the formatted filesystem - if (!MakeDefaultFilesystem()) { + if (!writeFSContents()) { setStatusLEDColor(RED); - HaltFilesystem("FATAL ERROR: Could not write filesystem contents!"); + fsHalt("FATAL ERROR: Could not write filesystem contents!"); + } + + // Initialize USB-MSC + initUSBMSC(); + + // If we wrote a fresh secrets.json file, halt until user edits the file and + // RESETs the device Signal to user that action must be taken (edit + // secrets.json) + if (_is_secrets_file_empty) { + writeToBootOut( + "Please edit the secrets.json file. Then, reset your board.\n"); +#ifdef USE_DISPLAY + WsV2._ui_helperV2->show_scr_error( + "INVALID SETTINGS FILE", + "The settings.json file on the WIPPER drive contains default values. " + "Please edit it to reflect your Adafruit IO and network credentials. " + "When you're done, press RESET on the board."); +#endif + fsHalt("The settings.json file on the WIPPER drive contains default " + "values\n. Using a text editor, edit it to reflect your Adafruit IO " + "and WiFi credentials. Then, reset the board."); } } -/************************************************************/ /*! @brief Filesystem destructor */ -/************************************************************/ Wippersnapper_FS::~Wippersnapper_FS() { // Unmount filesystem wipperFatFs_v2.end(); } -/**************************************************************************/ +void refreshMassStorage(void) { _fs_changed = true; } + /*! @brief Attempts to obtain the hardware's CS pin from the config.json file. */ -/**************************************************************************/ -void Wippersnapper_FS::GetPinSDCS() { +void Wippersnapper_FS::GetSDCSPin() { File32 file_cfg; DeserializationError error; // Attempt to open and deserialize the config.json file @@ -178,16 +192,13 @@ void Wippersnapper_FS::GetPinSDCS() { // Parse config.json and save the SD CS pin JsonObject exportedFromDevice = WsV2._config_doc["exportedFromDevice"]; WsV2.pin_sd_cs = exportedFromDevice["sd_cs_pin"] | 255; - file_cfg.flush(); file_cfg.close(); } -/**************************************************************************/ /*! @brief Writes files to the filesystem to disable macOS from indexing. @returns True if files written successfully, false otherwise. */ -/**************************************************************************/ bool disableMacOSIndexing() { wipperFatFs_v2.mkdir("/.fseventsd/"); File32 writeFile = wipperFatFs_v2.open("/.fseventsd/no_log", FILE_WRITE); @@ -205,53 +216,48 @@ bool disableMacOSIndexing() { return false; writeFile.close(); - refreshMassStorage(); return true; } -/**************************************************************************/ /*! @brief Initializes the flash filesystem. @param force_format If true, forces a new filesystem to be created. [DESTRUCTIVE] @return True if filesystem initialized correctly, false otherwise. */ -/**************************************************************************/ -bool Wippersnapper_FS::MakeDefaultFilesystem() { +bool Wippersnapper_FS::writeFSContents() { // If CircuitPython was previously installed - erase CircuitPython's default // filesystem - EraseCircuitPythonFS(); + eraseCPFS(); // If WipperSnapper was previously installed - remove the old // wippersnapper_boot_out.txt file - EraseFileBoot(); + eraseBootFile(); // Disble indexing on macOS disableMacOSIndexing(); // Create wippersnapper_boot_out.txt file - if (!CreateFileBoot()) + if (!createBootFile()) return false; // Check if secrets.json file already exists - if (!GetFileSecrets()) { - CreateFileSecrets(); + if (!getSecretsFile()) { + // Create new secrets.json file and halt + createSecretsFile(); + _is_secrets_file_empty = true; } - - CreateFileConfig(); return true; } -/**************************************************************************/ /*! @brief Initializes the USB MSC device. */ -/**************************************************************************/ -void Wippersnapper_FS::InitUsbMsc() { +void Wippersnapper_FS::initUSBMSC() { // Set disk vendor id, product id and revision with string up to 8, 16, 4 // characters respectively usb_msc_v2.setID("Adafruit", "External Flash", "1.0"); - // Set r/w callbacks + // Set callback usb_msc_v2.setReadWriteCallback(qspi_msc_read_cb_v2, qspi_msc_write_cb_v2, qspi_msc_flush_cb_v2); @@ -261,28 +267,29 @@ void Wippersnapper_FS::InitUsbMsc() { // MSC is ready for read/write usb_msc_v2.setUnitReady(true); - // Setup callback for when MSC ready - _fs_changed = false; - usb_msc_v2.setReadyCallback(0, msc_ready_callback); - // init MSC usb_msc_v2.begin(); - // Re-enumerate to allow msc class begin() to take effect - if (TinyUSBDevice.mounted()) { - TinyUSBDevice.detach(); - delay(10); - TinyUSBDevice.attach(); - } - _did_init_msc = true; + // If already enumerated, additional class driverr begin() e.g msc, hid, midi + // won't take effect until re-enumeration + // Attach MSC and wait for enumeration + TinyUSBDevice.attach(); + delay(500); +} + +/*! + @brief Checks if secrets.json file exists on the flash filesystem. + @returns True if secrets.json file exists, False otherwise. +*/ +bool Wippersnapper_FS::getSecretsFile() { + // Does secrets.json file exist? + return wipperFatFs_v2.exists("/secrets.json"); } -/**************************************************************************/ /*! @brief Erases the default CircuitPython filesystem if it exists. */ -/**************************************************************************/ -void Wippersnapper_FS::EraseCircuitPythonFS() { +void Wippersnapper_FS::eraseCPFS() { if (wipperFatFs_v2.exists("/boot_out.txt")) { wipperFatFs_v2.remove("/boot_out.txt"); wipperFatFs_v2.remove("/code.py"); @@ -291,23 +298,19 @@ void Wippersnapper_FS::EraseCircuitPythonFS() { } } -/**************************************************************************/ /*! @brief Erases the existing "wipper_boot_out.txt" file from the FS. */ -/**************************************************************************/ -void Wippersnapper_FS::EraseFileBoot() { +void Wippersnapper_FS::eraseBootFile() { // overwrite previous boot_out file on each boot if (wipperFatFs_v2.exists("/wipper_boot_out.txt")) wipperFatFs_v2.remove("/wipper_boot_out.txt"); } -/**************************************************************************/ /*! @brief Creates or overwrites `wipper_boot_out.txt` file to FS. */ -/**************************************************************************/ -bool Wippersnapper_FS::CreateFileBoot() { +bool Wippersnapper_FS::createBootFile() { bool is_success = false; char sMAC[18] = {0}; @@ -339,7 +342,6 @@ bool Wippersnapper_FS::CreateFileBoot() { bootFile.flush(); bootFile.close(); - refreshMassStorage(); is_success = true; } else { bootFile.close(); @@ -347,55 +349,32 @@ bool Wippersnapper_FS::CreateFileBoot() { return is_success; } -/**************************************************************************/ /*! - @brief Creates a default config document in memory. + @brief Creates a default secrets.json file on the filesystem. */ -/**************************************************************************/ -void Wippersnapper_FS::CreateFileConfig() { - // Load config.json into memory, if it already exists on the FS - if (wipperFatFs_v2.exists("/config.json")) { - File32 file_cfg = wipperFatFs_v2.open("/config.json", FILE_READ); - if (file_cfg) { - DeserializationError error = deserializeJson(_doc_cfg, file_cfg); - if (error) { - // If we can't deserialize, just raise - HaltFilesystem( - "[fs] Error: Unable to deserialize config.json, is it corrupted?"); - } - file_cfg.close(); - // Check if the config.json file has the required keys - if (!_doc_cfg.containsKey("exportedFromDevice")) { - // Build exportedFromDevice object - JsonObject exportedFromDevice = - _doc_cfg["exportedFromDevice"].to(); - exportedFromDevice["sd_cs_pin"] = 255; - exportedFromDevice["referenceVoltage"] = 0; - exportedFromDevice["totalGPIOPins"] = 0; - exportedFromDevice["totalAnalogPins"] = 0; - exportedFromDevice["statusLEDBrightness"] = 0.3; - } +void Wippersnapper_FS::createSecretsFile() { + // Open file for writing + File32 secretsFile = wipperFatFs_v2.open("/secrets.json", FILE_WRITE); - if (!_doc_cfg.containsKey("components")) { - // Build components array - _doc_cfg["components"].to(); - } + // Create a default secretsConfig structure + secretsConfig secretsConfig; + strcpy(secretsConfig.aio_user, "YOUR_IO_USERNAME_HERE"); + strcpy(secretsConfig.aio_key, "YOUR_IO_KEY_HERE"); + strcpy(secretsConfig.network.ssid, "YOUR_WIFI_SSID_HERE"); + strcpy(secretsConfig.network.pass, "YOUR_WIFI_PASS_HERE"); + secretsConfig.status_pixel_brightness = 0.2; - return; - } - file_cfg.close(); - } + // Create and fill JSON document from secretsConfig + JsonDocument doc; + doc.set(secretsConfig); + + // Serialize JSON to file + serializeJsonPretty(doc, secretsFile); - // Create a default configConfig structure in a new doc - _doc_cfg.clear(); - JsonObject exportedFromDevice = - _doc_cfg["exportedFromDevice"].to(); - exportedFromDevice["sd_cs_pin"] = 255; - exportedFromDevice["referenceVoltage"] = 0; - exportedFromDevice["totalGPIOPins"] = 0; - exportedFromDevice["totalAnalogPins"] = 0; - exportedFromDevice["statusLEDBrightness"] = 0.3; - JsonArray components = _doc_cfg["components"].to(); + // Flush and close file + secretsFile.flush(); + secretsFile.close(); + delay(2500); } /**************************************************************************/ @@ -443,12 +422,11 @@ void Wippersnapper_FS::AddI2cDeviceToFileConfig( } } -/**************************************************************************/ + /*! @brief Writes the in-memory config document to the filesystem. @returns True if the file was successfully written, False otherwise. */ -/**************************************************************************/ bool Wippersnapper_FS::WriteFileConfig() { // If it exists, remove the existing config.json file // as we're about to write the new one into memory @@ -461,7 +439,7 @@ bool Wippersnapper_FS::WriteFileConfig() { // Write the document to the filesystem File32 file_cfg = wipperFatFs_v2.open("/config.json", FILE_WRITE); if (!file_cfg) { - HaltFilesystem("Could not create the config file!"); + fsHalt("Could not create the config file!"); return false; } _doc_cfg.shrinkToFit(); @@ -473,7 +451,7 @@ bool Wippersnapper_FS::WriteFileConfig() { flash_v2.syncBlocks(); refreshMassStorage(); delay(500); - InitUsbMsc(); + initUSBMSC(); WS_PRINTER.flush(); delay(2500); WS_PRINTER.println("Config file written to flash!"); // List current config / components and periods @@ -481,67 +459,23 @@ bool Wippersnapper_FS::WriteFileConfig() { return true; } -/**************************************************************************/ -/*! - @brief Checks if secrets.json file exists on the flash filesystem. - @returns True if secrets.json file exists, False otherwise. -*/ -/**************************************************************************/ -bool Wippersnapper_FS::GetFileSecrets() { - // Does secrets.json file exist? - return wipperFatFs_v2.exists("/secrets.json"); -} - -/**************************************************************************/ -/*! - @brief Creates a default secrets.json file on the filesystem. -*/ -/**************************************************************************/ -void Wippersnapper_FS::CreateFileSecrets() { - // Open file for writing - File32 secretsFile = wipperFatFs_v2.open("/secrets.json", FILE_WRITE); - - // Create a default secretsConfig structure - secretsConfig secretsConfig; - strcpy(secretsConfig.aio_user, "YOUR_IO_USERNAME_HERE"); - strcpy(secretsConfig.aio_key, "YOUR_IO_KEY_HERE"); - strcpy(secretsConfig.network.ssid, "YOUR_WIFI_SSID_HERE"); - strcpy(secretsConfig.network.pass, "YOUR_WIFI_PASS_HERE"); - secretsConfig.status_pixel_brightness = 0.2; - - // Create and fill JSON document from secretsConfig - JsonDocument doc; - doc.set(secretsConfig); - - // Serialize JSON to file - serializeJsonPretty(doc, secretsFile); - - // Flush and close file - secretsFile.flush(); - secretsFile.close(); - refreshMassStorage(); - delay(2500); -} - -/**************************************************************************/ /*! @brief Parses a secrets.json file on the flash filesystem. */ -/**************************************************************************/ -void Wippersnapper_FS::ParseFileSecrets() { +void Wippersnapper_FS::parseSecrets() { // Attempt to open the secrets.json file for reading File32 secretsFile = wipperFatFs_v2.open("/secrets.json"); if (!secretsFile) { - HaltFilesystem("ERROR: Could not open secrets.json file for reading!"); + fsHalt("ERROR: Could not open secrets.json file for reading!"); } // Attempt to deserialize the file's JSON document JsonDocument doc; DeserializationError error = deserializeJson(doc, secretsFile); if (error) { - HaltFilesystem(String("ERROR: Unable to parse secrets.json file - " - "deserializeJson() failed with code") + - error.c_str()); + fsHalt(String("ERROR: Unable to parse secrets.json file - " + "deserializeJson() failed with code") + + error.c_str()); } if (doc.containsKey("network_type_wifi")) { @@ -562,9 +496,8 @@ void Wippersnapper_FS::ParseFileSecrets() { WS_DEBUG_PRINT("Network count: "); WS_DEBUG_PRINTLN(altNetworkCount); if (altNetworkCount == 0) { - HaltFilesystem( - "ERROR: No alternative network entries found under " - "network_type_wifi.alternative_networks in secrets.json!"); + fsHalt("ERROR: No alternative network entries found under " + "network_type_wifi.alternative_networks in secrets.json!"); } // check if over 3, warn user and take first three for (int i = 0; i < altNetworkCount; i++) { @@ -582,11 +515,11 @@ void Wippersnapper_FS::ParseFileSecrets() { } WsV2._isWiFiMultiV2 = true; } else { - HaltFilesystem("ERROR: Unrecognised value type for " - "network_type_wifi.alternative_networks in secrets.json!"); + fsHalt("ERROR: Unrecognised value type for " + "network_type_wifi.alternative_networks in secrets.json!"); } } else { - HaltFilesystem("ERROR: Could not find network_type_wifi in secrets.json!"); + fsHalt("ERROR: Could not find network_type_wifi in secrets.json!"); } // Extract a config struct from the JSON document @@ -595,82 +528,71 @@ void Wippersnapper_FS::ParseFileSecrets() { // Validate the config struct is not filled with default values if (strcmp(WsV2._configV2.aio_user, "YOUR_IO_USERNAME_HERE") == 0 || strcmp(WsV2._configV2.aio_key, "YOUR_IO_KEY_HERE") == 0) { - WriteFileBoot( + writeToBootOut( "ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change " "io_username and io_key to match your Adafruit IO credentials!\n"); #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error( + WsV2._ui_helperV2->show_scr_error( "INVALID IO CREDS", "The \"io_username/io_key\" fields within secrets.json are invalid, " "please " "change it to match your Adafruit IO credentials. Then, press RESET."); #endif - HaltFilesystem( + fsHalt( "ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change " "io_username and io_key to match your Adafruit IO credentials!"); } if (strcmp(WsV2._configV2.network.ssid, "YOUR_WIFI_SSID_HERE") == 0 || strcmp(WsV2._configV2.network.pass, "YOUR_WIFI_PASS_HERE") == 0) { - WriteFileBoot("ERROR: Invalid network credentials in secrets.json! TO " - "FIX: Please change network_ssid and network_password to " - "match your Adafruit IO credentials!\n"); + writeToBootOut("ERROR: Invalid network credentials in secrets.json! TO " + "FIX: Please change network_ssid and network_password to " + "match your Adafruit IO credentials!\n"); #ifdef USE_DISPLAY - WsV2._ui_helper->show_scr_error( + WsV2._ui_helperV2->show_scr_error( "INVALID NETWORK", "The \"network_ssid and network_password\" fields within secrets.json " "are invalid, please change it to match your WiFi credentials. Then, " "press RESET."); #endif - HaltFilesystem( - "ERROR: Invalid network credentials in secrets.json! TO FIX: Please " - "change network_ssid and network_password to match your Adafruit IO " - "credentials!"); + fsHalt("ERROR: Invalid network credentials in secrets.json! TO FIX: Please " + "change network_ssid and network_password to match your Adafruit IO " + "credentials!"); } - WriteFileBoot("Secrets Contents\n"); - WriteFileBoot("Network Info\n: "); - WriteFileBoot(WsV2._configV2.network.ssid); - WriteFileBoot(WsV2._configV2.network.pass); - WriteFileBoot("IO Creds.\n: "); - WriteFileBoot(WsV2._configV2.aio_user); - WriteFileBoot(WsV2._configV2.aio_key); + writeToBootOut("Secrets Contents\n"); + writeToBootOut("Network Info\n: "); + writeToBootOut(WsV2._configV2.network.ssid); + writeToBootOut(WsV2._configV2.network.pass); + writeToBootOut("IO Creds.\n: "); + writeToBootOut(WsV2._configV2.aio_user); + writeToBootOut(WsV2._configV2.aio_key); // Close secrets.json file secretsFile.close(); - refreshMassStorage(); } -/**************************************************************************/ /*! @brief Appends message string to wipper_boot_out.txt file. @param str PROGMEM string. */ -/**************************************************************************/ -void Wippersnapper_FS::WriteFileBoot(PGM_P str) { +void Wippersnapper_FS::writeToBootOut(PGM_P str) { // Append error output to FS File32 bootFile = wipperFatFs_v2.open("/wipper_boot_out.txt", FILE_WRITE); if (!bootFile) - HaltFilesystem("ERROR: Unable to open wipper_boot_out.txt for logging!"); + fsHalt("ERROR: Unable to open wipper_boot_out.txt for logging!"); bootFile.print(str); bootFile.flush(); bootFile.close(); - refreshMassStorage(); } -/**************************************************************************/ /*! @brief Halts execution and blinks the status LEDs yellow. @param msg Error message to print to serial console. */ -/**************************************************************************/ -void Wippersnapper_FS::HaltFilesystem(String msg) { - if (!_did_init_msc) { - WS_DEBUG_PRINTLN("HaltFilesystem: InitUsbMsc"); - InitUsbMsc(); - } +void Wippersnapper_FS::fsHalt(String msg) { TinyUSBDevice.attach(); delay(500); statusLEDSolid(WS_LED_STATUS_FS_WRITE); @@ -682,19 +604,12 @@ void Wippersnapper_FS::HaltFilesystem(String msg) { } } -/**************************************************************************/ /*! @brief Halts execution and blinks the status LEDs yellow. @param msg Error message to print to serial console. */ -/**************************************************************************/ -void Wippersnapper_FS::HaltFilesystem(String msg, - ws_led_status_t ledStatusColor) { - if (!_did_init_msc) { - WS_DEBUG_PRINTLN("HaltFilesystem: InitUsbMsc"); - InitUsbMsc(); - } +void Wippersnapper_FS::fsHalt(String msg, ws_led_status_t ledStatusColor) { TinyUSBDevice.attach(); delay(500); statusLEDSolid(ledStatusColor); @@ -707,12 +622,10 @@ void Wippersnapper_FS::HaltFilesystem(String msg, } #ifdef ARDUINO_FUNHOUSE_ESP32S2 -/**************************************************************************/ /*! @brief Creates a default display_config.json file on the filesystem. */ -/**************************************************************************/ -void Wippersnapper_FS::CreateDisplayCfg() { +void Wippersnapper_FS::createDisplayConfig() { // Open file for writing File32 displayFile = wipperFatFs_v2.open("/display_config.json", FILE_WRITE); @@ -731,58 +644,52 @@ void Wippersnapper_FS::CreateDisplayCfg() { // Create and fill JSON document from displayConfig JsonDocument doc; if (!doc.set(displayConfig)) { - HaltFilesystem( - "ERROR: Unable to set displayConfig, no space in arduinoJSON " - "document!"); + fsHalt("ERROR: Unable to set displayConfig, no space in arduinoJSON " + "document!"); } // Write the file out to the filesystem serializeJsonPretty(doc, displayFile); displayFile.flush(); displayFile.close(); - refreshMassStorage(); + delay(2500); // give FS some time to write the file } -/**************************************************************************/ /*! @brief Parses a display_config.json file on the flash filesystem. @param dispCfg displayConfig struct to populate. */ -/**************************************************************************/ -void Wippersnapper_FS::ParseFileDisplayCfg(displayConfig &dispCfg) { +void Wippersnapper_FS::parseDisplayConfig(displayConfig &dispCfg) { // Check if display_config.json file exists, if not, generate it if (!wipperFatFs_v2.exists("/display_config.json")) { WS_DEBUG_PRINTLN("Could not find display_config.json, generating..."); #ifdef ARDUINO_FUNHOUSE_ESP32S2 - CreateDisplayCfg(); // generate a default display_config.json for - // FunHouse + createDisplayConfig(); // generate a default display_config.json for + // FunHouse #endif } // Attempt to open file for JSON parsing File32 file = wipperFatFs_v2.open("/display_config.json", FILE_READ); if (!file) { - HaltFilesystem( - "FATAL ERROR: Unable to open display_config.json for parsing"); + fsHalt("FATAL ERROR: Unable to open display_config.json for parsing"); } // Attempt to deserialize the file's json document JsonDocument doc; DeserializationError error = deserializeJson(doc, file); if (error) { - HaltFilesystem(String("FATAL ERROR: Unable to parse display_config.json - " - "deserializeJson() failed with code") + - error.c_str()); + fsHalt(String("FATAL ERROR: Unable to parse display_config.json - " + "deserializeJson() failed with code") + + error.c_str()); } // Close the file, we're done with it file.close(); - refreshMassStorage(); // Extract a displayConfig struct from the JSON document dispCfg = doc.as(); } #endif // ARDUINO_FUNHOUSE_ESP32S2 -/**************************************************************************/ /*! @brief Callback invoked when received READ10 command. Copies disk's data up to buffer. @@ -794,7 +701,6 @@ void Wippersnapper_FS::ParseFileDisplayCfg(displayConfig &dispCfg) { Desired size of buffer to read. @returns Number of written bytes (must be multiple of block size) */ -/**************************************************************************/ int32_t qspi_msc_read_cb_v2(uint32_t lba, void *buffer, uint32_t bufsize) { // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks // already include 4K sector caching internally. We don't need to cache it, @@ -803,7 +709,6 @@ int32_t qspi_msc_read_cb_v2(uint32_t lba, void *buffer, uint32_t bufsize) { : -1; } -/**************************************************************************/ /*! @brief Callback invoked when received WRITE command. Process data in buffer to disk's storage. @@ -815,7 +720,6 @@ int32_t qspi_msc_read_cb_v2(uint32_t lba, void *buffer, uint32_t bufsize) { Desired size of buffer to write. @returns Number of written bytes (must be multiple of block size) */ -/**************************************************************************/ int32_t qspi_msc_write_cb_v2(uint32_t lba, uint8_t *buffer, uint32_t bufsize) { // Note: SPIFLash Bock API: readBlocks/writeBlocks/syncBlocks // already include 4K sector caching internally. We don't need to cache it, @@ -823,28 +727,10 @@ int32_t qspi_msc_write_cb_v2(uint32_t lba, uint8_t *buffer, uint32_t bufsize) { return flash_v2.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1; } -/**************************************************************************/ -/*! - @brief Callback invoked when the host sends a Test Unit Ready command. - @returns True if the host can read/write the LUN. -*/ -/**************************************************************************/ -bool msc_ready_callback(void) { - // if fs has changed, mark unit as not ready temporarily - // to force PC to flush cache - bool ret = !_fs_changed; - _fs_changed = false; - return ret; -} - -void refreshMassStorage(void) { _fs_changed = true; } - -/***************************************************************************/ /*! @brief Callback invoked when WRITE10 command is completed (status received and accepted by host). Used to flush any pending cache. */ -/***************************************************************************/ void qspi_msc_flush_cb_v2(void) { // sync w/flash flash_v2.syncBlocks(); diff --git a/src/provisioning/tinyusb/Wippersnapper_FS.h b/src/provisioning/tinyusb/Wippersnapper_FS.h index 07cf56af7..df7db158b 100644 --- a/src/provisioning/tinyusb/Wippersnapper_FS.h +++ b/src/provisioning/tinyusb/Wippersnapper_FS.h @@ -7,7 +7,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2021-2025 for Adafruit Industries. + * Copyright (c) Brent Rubell 2021-2024 for Adafruit Industries. * * BSD license, all text here must be included in any redistribution. * @@ -18,8 +18,8 @@ #include "Adafruit_SPIFlash.h" #include "Adafruit_TinyUSB.h" #include "SdFat_Adafruit_Fork.h" -// using f_mkfs() for formatting #include "fatfs/ff.h" // NOTE: This should be #included before fatfs/diskio.h!!! +// using f_mkfs() for formatting #include "fatfs/diskio.h" #include "Wippersnapper_V2.h" @@ -32,46 +32,46 @@ struct displayConfig; int32_t qspi_msc_write_cb_v2(uint32_t lba, uint8_t *buffer, uint32_t bufsize); int32_t qspi_msc_read_cb_v2(uint32_t lba, void *buffer, uint32_t bufsize); void qspi_msc_flush_cb_v2(void); -bool msc_ready_callback(void); -void refreshMassStorage(void); -/***************************************************************************/ + /*! @brief Class that handles Wippersnapper's optional filesystem commands and storage. */ -/***************************************************************************/ class Wippersnapper_FS { public: Wippersnapper_FS(); ~Wippersnapper_FS(); - void InitUsbMsc(); - // Filesystem - bool MakeDefaultFilesystem(); - void HaltFilesystem(String msg); - void HaltFilesystem(String msg, ws_led_status_t ledStatusColor); - void EraseCircuitPythonFS(); - // boot.txt - bool CreateFileBoot(); - void WriteFileBoot(PGM_P str); - void EraseFileBoot(); - // secrets.json - void CreateFileSecrets(); - bool GetFileSecrets(); - void ParseFileSecrets(); -#ifdef ARDUINO_FUNHOUSE_ESP32S2 - void ParseFileDisplayCfg(displayConfig &displayFile); - void CreateDisplayCfg(); -#endif - // config.json - void CreateFileConfig(); - void GetPinSDCS(); - bool AddSDCSPinToFileConfig(uint8_t pin); - void AddI2cDeviceToFileConfig(uint32_t address, const char *driver_name, const char **sensor_type_strings = nullptr, size_t sensor_types_count = 0); + + void initUSBMSC(); + + void GetSDCSPin(); + + bool writeFSContents(); + void fsHalt(String msg); + void fsHalt(String msg, ws_led_status_t ledStatusColor); + void eraseCPFS(); + + bool createBootFile(); + void writeToBootOut(PGM_P str); + void eraseBootFile(); + void AddI2cDeviceToFileConfig(uint32_t address, const char *driver_name, + const char **sensor_type_strings, + size_t sensor_types_count); bool WriteFileConfig(); - // getter for _doc_cfg + bool AddSDCSPinToFileConfig(uint8_t pin); JsonDocument &GetDocCfg() { return _doc_cfg; } + + // Secrets.json API + void createSecretsFile(); + bool getSecretsFile(); + void parseSecrets(); +#ifdef ARDUINO_FUNHOUSE_ESP32S2 + void parseDisplayConfig(displayConfig &displayFile); + void createDisplayConfig(); +#endif private: + bool _is_secrets_file_empty = false; JsonDocument _doc_cfg; }; extern Wippersnapper_V2 WsV2; -#endif // Wippersnapper_FS_H \ No newline at end of file +#endif // Wippersnapper_FS_V2_V2_H \ No newline at end of file diff --git a/src/ws_adapters.h b/src/ws_adapters.h index 3459d437e..d2bcf87bb 100644 --- a/src/ws_adapters.h +++ b/src/ws_adapters.h @@ -40,8 +40,8 @@ typedef ws_wifi_esp32 ws_adapter_wifi; #include "adapters/wifi/ws_wifi_esp32.h" typedef ws_wifi_esp32 ws_adapter_wifi; // Networking adapters for Raspberry Pi Pico W-series -#elif defined(ARDUINO_RASPBERRY_PI_PICO_W) -#define SD_CS_PIN 17 +#elif defined(ARDUINO_RASPBERRY_PI_PICO_W) || \ + defined(ARDUINO_RASPBERRY_PI_PICO_2W) #include "adapters/wifi/ws_wifi_pico.h" typedef ws_wifi_pico ws_adapter_wifi; // Networking adapter for Arduino Nano 33 IoT and MKR WiFi 1010 diff --git a/tests/bin/offline/firmware.elf b/tests/bin/offline/firmware.elf index 8a53779e0..16e90150a 100755 Binary files a/tests/bin/offline/firmware.elf and b/tests/bin/offline/firmware.elf differ diff --git a/tests/test_offline.py b/tests/test_offline.py index 06ccf4a00..b5b44c118 100644 --- a/tests/test_offline.py +++ b/tests/test_offline.py @@ -30,9 +30,11 @@ def test_analog_input(): assert result.returncode == 0 -def test_ds18b20(): +"""def test_ds18b20(): result = run_wokwi_cli(f"tests/bin/offline/firmware.elf", "120000", f"tests/scenarios/offline/test-log-ds18b20.scenario.yaml", f"tests/diagrams/offline.json") assert result.returncode == 0 +""" + def test_invalid_json(): result = run_wokwi_cli(f"tests/bin/offline/firmware.elf", "120000", f"tests/scenarios/offline/test-invalid-json.scenario.yaml", f"tests/diagrams/offline.json")