├── .actrc ├── .clang-format ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── build_documentation.yaml │ ├── build_examples.yaml │ ├── changelog_reminder.yaml │ ├── prepare_release.yaml │ └── verify_library_structure.yaml ├── .prettierignore ├── ChangeLog.md ├── LICENSE.md ├── README.md ├── VERSION ├── _config.yml ├── build-menu-configurations.ps1 ├── continuous_integration ├── ReadMe.md ├── check_component_inclusion.py ├── dependencies.json ├── generate_job_matrix.py ├── platformio_extra_flags.ini └── pre-commit-check-deps.py ├── docs ├── ColorText.png ├── Doxyfile ├── DoxygenLayout.xml ├── FAQ │ ├── Arduino-Streams.md │ ├── Code-Debugging.md │ ├── Deceasing-Memory-Use.md │ ├── Developer-Setup.md │ ├── Power-Parasites.md │ └── Processor-Compatibility.md ├── General-Sensor-Notes.md ├── Getting-Started │ ├── Getting-Started.md │ ├── Library-Dependencies.md │ ├── Physical-Dependencies.md │ └── Terminology.md ├── MS_FavIcon.png ├── Modem-Notes.md ├── ModularSensors_ubuntu_264x55.png ├── directories.dox ├── doxygen_extra_pages.dox ├── mcss-Doxyfile └── mcss-conf.py ├── examples ├── DRWI_2G │ ├── DRWI_2G.ino │ └── ReadMe.md ├── DRWI_DigiLTE │ ├── DRWI_DigiLTE.ino │ └── ReadMe.md ├── DRWI_Mayfly1 │ ├── DRWI_Mayfly1.ino │ └── ReadMe.md ├── DRWI_Mayfly1_WiFi │ ├── DRWI_Mayfly1_WiFi.ino │ └── ReadMe.md ├── DRWI_NoCellular │ ├── DRWI_NoCellular.ino │ └── ReadMe.md ├── DRWI_SIM7080LTE │ ├── DRWI_SIM7080LTE.ino │ └── ReadMe.md ├── ReadMe.md ├── baro_rho_correction │ ├── ReadMe.md │ └── baro_rho_correction.ino ├── data_saving │ ├── ReadMe.md │ └── data_saving.ino ├── double_logger │ ├── ReadMe.md │ └── double_logger.ino ├── example_dependencies.json ├── examples.dox ├── logging_to_MMW │ ├── ReadMe.md │ └── logging_to_MMW.ino ├── logging_to_ThingSpeak │ ├── ReadMe.md │ └── logging_to_ThingSpeak.ino ├── menu_a_la_carte │ ├── ReadMe.md │ └── menu_a_la_carte.ino ├── simple_logging │ ├── ReadMe.md │ └── simple_logging.ino ├── simple_logging_LearnEnviroDIY │ ├── ReadMe.md │ └── simple_logging_LearnEnviroDIY.ino └── single_sensor │ ├── ReadMe.md │ └── single_sensor.ino ├── extras ├── LTExBee_FirstConnection │ └── LTExBee_FirstConnection.ino ├── LTExBee_FirstConnectionBypass │ └── LTExBee_FirstConnectionBypass.ino ├── ReadMe.md ├── Stream_Debug │ └── Stream_Debug.ino ├── extras.dox ├── i2c_scanner │ └── i2c_scanner.ino ├── i2c_warmUp │ └── i2c_warmUp.ino ├── interrupt_counter │ └── interrupt_counter.ino ├── mega_serial_spy │ └── mega_serial_spy.ino ├── oneWireSearch │ └── oneWireSearch.ino ├── powerOn │ └── powerOn.ino ├── resetBee │ └── resetBee.ino └── sdi12_address_change │ └── sdi12_address_change.ino ├── library.json ├── library.properties └── src ├── LoggerBase.cpp ├── LoggerBase.h ├── LoggerModem.cpp ├── LoggerModem.h ├── ModSensorDebugger.h ├── ModSensorInterrupts.h ├── ModularSensors.h ├── SensorBase.cpp ├── SensorBase.h ├── VariableArray.cpp ├── VariableArray.h ├── VariableBase.cpp ├── VariableBase.h ├── WatchDogs ├── WatchDogAVR.cpp ├── WatchDogAVR.h ├── WatchDogSAMD.cpp └── WatchDogSAMD.h ├── dataPublisherBase.cpp ├── dataPublisherBase.h ├── modems ├── DigiXBee.cpp ├── DigiXBee.h ├── DigiXBee3GBypass.cpp ├── DigiXBee3GBypass.h ├── DigiXBeeCellularTransparent.cpp ├── DigiXBeeCellularTransparent.h ├── DigiXBeeLTEBypass.cpp ├── DigiXBeeLTEBypass.h ├── DigiXBeeWifi.cpp ├── DigiXBeeWifi.h ├── EspressifESP32.h ├── EspressifESP8266.cpp ├── EspressifESP8266.h ├── LoggerModemMacros.h ├── QuectelBG96.cpp ├── QuectelBG96.h ├── SIMComSIM7000.cpp ├── SIMComSIM7000.h ├── SIMComSIM7080.cpp ├── SIMComSIM7080.h ├── SIMComSIM800.cpp ├── SIMComSIM800.h ├── SequansMonarch.cpp ├── SequansMonarch.h ├── Sodaq2GBeeR6.cpp ├── Sodaq2GBeeR6.h ├── SodaqUBeeR410M.cpp ├── SodaqUBeeR410M.h ├── SodaqUBeeU201.cpp └── SodaqUBeeU201.h ├── publishers ├── DreamHostPublisher.cpp ├── DreamHostPublisher.h ├── EnviroDIYPublisher.cpp ├── EnviroDIYPublisher.h ├── ThingSpeakPublisher.cpp ├── ThingSpeakPublisher.h ├── UbidotsPublisher.cpp └── UbidotsPublisher.h ├── sensors ├── AOSongAM2315.cpp ├── AOSongAM2315.h ├── AOSongDHT.cpp ├── AOSongDHT.h ├── AlphasenseCO2.cpp ├── AlphasenseCO2.h ├── AnalogElecConductivity.cpp ├── AnalogElecConductivity.h ├── ApogeeSQ212.cpp ├── ApogeeSQ212.h ├── AtlasParent.cpp ├── AtlasParent.h ├── AtlasScientificCO2.cpp ├── AtlasScientificCO2.h ├── AtlasScientificDO.cpp ├── AtlasScientificDO.h ├── AtlasScientificEC.cpp ├── AtlasScientificEC.h ├── AtlasScientificORP.h ├── AtlasScientificRTD.h ├── AtlasScientificpH.h ├── BoschBME280.cpp ├── BoschBME280.h ├── BoschBMP3xx.cpp ├── BoschBMP3xx.h ├── CampbellClariVUE10.h ├── CampbellOBS3.cpp ├── CampbellOBS3.h ├── CampbellRainVUE10.h ├── Decagon5TM.cpp ├── Decagon5TM.h ├── DecagonCTD.h ├── DecagonES2.h ├── EverlightALSPT19.cpp ├── EverlightALSPT19.h ├── ExternalVoltage.h ├── FreescaleMPL115A2.cpp ├── FreescaleMPL115A2.h ├── GroPointGPLP8.h ├── GroPointParent.cpp ├── GroPointParent.h ├── InSituRDO.h ├── InSituTrollSdi12a.h ├── KellerAcculevel.h ├── KellerNanolevel.h ├── KellerParent.cpp ├── KellerParent.h ├── MaxBotixSonar.cpp ├── MaxBotixSonar.h ├── MaximDS18.cpp ├── MaximDS18.h ├── MaximDS3231.cpp ├── MaximDS3231.h ├── MeaSpecMS5803.cpp ├── MeaSpecMS5803.h ├── MeterHydros21.h ├── MeterTeros11.cpp ├── MeterTeros11.h ├── PaleoTerraRedox.cpp ├── PaleoTerraRedox.h ├── ProcessorStats.cpp ├── ProcessorStats.h ├── RainCounterI2C.cpp ├── RainCounterI2C.h ├── SDI12Sensors.cpp ├── SDI12Sensors.h ├── SensirionSHT4x.cpp ├── SensirionSHT4x.h ├── TIADS1x15.cpp ├── TIADS1x15.h ├── TIINA219.cpp ├── TIINA219.h ├── TallyCounterI2C.cpp ├── TallyCounterI2C.h ├── TurnerCyclops.cpp ├── TurnerCyclops.h ├── TurnerTurbidityPlus.cpp ├── TurnerTurbidityPlus.h ├── VegaPuls21.h ├── YosemitechParent.cpp ├── YosemitechParent.h ├── YosemitechY4000.h ├── YosemitechY504.h ├── YosemitechY510.h ├── YosemitechY511.h ├── YosemitechY513.h ├── YosemitechY514.h ├── YosemitechY520.h ├── YosemitechY532.h ├── YosemitechY533.h ├── YosemitechY551.h ├── YosemitechY560.h ├── YosemitechY700.h └── ZebraTechDOpto.h └── src.dox /.actrc: -------------------------------------------------------------------------------- 1 | --platform ubuntu-latest=ghcr.io/catthehacker/ubuntu:full-latest 2 | --pull=false 3 | --detect-event 4 | --env RUNNER_DEBUG=1 # Enable debug logging 5 | # --env NODE_DEBUG= # Might be useful in few cases 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -3 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: true 8 | AlignConsecutiveDeclarations: true 9 | AlignEscapedNewlines: Left 10 | AlignOperands: false 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Always 16 | AllowShortCaseLabelsOnASingleLine: true 17 | AllowShortEnumsOnASingleLine: true 18 | AllowShortFunctionsOnASingleLine: Empty 19 | AllowShortIfStatementsOnASingleLine: WithoutElse 20 | AllowShortLambdasOnASingleLine: All 21 | AllowShortLoopsOnASingleLine: true 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: false 25 | AlwaysBreakTemplateDeclarations: Yes 26 | BinPackArguments: true 27 | BinPackParameters: true 28 | BreakAfterJavaFieldAnnotations: false 29 | BreakBeforeBinaryOperators: None 30 | BreakBeforeBraces: Attach 31 | BreakBeforeInheritanceComma: false 32 | BreakBeforeTernaryOperators: true 33 | BreakConstructorInitializersBeforeComma: false 34 | BreakConstructorInitializers: BeforeColon 35 | BreakInheritanceList: BeforeColon 36 | BreakStringLiterals: true 37 | ColumnLimit: 80 38 | CommentPragmas: '^ IWYU pragma:' 39 | CompactNamespaces: false 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 41 | ConstructorInitializerIndentWidth: 4 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | DisableFormat: false 46 | EmptyLineAfterAccessModifier: Leave 47 | EmptyLineBeforeAccessModifier: Leave 48 | ExperimentalAutoDetectBinPacking: false 49 | FixNamespaceComments: true 50 | ForEachMacros: 51 | - foreach 52 | - Q_FOREACH 53 | - BOOST_FOREACH 54 | IncludeBlocks: Preserve 55 | IncludeCategories: 56 | - Regex: 'TinyGsmClient.h' 57 | Priority: -1 58 | - Regex: 'VariableBase.h' 59 | Priority: -1 60 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 61 | Priority: 2 62 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 63 | Priority: 3 64 | - Regex: '.*' 65 | Priority: 1 66 | IncludeIsMainRegex: '([-_](test|unittest))?$' 67 | IndentAccessModifiers: false 68 | IndentCaseBlocks: false 69 | IndentCaseLabels: true 70 | IndentExternBlock: Indent 71 | IndentGotoLabels: true 72 | IndentPPDirectives: None 73 | IndentWidth: 4 74 | IndentWrappedFunctionNames: false 75 | InsertTrailingCommas: None 76 | JavaScriptQuotes: Leave 77 | JavaScriptWrapImports: true 78 | KeepEmptyLinesAtTheStartOfBlocks: false 79 | MacroBlockBegin: '' 80 | MacroBlockEnd: '' 81 | MaxEmptyLinesToKeep: 2 82 | NamespaceIndentation: None 83 | # ObjCBinPackProtocolList: Auto 84 | ObjCBlockIndentWidth: 2 85 | ObjCSpaceAfterProperty: false 86 | ObjCSpaceBeforeProtocolList: true 87 | PackConstructorInitializers: CurrentLine 88 | PenaltyBreakAssignment: 25 89 | PenaltyBreakBeforeFirstCallParameter: 19 90 | PenaltyBreakComment: 300 91 | PenaltyBreakFirstLessLess: 120 92 | PenaltyBreakString: 1000 93 | PenaltyBreakTemplateDeclaration: 10 94 | PenaltyExcessCharacter: 600 95 | PenaltyReturnTypeOnItsOwnLine: 50 96 | PointerAlignment: Left 97 | PointerBindsToType: true 98 | QualifierAlignment: Left 99 | ReferenceAlignment: Left 100 | ReflowComments: true 101 | SeparateDefinitionBlocks: Leave 102 | SortIncludes: false 103 | SortUsingDeclarations: true 104 | SpaceAfterCStyleCast: false 105 | SpaceAfterLogicalNot: false 106 | SpaceAfterTemplateKeyword: true 107 | SpaceBeforeAssignmentOperators: true 108 | SpaceBeforeCaseColon: false 109 | SpaceBeforeCpp11BracedList: false 110 | SpaceBeforeCtorInitializerColon: true 111 | SpaceBeforeInheritanceColon: true 112 | SpaceBeforeParens: ControlStatements 113 | SpaceBeforeRangeBasedForLoopColon: true 114 | SpaceInEmptyParentheses: false 115 | SpacesBeforeTrailingComments: 2 116 | SpacesInAngles: false 117 | SpacesInContainerLiterals: true 118 | SpacesInCStyleCastParentheses: false 119 | SpacesInConditionalStatement: false 120 | SpacesInLineCommentPrefix: 121 | Minimum: 1 122 | SpacesInParentheses: false 123 | SpacesInSquareBrackets: false 124 | Standard: Cpp11 125 | TabWidth: 4 126 | UseTab: Never 127 | --- 128 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ['https://stroudcenter.org/donate/'] 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'weekly' 7 | labels: 8 | - 'CI/CD' 9 | commit-message: 10 | prefix: ci 11 | -------------------------------------------------------------------------------- /.github/workflows/build_documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Check, Build, and Publish Documentation 2 | 3 | on: 4 | # Triggers the workflow on push or pull request events 5 | push: 6 | pull_request: 7 | # Trigger when a release is created 8 | # NOTE: This will only trigger if the release is created from the UI or with a personal access token 9 | release: 10 | types: 11 | - published 12 | # Trigger with the release workflow finishes 13 | workflow_run: 14 | workflows: ['Create a New Release'] 15 | types: [completed] 16 | branches: [master] 17 | # Also give a manual trigger 18 | workflow_dispatch: 19 | inputs: 20 | publish: 21 | description: 'Publish Documentation to GitHub Pages' 22 | required: false 23 | type: boolean 24 | default: false 25 | 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: true 29 | 30 | jobs: 31 | check_menu_inclusion: 32 | runs-on: ubuntu-latest 33 | if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} 34 | name: Check that all classes are documented in the menu-a-la-carte example 35 | 36 | steps: 37 | - uses: actions/checkout@v4 38 | 39 | - name: Set up Python 40 | uses: actions/setup-python@v5 41 | with: 42 | python-version: '3.x' 43 | 44 | # Using answer from here to get the exit code and pass the output: https://stackoverflow.com/questions/59191913/how-do-i-get-the-output-of-a-specific-step-in-github-actions 45 | - name: check for classes in the menu example 46 | id: check_component 47 | continue-on-error: true 48 | run: | 49 | cd $GITHUB_WORKSPACE/continuous_integration 50 | python check_component_inclusion.py 2>&1 | tee check_component.log 51 | result_code=${PIPESTATUS[0]} 52 | missing_menu_docs=$(cat check_component.log) 53 | missing_menu_docs="${missing_menu_docs//'%'/'%25'}" 54 | missing_menu_docs="${missing_menu_docs//$'\n'/'%0A'}" 55 | missing_menu_docs="${missing_menu_docs//$'\r'/'%0D'}" 56 | echo "missing_menu_docs=missing_menu_docs" >> $GITHUB_OUTPUT 57 | if [[ $result_code ]]; then 58 | echo "$(cat check_component.log)" >> $GITHUB_STEP_SUMMARY 59 | else 60 | echo "Valid library.json =)" >> $GITHUB_STEP_SUMMARY 61 | fi 62 | echo "Finished menu inclusion verification" 63 | exit $result_code 64 | 65 | - name: Create commit comment 66 | uses: peter-evans/commit-comment@v3 67 | if: steps.check_component.outcome=='failure' 68 | with: 69 | body: | 70 | All sensor and variable subclasses must be included in the Menu a la Carte example 71 | ${{ steps.check_component.outputs.missing_menu_docs }} 72 | 73 | - name: Fail if cannot find all menu flags 74 | id: verification_failure 75 | if: steps.check_component.outcome=='failure' 76 | run: exit 1 77 | 78 | doc_build: 79 | if: ${{ (! contains(github.event.head_commit.message, 'ci skip')) && (github.event_name != 'workflow_run' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')) }} 80 | name: Build documentation 81 | uses: EnviroDIY/workflows/.github/workflows/build_documentation.yaml@main 82 | with: 83 | use_graphviz: false 84 | publish: ${{ (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true')}} 85 | rebuild_cache_number: 1 86 | secrets: inherit 87 | -------------------------------------------------------------------------------- /.github/workflows/build_examples.yaml: -------------------------------------------------------------------------------- 1 | name: Build Examples 2 | 3 | # Triggers the workflow on push or pull request events 4 | on: [push, pull_request] 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | build_examples: 12 | name: Build all examples with PlatformIO and the Arduino CLI 13 | if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} 14 | uses: EnviroDIY/workflows/.github/workflows/build_examples.yaml@main 15 | with: 16 | boards_to_build: 'mayfly,megaatmega2560,zeroUSB,adafruit_feather_m0' 17 | secrets: inherit 18 | -------------------------------------------------------------------------------- /.github/workflows/changelog_reminder.yaml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | name: Changelog Reminder 3 | jobs: 4 | remind: 5 | name: Changelog Reminder 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | with: 10 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 11 | 12 | - name: Changelog Reminder 13 | uses: peterjgrainger/action-changelog-reminder@v1.3.0 14 | with: 15 | changelog_regex: '/CHANGELOG\/.*\/*.md/i' 16 | customPrMessage: 'Please add your changes to the change log!' 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/prepare_release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | # Sequence of patterns matched against refs/tags 6 | paths: 7 | - 'VERSION' # Push events when the VERSION file changes 8 | workflow_dispatch: 9 | inputs: 10 | include_dependencies: 11 | description: 'True to include a zip file with dependencies in the release' 12 | required: false 13 | type: boolean 14 | default: true 15 | 16 | name: Create a New Release 17 | 18 | env: 19 | PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} 20 | 21 | jobs: 22 | wait_for_checks: 23 | if: ${{ github.event_name != 'workflow_dispatch' }} 24 | strategy: 25 | matrix: 26 | req_workflow: 27 | [ 28 | verify_library_structure.yaml, 29 | build_examples.yaml, 30 | build_documentation.yaml, 31 | ] 32 | name: Wait for Checks to complete 33 | runs-on: ubuntu-latest 34 | steps: 35 | - name: Wait on Workflow 36 | uses: ArcticLampyrid/action-wait-for-workflow@v1.2.0 37 | with: 38 | workflow: ${{ matrix.req_workflow }} 39 | sha: ${{ github.sha || github.event.pull_request.head.sha || github.event.pull_request.head.ref }} # optional 40 | allowed-conclusions: | 41 | success 42 | cancelled 43 | skipped 44 | 45 | release: 46 | name: Prepare a new release 47 | needs: [wait_for_checks] 48 | if: | 49 | always() && 50 | (needs.wait_for_checks.result == 'success' || needs.wait_for_checks.result == 'skipped') 51 | uses: EnviroDIY/workflows/.github/workflows/prepare_release.yaml@main 52 | secrets: inherit 53 | with: 54 | include_dependencies: ${{ ( true ) || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true') }} 55 | -------------------------------------------------------------------------------- /.github/workflows/verify_library_structure.yaml: -------------------------------------------------------------------------------- 1 | name: Verify library manifest and structure 2 | 3 | # Triggers the workflow on push or pull request events 4 | on: [push, pull_request] 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | verify_library_structure: 12 | name: Validate library structure 13 | if: ${{ ! contains(github.event.head_commit.message, 'ci skip') }} 14 | uses: EnviroDIY/workflows/.github/workflows/verify_library_structure.yaml@main 15 | with: 16 | library-manager: 'submit' 17 | library-compliance: 'strict' 18 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs/header.html -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # Software License Agreement (BSD-3 License) 2 | 3 | **Copyright (c) 2010-2024, Stroud Water Research Center (SWRC) and the EnviroDIY Development Team.** 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 15 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.36.0 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /continuous_integration/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Extra scripts for Continuous Integration Using GitHub Actions 2 | 3 | - check_component_inclusion.py 4 | - A python script to check that all sensor, modem, and publisher classes are included in the massive menu a la carte example. 5 | 6 | - dependencies.json 7 | - A copy of the library dependencies in library.json to diff against for dependency changes. 8 | - The dependencies.json is automatically generated by a git pre-commit hook. 9 | 10 | - pre-commit-check-deps.py 11 | - A python script to copy from the library.json to dependencies.json for rebuilding dependency archives on the GitHub actions runner. 12 | - This is run as a pre-commit hook 13 | 14 | - generate_job_matrix.py 15 | - A python script to generate a series of shell scripts to splice apart the menu a la carte example and run each sensor, modem, and publisher as indivudual testing jobs on a number of different processors. 16 | 17 | - install-deps-arsuino-cli.sh 18 | - A shell script to install all Arduino library dependencies on the GitHub actions runner used for CI testing. 19 | 20 | - install-deps-platformio.sh 21 | - A shell script to install all PlatformIO library dependencies on the GitHub actions runner used for CI testing. 22 | 23 | - continuous_integration/platformio.ini 24 | - PlatformIO environments for CI testing 25 | 26 | - continuous_integration/platformio_extra_flags.ini 27 | - Even more PlatformIO environments for CI testing 28 | -------------------------------------------------------------------------------- /continuous_integration/platformio_extra_flags.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [env:software_wire] 12 | extends = env:mayfly 13 | build_flags = 14 | -D SDI12_EXTERNAL_PCINT 15 | -D MS_PALEOTERRA_SOFTWAREWIRE 16 | -D MS_RAIN_SOFTWAREWIRE 17 | lib_deps = 18 | https://github.com/Testato/SoftwareWire.git#v1.5.1 19 | 20 | [env:ads1015] 21 | extends = env:mayfly 22 | build_flags = 23 | -D SDI12_EXTERNAL_PCINT 24 | -D MS_USE_ADS1015 25 | 26 | [env:sdi12_non_concurrent] 27 | extends = env:mayfly 28 | build_flags = 29 | -D SDI12_EXTERNAL_PCINT 30 | -D MS_SDI12_NON_CONCURRENT 31 | 32 | [env:AltSoftSerial] 33 | extends = env:mayfly 34 | build_flags = 35 | -D SDI12_EXTERNAL_PCINT 36 | -D BUILD_TEST_ALTSOFTSERIAL 37 | lib_deps = 38 | https://github.com/PaulStoffregen/AltSoftSerial.git 39 | 40 | [env:NeoSWSerial] 41 | extends = env:mayfly 42 | build_flags = 43 | -D SDI12_EXTERNAL_PCINT 44 | -D NEOSWSERIAL_EXTERNAL_PCINT 45 | -D BUILD_TEST_NEOSWSERIAL 46 | lib_deps = 47 | https://github.com/SRGDamia1/NeoSWSerial.git 48 | 49 | [env:SoftwareSerial] 50 | extends = env:mayfly 51 | build_flags = 52 | -D SDI12_EXTERNAL_PCINT 53 | -D BUILD_TEST_SOFTSERIAL 54 | lib_deps = 55 | https://github.com/EnviroDIY/SoftwareSerial_ExternalInts.git 56 | -------------------------------------------------------------------------------- /continuous_integration/pre-commit-check-deps.py: -------------------------------------------------------------------------------- 1 | import json 2 | import subprocess 3 | 4 | # Open the current library.json file 5 | library_json = open( 6 | "C:\\Users\\sdamiano\\Documents\\GitHub\\EnviroDIY\\ModularSensors\\library.json", 7 | ) 8 | library_data = json.load(library_json) 9 | 10 | # Open the current library.json file 11 | dependency_json = open( 12 | "C:\\Users\\sdamiano\\Documents\\GitHub\\EnviroDIY\\ModularSensors\\continuous_integration\\dependencies.json", 13 | ) 14 | dependency_data = json.load(dependency_json) 15 | 16 | lib_dependencies = library_data["dependencies"] 17 | out_dependencies = dependency_data["dependencies"] 18 | 19 | if lib_dependencies != out_dependencies: 20 | dependency_data["action_cache_version"] += 1 21 | dependency_data["dependencies"] = library_data["dependencies"] 22 | 23 | with open( 24 | "C:\\Users\\sdamiano\\Documents\\GitHub\\EnviroDIY\\ModularSensors\\continuous_integration\\dependencies.json", 25 | "w", 26 | ) as outfile: 27 | json.dump(dependency_data, outfile, indent=2) 28 | 29 | subprocess.run("git add continuous_integration\\dependencies.json") 30 | -------------------------------------------------------------------------------- /docs/ColorText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnviroDIY/ModularSensors/26f255895bb96d6bea1d5b1be3d1d102ab303ea2/docs/ColorText.png -------------------------------------------------------------------------------- /docs/FAQ/Code-Debugging.md: -------------------------------------------------------------------------------- 1 | # In-Library Debugging 2 | 3 | For intense _code_ debugging for any individual component of the library (sensor communication, modem communication, variable array functions, etc), open the source file header (\*.h), for that component. 4 | Find the line `// #define DEBUGGING_SERIAL_OUTPUT xxxxx`, where xxxxx is the name of a serial output (ie, Serial or USBSerial). 5 | Remove the two comment slashes from that line. 6 | Then recompile and upload your code. 7 | This will (sometimes dramatically) increase the number of statements going out to the debugging serial port. 8 | A few sensors also the line `// #define DEEP_DEBUGGING_SERIAL_OUTPUT xxxxx`, un-commenting of which will send even more information to the defined port. 9 | Note that this type of debugging is intended to help find errors in the code of this library, not problems with the results given by any sensors! 10 | -------------------------------------------------------------------------------- /docs/FAQ/Deceasing-Memory-Use.md: -------------------------------------------------------------------------------- 1 | # Decreasing Memory Footprint 2 | 3 | There are things you could do to decrease memory use. 4 | 5 | Decrease max num vars etc 6 | 7 | @todo The decreasing memory footprint section is not yet written. 8 | -------------------------------------------------------------------------------- /docs/FAQ/Power-Parasites.md: -------------------------------------------------------------------------------- 1 | # Power Draw over Data Lines 2 | 3 | When deploying a logger out into the wild and depending on only battery or solar charging, getting the power draw from sensors to be as low as possible is crucial. 4 | This library assumes that the main power/Vcc supply to each sensor can be turned on by setting its powerPin high and off by setting its powerPin low. 5 | For most well-designed sensors, this should stop all power draw from the sensor. 6 | Real sensors, unfortunately, aren't as well designed as one might hope and some sensors (and particularly RS485 adapters) can continue to suck power from by way of high or floating data pins. 7 | For most sensors, this library attempts to set all data pins low when sending the sensors and then logger to sleep. 8 | 9 | If you are still seeing "parasitic" power draw, here are some work-arounds you can try: 10 | 11 | - For sensors (and adapters) drawing power over a serial line: 12 | - Write-out your entire loop function. 13 | (Don't just use `logData()`.) 14 | - Add a `SerialPortName.begin(BAUD);` statement to the beginning of your loop, before `sensorsPowerUp()`. 15 | - After `sensorsPowerDown()` add `SerialPortName.end(BAUD);`. 16 | - After "ending" the serial communication, explicitly set your Rx and Tx pins low using `digitalWrite(#, LOW);`. 17 | - For sensors drawing power over I2C: 18 | - Many (most?) boards have external pull-up resistors on the hardware I2C/Wire pins which cannot be disconnected from the main power supply. 19 | This means I2C parasitic power draw is best solved via hardware, not software. 20 | - Use a specially designed I2C isolator 21 | - Use a generic opto-isolator or other type of isolator on both the SCL and SDA lines 22 | - In this future, this library _may_ offer the option of using software I2C, which would allow you to use the same technique as is currently usable to stop serial parasitic draw. 23 | Until such an update happens, however, hardware solutions are required. 24 | 25 | The ["data_saving"](@todo add link to loop of datasaving example) example shows setting ending a serial stream and seeting pins low to prevent an RS485 adapter from drawing power during sleep. 26 | -------------------------------------------------------------------------------- /docs/General-Sensor-Notes.md: -------------------------------------------------------------------------------- 1 | # Notes about Sensors 2 | 3 | There are a number of sensors supported by this library. 4 | Depending on the sensor, it may communicate with the Arduino board using as a serial peripheral interface (SPI), inter-integrated circuit (I2C, also called "Wire," "Two Wire", or "TWI"), or some type of universal synchronous/asynchronous receiver/transmitter (UART/USART, or simply "serial") protocol. 5 | (USART or serial includes transistor-transistor logic (TTL), RS232 (adapter needed), and RS485 (adapter needed) communication). 6 | See the section on [Processor Compatibility](https://envirodiy.github.io/ModularSensors/page_processor_compatibility.html) for more specific notes on which pins are available for each type of communication on the various supported processors. 7 | 8 | Essentially all of the sensors can have their power supplies turned off between readings, but not all boards are able to switch output power on and off. 9 | When the sensor constructor asks for the Arduino pin controlling power on/off, use -1 for any board which is not capable of switching the output power on and off. 10 | 11 | Please, please, when setting up multiple sensors on a logger, be smart about it. 12 | Don't try to connect too many sensors all at once or you're likely to exceed your logger's power regulator or come across strange interferences between them. 13 | _**TEST YOUR LOGGER WITH ALL SENSORS ATTACHED BEFORE DEPLOYING IT TO THE FIELD!**_ 14 | Don't even think about skipping the in-lab testing! 15 | Theoretically every single sensor possible could be attached to the same processor, but the reality is that boards have finite numbers of pins, solar panels can only create so much charge, and not all sensors like each other very much. 16 | -------------------------------------------------------------------------------- /docs/Getting-Started/Library-Dependencies.md: -------------------------------------------------------------------------------- 1 | # Library Dependencies 2 | 3 | > [!WARNING] 4 | > This page is frequently out of date. Please see the library.json or dependencies.json and example_dependencies.json for the most up-to-date library references! 5 | 6 | In order to support multiple functions and sensors, there are quite a lot of sub-libraries that this library depends on. 7 | _Even if you do not use the modules, you must have all of the dependencies installed for the library itself to properly compile._ 8 | Please check the [library.json](https://github.com/EnviroDIY/ModularSensors/blob/master/library.json) file for more details on the versions required of each library. 9 | If you are using [PlatformIO](https://platformio.org), you can list "EnviroDIY_ModularSensors" in the `lib_deps` section of your platformio.ini file and all of these libraries will be installed automatically. 10 | If using the "standard" Arduino IDE, you must install each of these libraries individually, or in a bundle from the [EnviroDIY Libraries](https://github.com/EnviroDIY/Libraries) meta-repository. 11 | 12 | - [EnableInterrupt](https://github.com/GreyGnome/EnableInterrupt) - Administrates and handles pin change interrupts, allowing the logger to sleep and save battery. 13 | This also controls the interrupts for the versions of SoftwareSerial and SDI-12 linked below that have been stripped of interrupt control. 14 | Because we use this library, _you must always add the line `#include ` to the top of your sketch._ 15 | - AVR sleep library - This is for low power sleeping for AVR processors. 16 | (This library is built in to the Arduino and PlatformIO IDEs.) 17 | - [EnviroDIY DS-3231](https://github.com/EnviroDIY/Sodaq_DS3231) - For real time clock control 18 | - [RTCZero library](https://github.com/arduino-libraries/RTCZero) - This real time clock control and low power sleeping on SAMD processors. 19 | (This library may be built in to the Arduino IDE.) 20 | NOTE: If using an AVR board, you must explicitly _ignore_ this library when compiling with PlatformIO or you will have compiler errors. 21 | - [SdFat library](https://github.com/greiman/SdFat) - This enables communication with the SD card. 22 | - [TinyGSM library](https://github.com/vshymanskyy/TinyGSM) - This provides internet (TCP/IP) connectivity. 23 | - [PubSubClient](https://github.com/knolleary/pubsubclient) - For MQTT connectivity 24 | - [Adafruit ADS1X15 library](https://github.com/adafruit/Adafruit_ADS1X15) - For high-resolution analog to digital conversion. 25 | - NOTE: As of version 0.36.0 the standard Adafruit library should be used, *NOT* the Soligen2010 fork. 26 | - [EnviroDIY Arduino SDI-12 library](https://github.com/EnviroDIY/Arduino-SDI-12/tree/ExtInts) - For control of SDI-12 based sensors. 27 | This modified version is needed so there are no pin change interrupt conflicts with the SoftwareSerial library or the software pin change interrupt library used to wake the processor. 28 | - [SensorModbusMaster](https://github.com/EnviroDIY/SensorModbusMaster) - for easy communication with Modbus devices. 29 | - [OneWire](https://github.com/PaulStoffregen/OneWire) - This enables communication with Maxim/Dallas OneWire devices. 30 | - [DallasTemperature](https://github.com/milesburton/Arduino-Temperature-Control-Library) - for communication with the DS18 line of Maxim/Dallas OneWire temperature probes. 31 | - [Adafruit BusIO](https://github.com/adafruit/Adafruit_BusIO) - a dependency of several other Adafruit libraries, used to unify commands for fetching data via SPI and I2C. 32 | - [Adafruit Unified Sensor Driver](https://github.com/adafruit/Adafruit_Sensor) - a dependency of several other Adafruit libraries, used to unify sensor data return types. 33 | - [Adafruit AM2315 library](https://github.com/adafruit/Adafruit_AM2315) - for the AOSong AM2315 temperature and humidity sensor. 34 | - [Adafruit DHT library](https://github.com/adafruit/DHT-sensor-library) - for other AOSong temperature and humidity sensors. 35 | - [Adafruit BME280 library](https://github.com/adafruit/Adafruit_BME280_Library) - for the Bosch BME280 environmental sensor. 36 | - [Adafruit INA219 library](https://github.com/adafruit/Adafruit_INA219) - for the INA219 current/voltage sensor. 37 | - [Adafruit MPL115A2 library](https://github.com/adafruit/Adafruit_MPL115A2) - for the Freescale Semiconductor MPL115A2 barometer. 38 | - [Adafruit SHT4x library](https://github.com/adafruit/Adafruit_SHT4X) - for the Senserion SHT40 temperature and humidity sensor. This sensor is built into the EnviroDIY Mayfly and Stonefly. 39 | - [YosemitechModbus](https://github.com/EnviroDIY/YosemitechModbus) - for all Yosemitech modbus environmental sensors. 40 | - [Northern Widget MS5803 Library](https://github.com/NorthernWidget/MS5803) - for the TE Connectivity MEAS MS5803 pressure sensor 41 | - [EnviroDIY KellerModbus Library](https://github.com/EnviroDIY/KellerModbus) - for all Keller modbus pressure and water level sensors. 42 | - [EnviroDIY GroPointModbus Library](https://github.com/EnviroDIY/GroPointModbus.git) - For GroPoint soil moisture probes. 43 | - [BMP388_DEV](https://registry.platformio.org/libraries/martinl1/BMP388_DEV) - for communication with the Bosch BMP388 barometer 44 | - WARNING: The repository for this library has been removed from GitHub. The library itself is still available in the PlatformIO and Arduino library registries. 45 | - [Tally Library](https://github.com/EnviroDIY/Tally_Library.git#Dev_I2C) - For the Project Tally Event sensor 46 | - NOTE: Use the `Dev_I2C` feature branch 47 | -------------------------------------------------------------------------------- /docs/Getting-Started/Physical-Dependencies.md: -------------------------------------------------------------------------------- 1 | # Physical Dependencies 2 | 3 | This library is designed for wireless, solar-powered environmental data logging applications, that is, to log data from many physical sensors and to put the processor and all peripherals to sleep to conserver power between readings. 4 | The most banal functions of the library require only an AVR or SAMD processor, but making real use of this library requires: 5 | 6 | - A sufficiently powerful AVR or SAMD processor mounted on some sort of circuit board. 7 | (See [Processor/Board Compatibility](https://envirodiy.github.io/ModularSensors/page_processor_compatibility.html) for more details on specific processors and boards that are supported.) 8 | - For all AVR processors, you must also have a [Maxim DS3231](https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS3231.html) high precision I2C real-time clock with the SQE/INT pin connected to a pin on your processor which supports either external or pin-change interrupts. 9 | - For SAMD boards, this library makes use of their on-board (though less accurate) real-time clock. 10 | - A SD card reader attached to the processor via SPI. 11 | - Environmental sensors 12 | - A battery to power the system 13 | - A solar charging circuit 14 | - A modem-type unit to communicate remote data (Optional for logging data, but required for sending data directly to the internet. 15 | - The list of supported modems is available here: 16 | - Protected water-proof enclosures and mountings for all of the above 17 | - An OTG cable to connect serial output from the board to a cell phone (Optional, but very helpful for debugging.) 18 | -------------------------------------------------------------------------------- /docs/MS_FavIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnviroDIY/ModularSensors/26f255895bb96d6bea1d5b1be3d1d102ab303ea2/docs/MS_FavIcon.png -------------------------------------------------------------------------------- /docs/ModularSensors_ubuntu_264x55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EnviroDIY/ModularSensors/26f255895bb96d6bea1d5b1be3d1d102ab303ea2/docs/ModularSensors_ubuntu_264x55.png -------------------------------------------------------------------------------- /docs/directories.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * @dir docs 3 | * 4 | * @brief Contains extra documentation and files to help build the documentation 5 | */ 6 | /** 7 | * @dir docs/FAQ 8 | * 9 | * @brief Contains documentation on frequently asked questions 10 | */ 11 | /** 12 | * @dir docs/Getting-Started 13 | * 14 | * @brief Contains documentation for new users 15 | */ 16 | -------------------------------------------------------------------------------- /docs/doxygen_extra_pages.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @page page_faq FAQs 3 | 4 | Here are some links to pages with more detailed information about some common issues with the ModularSensors library. 5 | 6 | @subpage page_processor_compatibility 7 | 8 | @subpage page_arduino_streams 9 | 10 | @subpage page_power_parasites 11 | 12 | @subpage page_memory_use 13 | 14 | @subpage page_code_debugging 15 | 16 | @subpage page_for_developers 17 | 18 | [TinyGSM Troubleshooting page](https://github.com/vshymanskyy/TinyGSM#troubleshooting) 19 | */ 20 | 21 | /** 22 | @page page_other_notes Other Sensor and Modem Notes 23 | 24 | Here are links to detailed information about the various supported sensors and modems. 25 | 26 | @subpage page_sensor_notes 27 | 28 | @subpage page_modem_notes 29 | */ 30 | -------------------------------------------------------------------------------- /docs/mcss-Doxyfile: -------------------------------------------------------------------------------- 1 | @INCLUDE = Doxyfile 2 | GENERATE_HTML = NO 3 | GENERATE_XML = YES 4 | XML_PROGRAMLISTING = NO 5 | HTML_OUTPUT = ../ModularSensors_Doxygen/m.css 6 | SHOW_INCLUDE_FILES = YES 7 | WARN_LOGFILE = mcssDoxygenOutput.log 8 | PROJECT_REPOSITORY = https://github.com/EnviroDIY/ModularSensors 9 | ALIASES += \ 10 | "m_div{1}=@xmlonly@endxmlonly" \ 11 | "m_enddiv=@xmlonly@endxmlonly" \ 12 | "m_span{1}=@xmlonly@endxmlonly" \ 13 | "m_endspan=@xmlonly@endxmlonly" \ 14 | "m_class{1}=@xmlonly@endxmlonly" \ 15 | "m_footernavigation=@xmlonly@endxmlonly" \ 16 | "m_examplenavigation{2}=@xmlonly@endxmlonly" \ 17 | "m_keywords{1}=@xmlonly@endxmlonly" \ 18 | "m_keyword{3}=@xmlonly@endxmlonly" \ 19 | "m_enum_values_as_keywords=@xmlonly@endxmlonly" \ 20 | "m_since{2}=@since @m_class{m-label m-success m-flat} @ref changelog-\1-\2 \"since v\1.\2\"" \ 21 | "m_deprecated_since{3}=@since deprecated in v\1.\2.\3 @deprecated" 22 | 23 | HAVE_DOT = NO 24 | DOT_FONTNAME = Source Sans Pro 25 | DOT_FONTSIZE = 16 26 | -------------------------------------------------------------------------------- /examples/DRWI_Mayfly1/ReadMe.md: -------------------------------------------------------------------------------- 1 | # DRWI Sites with a Mayfly 1.x and EnviroDIY LTE Bee 2 | 3 | Example sketch for using the EnviroDIY SIM7080G LTE cellular module with an EnviroDIY Mayfly Data Logger. 4 | 5 | This example uses the sensors and equipment used by most groups participating in the DRWI (Delaware River Watershed Initiative) Citizen Science project with the Stroud Water Research Center. It includes a Meter Hydros 21 (CTD) and a SIM7080G-based EnviroDIY LTEbee for communication. This examples also makes use of the on-board light, temperature, and humidity sensors on the Mayfly 1.x. The results are saved to the SD card and posted to the Monitor My Watershed data portal. Only to be used with newer Mayfly v1.0 and v1.1 boards. 6 | 7 | The exact hardware configuration used in this example: 8 | 9 | - Mayfly v1.x board 10 | - EnviroDIY SIM7080 LTE module (with Hologram SIM card) 11 | - Hydros21 CTD sensor 12 | 13 | An EnviroDIY LTE SIM7080 module can be used with the older Mayfly v0.5b boards if you change line 101 (for modemVccPin) from 18 to -1. 14 | This is because the Mayfly v1.x board has a separate 3.3v regulator to power the Bee socket and is controlled by turning pin 18 on or off. 15 | Mayfly v0.5b has the Bee socket constantly powered, therefore using "-1" is the proper setting for that line of code. 16 | 17 | The EnviroDIY LTE SIM7080 module includes 2 antennas in the package. The small thin one is the cellular antenna, and should be connected to the socket labeled "CELL". The thicker block is the GPS antenna, and should be connected to the "GPS" socket, but only if you intend to use the GPS functionality of the module. ModularSensors does not currently suport GPS functionality, but other libraries such as TinyGPS can work with the SIM7080 module. 18 | 19 | The included cell antenna works best in high-signal-strength areas. For most remote areas and logger deployments, we suggest a larger LTE antenna, like the W3907B0100 20 | from PulseLarsen (Digikey 1837-1003-ND or Mouser 673-W3907B0100) 21 | 22 | Users purchasing a new Hydros21 CTD sensor will need to change the SDI-12 address of the sensor in order to use this sketch. Full instructions for using this sketch as part of a monitoring station can be found in the EnviroDIY Monitoring Station Manual. 23 | _______ 24 | 25 | [//]: # ( @tableofcontents ) 26 | 27 | [//]: # ( @m_footernavigation ) 28 | 29 | [//]: # ( Start GitHub Only ) 30 | 31 | - [DRWI Sites with a Mayfly 1.x and EnviroDIY LTE Bee](#drwi-sites-with-a-mayfly-1x-and-envirodiy-lte-bee) 32 | - [Unique Features of the DRWI Mayfly 1.x LTE Example](#unique-features-of-the-drwi-mayfly-1x-lte-example) 33 | 34 | [//]: # ( End GitHub Only ) 35 | 36 | _______ 37 | 38 | ## Unique Features of the DRWI Mayfly 1.x LTE Example 39 | 40 | - Specifically for sites within the Delaware River Watershed Initiative. 41 | - Uses a EnviroDIY LTE Bee based on the SIMCom SIM7080G 42 | 43 | [//]: # ( @section example_drwi_mayfly1_pio_config PlatformIO Configuration ) 44 | 45 | [//]: # ( @include{lineno} DRWI_Mayfly1/platformio.ini ) 46 | 47 | [//]: # ( @section example_drwi_mayfly1_code The Complete Code ) 48 | 49 | [//]: # ( @include{lineno} DRWI_Mayfly1/DRWI_Mayfly1.ino ) 50 | -------------------------------------------------------------------------------- /examples/DRWI_Mayfly1_WiFi/ReadMe.md: -------------------------------------------------------------------------------- 1 | # DRWI Sites with a Mayfly 1.x and EnviroDIY ESP32 WiFi Bee 2 | 3 | Example sketch for using the EnviroDIY ESP32 WiFi cellular module with an EnviroDIY Mayfly Data Logger. 4 | 5 | The exact hardware configuration used in this example: 6 | 7 | - Mayfly v1.1 board 8 | - EnviroDIY ESP32 WiFi module 9 | - Hydros21 CTD sensor 10 | 11 | An EnviroDIY ESP32 WiFi module can also be used with the older Mayfly v0.5b boards if you change line 95 (for modemVccPin) from 18 to -1. 12 | This is because the Mayfly v1.x board has a separate 3.3v regulator to power the Bee socket and is controlled by turning pin 18 on or off. 13 | Mayfly v0.5b has the Bee socket constantly powered, therefore using "-1" is the proper setting for that line of code. Leave the modemVccPin as 18 for Mayfly version 1.0 and 1.1. 14 | 15 | The WiFi antenna is built into the ESP32 Bee - no external antenna is needed 16 | 17 | Be sure to edit lines 101 and 102 to enter your Wifi access point name and password, and edit the UUID section beginning at line 200 with the correct UUIDs from your specific site on MonitorMyWatershed. 18 | 19 | _______ 20 | 21 | [//]: # ( @tableofcontents ) 22 | 23 | [//]: # ( @m_footernavigation ) 24 | 25 | [//]: # ( Start GitHub Only ) 26 | 27 | - [DRWI Sites with a Mayfly 1.x and EnviroDIY ESP32 WiFi Bee](#drwi-sites-with-a-mayfly-1x-and-envirodiy-esp32-wifi-bee) 28 | - [Unique Features of the DRWI Mayfly 1.x WiFi Example](#unique-features-of-the-drwi-mayfly-1x-wifi-example) 29 | 30 | [//]: # ( End GitHub Only ) 31 | 32 | _______ 33 | 34 | ## Unique Features of the DRWI Mayfly 1.x WiFi Example 35 | 36 | - Specifically for sites within the Delaware River Watershed Initiative. 37 | - Uses a EnviroDIY WiFi Bee based on the Espressif ESP32-WROOM-32 38 | 39 | [//]: # ( @section example_drwi_mayfly1_wifi_pio_config PlatformIO Configuration ) 40 | 41 | [//]: # ( @include{lineno} DRWI_Mayfly1_WiFi/platformio.ini ) 42 | 43 | [//]: # ( @section example_drwi_mayfly1_wifi_code The Complete Code ) 44 | 45 | [//]: # ( @include{lineno} DRWI_Mayfly1_WiFi/DRWI_Mayfly1_WiFi.ino ) 46 | -------------------------------------------------------------------------------- /examples/DRWI_SIM7080LTE/ReadMe.md: -------------------------------------------------------------------------------- 1 | # DRWI Sites with EnviroDIY LTE Bees 2 | 3 | The DRWI EnviroDIY LTEbee example uses the sensors and equipment common to older stations (2016-2020) deployed by groups participating in the DRWI Citizen Science project with the Stroud Water Research Center. It includes a Meter Hydros 21 (CTD), a Campbell OBS3+, (Turbidity) and a SIM7080G-based EnviroDIY LTEbee for communication. 4 | 5 | The exact hardware configuration used in this example: 6 | 7 | - Mayfly v1.x board 8 | - EnviroDIY SIM7080 LTE module (with Hologram SIM card) 9 | - Hydros21 CTD sensor 10 | - Campbell Scientific OBS3+ Turbidity sensor 11 | 12 | An EnviroDIY LTE SIM7080 module can be used with the older Mayfly v0.5b boards if you change line 101 (for modemVccPin) from 18 to -1. 13 | This is because the Mayfly v1.0 board has a separate 3.3v regulator to power the Bee socket and is controlled by turning pin 18 on or off. 14 | Mayfly v0.5b has the Bee socket constantly powered, therefore using "-1" is the proper setting for that line of code. 15 | 16 | The EnviroDIY LTE SIM7080 module includes 2 antennas in the package. The small thin one is the cellular antenna, and should be connected to the socket labeled "CELL". The thicker block is the GPS antenna, and should be connected to the "GPS" socket, but only if you intend to use the GPS functionality of the module. ModularSensors does not currently suport GPS functionality, but other libraries such as TinyGPS can work with the SIM7080 module. 17 | 18 | The included cell antenna works best in high-signal-strength areas. For most remote areas and logger deployments, we suggest a larger LTE antenna, like the W3907B0100 19 | from PulseLarsen (Digikey 1837-1003-ND or Mouser 673-W3907B0100) 20 | 21 | _______ 22 | 23 | [//]: # ( @tableofcontents ) 24 | 25 | [//]: # ( @m_footernavigation ) 26 | 27 | [//]: # ( Start GitHub Only ) 28 | 29 | - [DRWI Sites with EnviroDIY LTE Bees](#drwi-sites-with-envirodiy-lte-bees) 30 | - [Unique Features of the DRWI EnviroDIY LTE Example](#unique-features-of-the-drwi-envirodiy-lte-example) 31 | 32 | [//]: # ( End GitHub Only ) 33 | 34 | _______ 35 | 36 | ## Unique Features of the DRWI EnviroDIY LTE Example 37 | 38 | - Specifically for sites within the Delaware River Watershed Initiative. 39 | - Uses a EnviroDIY LTE Bee based on the SIMCom SIM7080G 40 | 41 | [//]: # ( @section example_drwi_ediylte_pio_config PlatformIO Configuration ) 42 | 43 | [//]: # ( @include{lineno} DRWI_SIM7080LTE/platformio.ini ) 44 | 45 | [//]: # ( @section example_drwi_ediylte_code The Complete Code ) 46 | 47 | [//]: # ( @include{lineno} DRWI_SIM7080LTE/DRWI_SIM7080LTE.ino ) 48 | -------------------------------------------------------------------------------- /examples/baro_rho_correction/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Calculating Results based on Measured Values 2 | 3 | This example demonstrates how to work with calculated variables and calculates water depth by correcting the total pressure measured by a MeaSpec MS5803 with the atmospheric pressure measured by a Bosch BME280 environmental sensor and the temperature measured by a Maxim DS18 temperature probe. 4 | 5 | The modem used in this example is a SIM800 based Sodaq GPRSBee r6. 6 | 7 | The sensors used in this example are a Maxim DS18 temperature probe, a Bosch BME280 environmental sensor, and a Measurement Specialties MS5803-14BA pressure sensor. 8 | 9 | _______ 10 | 11 | [//]: # ( @tableofcontents ) 12 | 13 | [//]: # ( @m_footernavigation ) 14 | 15 | [//]: # ( Start GitHub Only ) 16 | 17 | - [Calculating Results based on Measured Values](#calculating-results-based-on-measured-values) 18 | - [Unique Features of the Barometric Correction Example](#unique-features-of-the-barometric-correction-example) 19 | - [To Use this Example](#to-use-this-example) 20 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 21 | - [Set the logger ID](#set-the-logger-id) 22 | - [Set the universally universal identifiers (UUID) for each variable](#set-the-universally-universal-identifiers-uuid-for-each-variable) 23 | - [Upload!](#upload) 24 | 25 | [//]: # ( End GitHub Only ) 26 | 27 | _______ 28 | 29 | ## Unique Features of the Barometric Correction Example 30 | 31 | - All variables are created and named with their parent sensor (as opposed to being created within the variable array). 32 | - There are multiple calculated variables created and used. 33 | 34 | ## To Use this Example 35 | 36 | ### Prepare and set up PlatformIO 37 | 38 | - Register a site and sensors at the Monitor My Watershed/EnviroDIY data portal () 39 | - Create a new PlatformIO project 40 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/baro_rho_correction/platformio.ini) file in the examples/baro_rho_correction folder on GitHub. 41 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 42 | - Without this, the program won't compile. 43 | - Open [baro_rho_correction.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/baro_rho_correction/baro_rho_correction.ino) and save it to your computer. 44 | - After opening the link, you should be able to right click anywhere on the page and select "Save Page As". 45 | - Move it into the src directory of your project. 46 | - Delete main.cpp in that folder. 47 | 48 | ### Set the logger ID 49 | 50 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 51 | 52 | ```cpp 53 | // Logger ID, also becomes the prefix for the name of the data file on SD card 54 | const char *LoggerID = "XXXX"; 55 | ``` 56 | 57 | ### Set the universally universal identifiers (UUID) for each variable 58 | 59 | - Go back to the web page for your site at the Monitor My Watershed/EnviroDIY data portal () 60 | - For each variable, find the dummy UUID (`"12345678-abcd-1234-ef00-1234567890ab"`) and replace it with the real UUID for the variable. 61 | 62 | ### Upload! 63 | 64 | - Test everything at home **before** deploying out in the wild! 65 | 66 | _______ 67 | 68 | [//]: # ( @section example_baro_rho_pio_config PlatformIO Configuration ) 69 | 70 | [//]: # ( @include{lineno} baro_rho_correction/platformio.ini ) 71 | 72 | [//]: # ( @section example_baro_rho_code The Complete Code ) 73 | 74 | [//]: # ( @include{lineno} baro_rho_correction/baro_rho_correction.ino ) 75 | -------------------------------------------------------------------------------- /examples/data_saving/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Minimizing Cellular Data Use 2 | 3 | This is another double logger example, but in this case, both loggers are going at the same interval and the only difference between the loggers is the list of variables. 4 | There are two sets of variables, all coming from Yosemitech sensors. 5 | Because each sensor outputs temperature and we don't want to waste cellular data sending out multiple nearly identical temperature values, we have one logger that logs every possible variable result to the SD card and another logger that sends only unique results to the EnviroDIY data portal. 6 | 7 | The modem used in this example is a SIM800 based Sodaq GPRSBee r6. 8 | 9 | The sensors used in this example are Yosemitech Y504 Dissolved Oxygen Sensor, Yosemitech Y511 Turbidity Sensor with Wiper, Yosemitech Y514 Chlorophyll Sensor, and Yosemitech Y520 Conductivity Sensor. 10 | 11 | _______ 12 | 13 | [//]: # ( @tableofcontents ) 14 | 15 | [//]: # ( @m_footernavigation ) 16 | 17 | [//]: # ( Start GitHub Only ) 18 | 19 | - [Minimizing Cellular Data Use](#minimizing-cellular-data-use) 20 | - [Unique Features of the Data Saving Example](#unique-features-of-the-data-saving-example) 21 | - [To Use this Example](#to-use-this-example) 22 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 23 | - [Set the logger ID](#set-the-logger-id) 24 | - [Set the universally universal identifiers (UUID) for each variable](#set-the-universally-universal-identifiers-uuid-for-each-variable) 25 | - [Upload!](#upload) 26 | 27 | [//]: # ( End GitHub Only ) 28 | 29 | _______ 30 | 31 | ## Unique Features of the Data Saving Example 32 | 33 | - Uses AltSoftSerial to create an additional serial port for RS485 communication. 34 | - All variables are created and named with their parent sensor (as opposed to being created within the variable array). 35 | - Two different variable arrays and loggers are created and used. 36 | - Many of the same variables are used in both arrays. 37 | - Only one of the loggers publishes data. 38 | - The `loop` function is expanded into its components rather than using the `logData` functions. 39 | - This demonstrates *how* to write the loop out, without using the `logData` functions. 40 | - It also shows how to forcibly set serial pins `LOW` at the start and end of the loop in order to prevent power loss through an RS485 adapter. 41 | 42 | ## To Use this Example 43 | 44 | ### Prepare and set up PlatformIO 45 | 46 | - Register a site and sensors at the Monitor My Watershed/EnviroDIY data portal () 47 | - Create a new PlatformIO project 48 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/data_saving/platformio.ini) file in the examples/data_saving folder on GitHub. 49 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 50 | - Without this, the program won't compile. 51 | - Open [data_saving.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/data_saving/data_saving.ino) and save it to your computer. 52 | - After opening the link, you should be able to right click anywhere on the page and select "Save Page As". 53 | - Move it into the src directory of your project. 54 | - Delete main.cpp in that folder. 55 | 56 | ### Set the logger ID 57 | 58 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 59 | 60 | ```cpp 61 | // Logger ID, also becomes the prefix for the name of the data file on SD card 62 | const char *LoggerID = "XXXX"; 63 | ``` 64 | 65 | ### Set the universally universal identifiers (UUID) for each variable 66 | 67 | - Go back to the web page for your site at the Monitor My Watershed/EnviroDIY data portal () 68 | - For each variable, find the dummy UUID (`"12345678-abcd-1234-ef00-1234567890ab"`) and replace it with the real UUID for the variable. 69 | 70 | ### Upload! 71 | 72 | - Test everything at home **before** deploying out in the wild! 73 | 74 | _______ 75 | 76 | [//]: # ( @section example_data_saving_pio_config PlatformIO Configuration ) 77 | 78 | [//]: # ( @include{lineno} data_saving/platformio.ini ) 79 | 80 | [//]: # ( @section example_data_saving_code The Complete Code ) 81 | 82 | [//]: # ( @include{lineno} data_saving/data_saving.ino ) 83 | -------------------------------------------------------------------------------- /examples/double_logger/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Multiple Time Intervals 2 | 3 | This is a more complicated example using two different logger instances to log data at two different intervals, in this case, an AM3215 logging every minute, while checking the battery voltage only every 5 minutes. 4 | This showcases both how to use two different logging instances and how to use some of the functions to set up your own logging loop rather than using the logData() function. 5 | 6 | _______ 7 | 8 | [//]: # ( @tableofcontents ) 9 | 10 | [//]: # ( @m_footernavigation ) 11 | 12 | [//]: # ( Start GitHub Only ) 13 | 14 | - [Multiple Time Intervals](#multiple-time-intervals) 15 | - [Unique Features of the Double Logger Example](#unique-features-of-the-double-logger-example) 16 | - [To Use this Example](#to-use-this-example) 17 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 18 | - [Set the logger ID](#set-the-logger-id) 19 | - [Upload!](#upload) 20 | 21 | [//]: # ( End GitHub Only ) 22 | 23 | _______ 24 | 25 | ## Unique Features of the Double Logger Example 26 | 27 | - Two different variable arrays and loggers are created and used. 28 | - The Variables for the arrays are created within the array. 29 | - There is no variable overlap between the two arrays or loggers. 30 | - The `loop` function is expanded into its components rather than using the `logData` functions. 31 | - This demonstrates *how* to write the loop out, without using the `logData` functions. 32 | - This shows which functions are required for each of the two loggers and which can be used in common. 33 | 34 | ## To Use this Example 35 | 36 | ### Prepare and set up PlatformIO 37 | 38 | - Create a new PlatformIO project 39 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/double_logger/platformio.ini) file in the examples/double_logger folder on GitHub. 40 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 41 | - Without this, the program won't compile. 42 | - Open [double_logger.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/double_logger/double_logger.ino) and save it to your computer. 43 | - After opening the link, you should be able to right click anywhere on the page and select "Save Page As". 44 | - Move it into the src directory of your project. 45 | - Delete main.cpp in that folder. 46 | 47 | ### Set the logger ID 48 | 49 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 50 | 51 | ```cpp 52 | // Logger ID, also becomes the prefix for the name of the data file on SD card 53 | const char *LoggerID = "XXXX"; 54 | ``` 55 | 56 | ### Upload! 57 | 58 | - Test everything at home **before** deploying out in the wild! 59 | 60 | _______ 61 | 62 | [//]: # ( @section example_double_log_pio_config PlatformIO Configuration ) 63 | 64 | [//]: # ( @include{lineno} double_logger/platformio.ini ) 65 | 66 | [//]: # ( @section example_double_log_code The Complete Code ) 67 | 68 | [//]: # ( @include{lineno} double_logger/double_logger.ino ) 69 | -------------------------------------------------------------------------------- /examples/example_dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "action_cache_version": 1, 3 | "dependencies": [ 4 | { 5 | "name": "StreamDebugger", 6 | "owner": "vshymanskyy", 7 | "version": "~1.0.1" 8 | }, 9 | { 10 | "name": "NeoSWSerial", 11 | "version": "https://github.com/SRGDamia1/NeoSWSerial.git" 12 | }, 13 | { 14 | "name": "AltSoftSerial", 15 | "version": "https://github.com/PaulStoffregen/AltSoftSerial.git" 16 | }, 17 | { 18 | "name": "SoftwareWire", 19 | "version": "https://github.com/Testato/SoftwareWire.git#v1.5.1" 20 | }, 21 | { 22 | "name": "SoftwareSerial_ExternalInts", 23 | "version": "https://github.com/EnviroDIY/SoftwareSerial_ExternalInts.git" 24 | }, 25 | { 26 | "name": "SDI-12_ExtInts", 27 | "owner": "envirodiy", 28 | "version": "https://github.com/EnviroDIY/Arduino-SDI-12#ExtInts" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /examples/examples.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * @dir examples 3 | * 4 | * @brief Contains all of the example sketches. There is only one example in each subfolder - a standard for Arduino coding. 5 | */ 6 | /** 7 | * @page page_the_examples 8 | * @m_innerpage{page_examples_basic} 9 | * @page page_examples_basic Basic Functionality 10 | * [Basic Functionality](@ref examples_basic) 11 | * @m_innerpage{example_single_sensor} 12 | * @m_innerpage{example_simple_logging} 13 | * @m_innerpage{example_learn_envirodiy} 14 | */ 15 | 16 | /** 17 | * @page page_the_examples 18 | * @m_innerpage{page_examples_outgoing_data} 19 | * @page page_examples_outgoing_data Publishing Data 20 | * [Publishing Data](@ref examples_publishing) 21 | * @m_innerpage{example_mmw} 22 | * @m_innerpage{example_thingspeak} 23 | */ 24 | 25 | /** 26 | * @page page_the_examples 27 | * @m_innerpage{page_examples_complex} 28 | * @page page_examples_complex Calculations and Complex Logging 29 | * [Calculations and Complex Logging](@ref examples_complex) 30 | * @m_innerpage{example_baro_rho} 31 | * @m_innerpage{example_double_log} 32 | * @m_innerpage{example_data_saving} 33 | */ 34 | 35 | /** 36 | * @page page_the_examples 37 | * @m_innerpage{page_examples_drwi} 38 | * @page page_examples_drwi DRWI Citizen Science 39 | * [DRWI Citizen Science](@ref examples_drwi) 40 | * @m_innerpage{example_drwi_mayfly1} 41 | * @m_innerpage{example_drwi_mayfly1_wifi} 42 | * @m_innerpage{example_drwi_ediylte} 43 | * @m_innerpage{example_drwi_digilte} 44 | * @m_innerpage{example_drwi_2g} 45 | * @m_innerpage{example_drwi_no_cell} 46 | */ 47 | 48 | /** 49 | * @page page_the_examples 50 | * @m_innerpage{page_examples_menu} 51 | * @page page_examples_menu Everything at Once - a la carte 52 | * [Everything at Once - a la carte](@ref examples_everything) 53 | * @m_innerpage{example_menu} 54 | */ 55 | -------------------------------------------------------------------------------- /examples/logging_to_MMW/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Sending Data to Monitor My Watershed/EnviroDIY 2 | 3 | This sketch reduces menu_a_la_carte.ino to provide an example of how to log to from two sensors, the BME280 and DS18. To complete the set up for logging to the web portal, the UUIDs for the site and each variable would need to be added to the sketch. 4 | 5 | The settings for other data portals were removed from the example. 6 | 7 | The modem settings were left unchanged because the sketch will test successfully without modem connection (wait patiently, it takes a few minutes). 8 | 9 | This is the example you should use to deploy a logger with a modem to stream live data to the Monitor My Watershed data portal. 10 | 11 | _______ 12 | 13 | [//]: # ( @tableofcontents ) 14 | 15 | [//]: # ( @m_footernavigation ) 16 | 17 | [//]: # ( Start GitHub Only ) 18 | 19 | - [Sending Data to Monitor My Watershed/EnviroDIY](#sending-data-to-monitor-my-watershedenvirodiy) 20 | - [Unique Features of the Monitor My Watershed Example](#unique-features-of-the-monitor-my-watershed-example) 21 | - [To Use this Example](#to-use-this-example) 22 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 23 | - [Set the logger ID](#set-the-logger-id) 24 | - [Set the universally universal identifiers (UUID) for each variable](#set-the-universally-universal-identifiers-uuid-for-each-variable) 25 | - [Upload!](#upload) 26 | 27 | [//]: # ( End GitHub Only ) 28 | 29 | _______ 30 | 31 | ## Unique Features of the Monitor My Watershed Example 32 | 33 | - A single logger publishes data to the Monitor My Watershed data portal. 34 | - Uses a cellular Digi XBee or XBee3 35 | 36 | ## To Use this Example 37 | 38 | ### Prepare and set up PlatformIO 39 | 40 | - Register a site and sensors at the Monitor My Watershed/EnviroDIY data portal () 41 | - Create a new PlatformIO project 42 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/logging_to_MMW/platformio.ini) file in the examples/logging_to_MMW folder on GitHub. 43 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 44 | - Without this, the program won't compile. 45 | - Open [logging_to_MMW.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/logging_to_MMW/logging_to_MMW.ino) and save it to your computer. 46 | - After opening the link, you should be able to right click anywhere on the page and select "Save Page As". 47 | - Move it into the src directory of your project. 48 | - Delete main.cpp in that folder. 49 | 50 | ### Set the logger ID 51 | 52 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 53 | 54 | ```cpp 55 | // Logger ID, also becomes the prefix for the name of the data file on SD card 56 | const char *LoggerID = "XXXX"; 57 | ``` 58 | 59 | ### Set the universally universal identifiers (UUID) for each variable 60 | 61 | - Go back to the web page for your site at the Monitor My Watershed/EnviroDIY data portal () 62 | - For each variable, find the dummy UUID (`"12345678-abcd-1234-ef00-1234567890ab"`) and replace it with the real UUID for the variable. 63 | 64 | ### Upload! 65 | 66 | - Test everything at home **before** deploying out in the wild! 67 | 68 | _______ 69 | 70 | [//]: # ( @section example_mmw_pio_config PlatformIO Configuration ) 71 | 72 | [//]: # ( @include{lineno} logging_to_MMW/platformio.ini ) 73 | 74 | [//]: # ( @section example_mmw_code The Complete Code ) 75 | 76 | [//]: # ( @include{lineno} logging_to_MMW/logging_to_MMW.ino ) 77 | -------------------------------------------------------------------------------- /examples/logging_to_ThingSpeak/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Sending data to ThingSpeak 2 | 3 | This shows the use of a "ThingSpeak logger" object. 4 | Data is sent to [ThingSpeak](https://thingspeak.com) using MQTT. 5 | 6 | _______ 7 | 8 | [//]: # ( @tableofcontents ) 9 | 10 | [//]: # ( @m_footernavigation ) 11 | 12 | [//]: # ( Start GitHub Only ) 13 | 14 | - [Sending data to ThingSpeak](#sending-data-to-thingspeak) 15 | - [Unique Features of the ThingSpeak Example](#unique-features-of-the-thingspeak-example) 16 | - [To Use this Example](#to-use-this-example) 17 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 18 | - [Modify the Example](#modify-the-example) 19 | - [Upload!](#upload) 20 | 21 | [//]: # ( End GitHub Only ) 22 | 23 | _______ 24 | 25 | ## Unique Features of the ThingSpeak Example 26 | 27 | - A single logger publishes data to ThingSpeak. 28 | - Uses an Espressif ESP8266 to publish data. 29 | 30 | ## To Use this Example 31 | 32 | ### Prepare and set up PlatformIO 33 | 34 | - Create a channel on ThingSpeak with fields to receive your data. 35 | - Create a new PlatformIO project 36 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/logging_to_ThingSpeak/platformio.ini) file in the examples/logging_to_ThingSpeak folder on GitHub. 37 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 38 | - Without this, the program won't compile. 39 | - Open [logging_to_ThingSpeak.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/logging_to_ThingSpeak/logging_to_ThingSpeak.ino) and save it to your computer. 40 | - After opening the link, you should be able to right click anywhere on the page and select "Save Page As". 41 | - Move it into the src directory of your project. 42 | - Delete main.cpp in that folder. 43 | 44 | ### Modify the Example 45 | 46 | - Modify logging_to_ThingSpeak.ino to have the modem, sensor, and variable objects that you are interested in. 47 | - This example is written for an _ESP8266 (wifi)_ modem. 48 | Change this to whatever modem you are using. 49 | Pastable chunks of code for each modem are available in the individual sensor documentation or in the menu a la carte example. 50 | - Don't forget to put in your wifi username/password or cellular APN! 51 | - This example is written for a Campbell OBS3+ and a Meter Hydros 21. 52 | Remove those sensors if you are not using them and add code for all of your sensors. 53 | See the pages for the individual sensors in the [documentation](https://envirodiy.github.io/ModularSensors/index.html) for code snippets/examples. 54 | - Remember, no more than **8** variables/fields can be sent to a single ThingSpeak channel. 55 | If you want to send data to multiple channels, you must create individual logger objects with unique publishers attached for each channel you want to send to. 56 | - **Make sure the pin numbers and serial ports selected in your code match with how things are physically attached to your board!** 57 | - Order the variables in your variable array in the same order as your fields are on ThingSpeak. 58 | - This order is **crucial**. 59 | The results from the variables in the VariableArray will be sent to ThingSpeak in the order they are in the array; that is, the first variable in the array will be sent as Field1, the second as Field2, etc. 60 | - Any UUID's or custom variable codes are ignored for ThingSpeak. 61 | They will only appear in the header of your file on the SD card. 62 | - Find this information for your ThingSpeak account and channel and put it into logging_to_ThingSpeak.ino: 63 | 64 | ```cpp 65 | const char *thingSpeakMQTTKey = "XXXXXXXXXXXXXXXX"; // Your MQTT API Key from Account > MyProfile. 66 | const char *thingSpeakChannelID = "######"; // The numeric channel id for your channel 67 | const char *thingSpeakChannelKey = "XXXXXXXXXXXXXXXX"; // The Write API Key for your channel 68 | ``` 69 | 70 | ### Upload! 71 | 72 | - Test everything at home **before** deploying out in the wild! 73 | 74 | _______ 75 | 76 | [//]: # ( @section example_thingspeak_pio_config PlatformIO Configuration ) 77 | 78 | [//]: # ( @include{lineno} logging_to_ThingSpeak/platformio.ini ) 79 | 80 | [//]: # ( @section example_thingspeak_code The Complete Code ) 81 | 82 | [//]: # ( @include{lineno} logging_to_ThingSpeak/logging_to_ThingSpeak.ino ) 83 | -------------------------------------------------------------------------------- /examples/simple_logging/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Simple Logging 2 | 3 | This shows the simplest use of a "logger" object. 4 | That is, creating an array of variable objects and then creating a logger object that utilizes those variables to update all of the variable results together and save the data to a SD card. 5 | The processor then goes to sleep between readings. 6 | 7 | This is the example you should use to deploy a logger somewhere where you don't want or have access to a way of streaming live data and you won't want to upload data to the Monitor My Watershed data portal. 8 | 9 | _______ 10 | 11 | [//]: # ( @tableofcontents ) 12 | 13 | [//]: # ( @m_footernavigation ) 14 | 15 | [//]: # ( Start GitHub Only ) 16 | 17 | - [Simple Logging](#simple-logging) 18 | - [Unique Features of the Simple Logging Example](#unique-features-of-the-simple-logging-example) 19 | - [To Use this Example](#to-use-this-example) 20 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 21 | - [Set the logger ID](#set-the-logger-id) 22 | - [Upload!](#upload) 23 | 24 | [//]: # ( End GitHub Only ) 25 | 26 | ## Unique Features of the Simple Logging Example 27 | 28 | - Only logs data to an SD card. 29 | 30 | ## To Use this Example 31 | 32 | ### Prepare and set up PlatformIO 33 | 34 | - Create a new PlatformIO project 35 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/simple_logging/platformio.ini) file in the examples/simple_logging folder on GitHub. 36 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 37 | - Without this, the program won't compile. 38 | - Open [simple_logging.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/simple_logging/simple_logging.ino) and save it to your computer. Put it into the src directory of your project. 39 | - Delete main.cpp in that folder. 40 | 41 | ### Set the logger ID 42 | 43 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 44 | 45 | ```cpp 46 | // Logger ID, also becomes the prefix for the name of the data file on SD card 47 | const char *LoggerID = "XXXX"; 48 | ``` 49 | 50 | ### Upload! 51 | 52 | - Test everything at home **before** deploying out in the wild! 53 | 54 | _______ 55 | 56 | [//]: # ( @section example_simple_logging_pio_config PlatformIO Configuration ) 57 | 58 | [//]: # ( @include{lineno} simple_logging/platformio.ini ) 59 | 60 | [//]: # ( @section example_simple_logging_code The Complete Code ) 61 | 62 | [//]: # ( @include{lineno} simple_logging/simple_logging.ino ) 63 | -------------------------------------------------------------------------------- /examples/simple_logging_LearnEnviroDIY/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Learn EnviroDIY Course 2 | 3 | This shows the simplest use of a "logger" object. 4 | That is, creating an array of variable objects and then creating a logger object that utilizes those variables to update all of the variable results together and save the data to a SD card. 5 | The processor then goes to sleep between readings. 6 | This example calls on two of the sensors available in this library. 7 | The example may be run exactly as written. 8 | 9 | This is the example you should use to deploy a logger somewhere where you don't want or have access to a way of streaming live data and you won't want to upload data to the Monitor My Watershed data portal. 10 | 11 | _______ 12 | 13 | [//]: # ( @tableofcontents ) 14 | 15 | [//]: # ( @m_footernavigation ) 16 | 17 | [//]: # ( Start GitHub Only ) 18 | 19 | - [Learn EnviroDIY Course](#learn-envirodiy-course) 20 | - [Unique Features of the Learn EnviroDIY Example](#unique-features-of-the-learn-envirodiy-example) 21 | - [To Use this Example](#to-use-this-example) 22 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 23 | - [Set the logger ID](#set-the-logger-id) 24 | - [Upload!](#upload) 25 | 26 | [//]: # ( End GitHub Only ) 27 | 28 | _______ 29 | 30 | ## Unique Features of the Learn EnviroDIY Example 31 | 32 | - Only logs data to an SD card. 33 | - Uses a few more sensors than the other simple logging example 34 | 35 | ## To Use this Example 36 | 37 | ### Prepare and set up PlatformIO 38 | 39 | - Create a new PlatformIO project 40 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/simple_logging_LearnEnviroDIY/platformio.ini) file in the examples/simple_logging_LearnEnviroDIY folder on GitHub. 41 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 42 | - Without this, the program won't compile. 43 | - Open [simple_logging_LearnEnviroDIY.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/simple_logging_LearnEnviroDIY/simple_logging_LearnEnviroDIY.ino) and save it to your computer. Put it into the src directory of your project. 44 | - Delete main.cpp in that folder. 45 | 46 | ### Set the logger ID 47 | 48 | - Change the "XXXX" in this section of code to the loggerID assigned by Stroud: 49 | 50 | ```cpp 51 | // Logger ID, also becomes the prefix for the name of the data file on SD card 52 | const char *LoggerID = "XXXX"; 53 | ``` 54 | 55 | ### Upload! 56 | 57 | - Test everything at home **before** deploying out in the wild! 58 | 59 | _______ 60 | 61 | [//]: # ( @section example_learn_envirodiy_pio_config PlatformIO Configuration ) 62 | 63 | [//]: # ( @include{lineno} simple_logging_LearnEnviroDIY/platformio.ini ) 64 | 65 | [//]: # ( @section example_learn_envirodiy_code The Complete Code ) 66 | 67 | [//]: # ( @include{lineno} simple_logging_LearnEnviroDIY/simple_logging_LearnEnviroDIY.ino ) 68 | -------------------------------------------------------------------------------- /examples/single_sensor/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Using a Single Sensor 2 | 3 | This somewhat trivial example show making use of the unified set of commands to print data from a MaxBotix ultrasonic range finder to the serial port. 4 | It also shows creating a calculated variable which is the water depth. 5 | 6 | _______ 7 | 8 | [//]: # ( @tableofcontents ) 9 | 10 | [//]: # ( @m_footernavigation ) 11 | 12 | [//]: # ( Start GitHub Only ) 13 | 14 | - [Using a Single Sensor](#using-a-single-sensor) 15 | - [Unique Features of the Single Sensor Example](#unique-features-of-the-single-sensor-example) 16 | - [To Use this Example](#to-use-this-example) 17 | - [Prepare and set up PlatformIO](#prepare-and-set-up-platformio) 18 | - [Upload!](#upload) 19 | 20 | [//]: # ( End GitHub Only ) 21 | 22 | _______ 23 | 24 | ## Unique Features of the Single Sensor Example 25 | 26 | - Only communicates with and collects data from a single sensor. 27 | - Does not make use of any VariableArray or logging features. 28 | 29 | ## To Use this Example 30 | 31 | ### Prepare and set up PlatformIO 32 | 33 | - Create a new PlatformIO project 34 | - Replace the contents of the platformio.ini for your new project with the [platformio.ini](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/single_sensor/platformio.ini) file in the examples/single_sensor folder on GitHub. 35 | - It is important that your PlatformIO configuration has the lib_ldf_mode and build flags set as they are in the example. 36 | - Without this, the program won't compile. 37 | - Open [single_sensor.ino](https://raw.githubusercontent.com/EnviroDIY/ModularSensors/master/examples/single_sensor/single_sensor.ino) and save it to your computer. Put it into the src directory of your project. 38 | - Delete main.cpp in that folder. 39 | 40 | ### Upload! 41 | 42 | - Upload and see what happens 43 | 44 | _______ 45 | 46 | [//]: # ( @section example_single_sensor_pio_config PlatformIO Configuration ) 47 | 48 | [//]: # ( @include{lineno} single_sensor/platformio.ini ) 49 | 50 | [//]: # ( @section example_single_sensor_code The Complete Code ) 51 | 52 | [//]: # ( @include{lineno} single_sensor/single_sensor.ino ) 53 | -------------------------------------------------------------------------------- /extras/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Extra Helper Sketches 2 | 3 | A collection of helper sketches to test individual sensor timing and configurations. 4 | 5 | - powerOn.ino 6 | - Testing sketch that simply turns on power to the sensors on the Mayfly. 7 | - oneWireSearch.ino 8 | - Testing sketch to scan for 1-Wire devices + code snippet generator 9 | - i2c_scanner.ino 10 | - Testing sketch to scan for attached I2C devices 11 | - i2c_warmUp.ino 12 | - Testing sketch to see how long an attached I2C device takes to begin to respond to commands. 13 | - interrupt_counter.ino 14 | - Testing sketch counting pin change interrupts. 15 | - resetBee.ino 16 | - Testing sketch to fully reset an XBee 17 | - LTExBee_FirstConnection.ino 18 | - Testing sketch to set up a never-previously-connected LTE XBee running in standard (transparent) mode. 19 | - LTExBee_FirstConnectionBypass.ino 20 | - Testing sketch to set up a never-previously-connected LTE XBee running in bypass mode. 21 | - mega_serial_spy.ino 22 | - Testing sketch to run on an Arduino Mega to print all output from connected serial ports to the terminal. 23 | - sdi12_address_change.ino 24 | - Copy of SDI-12 Example B: Changing the Address of your SDI-12 sensor 25 | - Stream_Debug.ino 26 | - Testing sketch to run StreamDebugger to copy text from one serial output to another. 27 | -------------------------------------------------------------------------------- /extras/Stream_Debug/Stream_Debug.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} Stream_Debug.ino 3 | * @brief Testing sketch to run StreamDebugger to copy text from one serial 4 | * output to another. 5 | * 6 | * @m_examplenavigation{page_extra_helper_sketches,} 7 | * ======================================================================= */ 8 | 9 | #include 10 | #include 11 | StreamDebugger StreamDbg(Serial1, Serial); 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | delay(50); 16 | 17 | Serial1.begin(9600); 18 | delay(50); 19 | 20 | StreamDbg.print("AT"); 21 | } 22 | 23 | void loop() { 24 | // Start direct-access from Serial to Serial1 25 | StreamDbg.directAccess(); 26 | } 27 | -------------------------------------------------------------------------------- /extras/extras.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * @dir extras 3 | * 4 | * @brief Contains extra sketches for testing and configuring new sensors. 5 | */ 6 | -------------------------------------------------------------------------------- /extras/i2c_scanner/i2c_scanner.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} i2c_scanner.ino 3 | * @brief Testing sketch to scan for attached I2C devices 4 | * 5 | * Version 1 6 | * This program (or code that looks like it) 7 | * can be found in many places. 8 | * For example on the Arduino.cc forum. 9 | * The original author is not know. 10 | * Version 2, Juni 2012, Using Arduino 1.0.1 11 | * Adapted to be as simple as possible by Arduino.cc user Krodal 12 | * Version 3, Feb 26 2013 13 | * V3 by louarnold 14 | * Version 4, March 3, 2013, Using Arduino 1.0.3 15 | * by Arduino.cc user Krodal. 16 | * Changes by louarnold removed. 17 | * Scanning addresses changed from 0...127 to 1...119, 18 | * according to the i2c scanner by Nick Gammon 19 | * http://www.gammon.com.au/forum/?id=10896 20 | * Version 5, March 28, 2013 21 | * As version 4, but address scans now to 127. 22 | * A sensor seems to use address 120. 23 | * Version 6, November 27, 2015. 24 | * Added waiting for the Leonardo serial communication. 25 | * 26 | * 27 | * This sketch tests the standard 7-bit addresses. 28 | * Devices with higher bit address might not be seen properly. 29 | * 30 | * @m_examplenavigation{page_extra_helper_sketches,} 31 | * ======================================================================= */ 32 | 33 | #include 34 | #include 35 | #include // Testato's Software I2C 36 | 37 | 38 | const int8_t softwareSDA = 5; // data in pin 39 | const int8_t softwareSCL = 4; // data out pin 40 | SoftwareWire softWire(5, 4); 41 | 42 | template 43 | THEWIRE createWire(int8_t sda = -1, int8_t scl = -1) { 44 | return THEWIRE(sda, scl); 45 | } 46 | template <> 47 | TwoWire createWire(int8_t sda, int8_t scl) { 48 | return Wire; 49 | } 50 | 51 | 52 | template 53 | void startWire(THEWIRE i2c) { 54 | i2c.begin(); 55 | // i2c.printStatus(Serial); 56 | } 57 | 58 | 59 | // void scan(int8_t sda = -1, int8_t scl = -1) 60 | // { 61 | // THEWIRE i2c = createWire(sda, scl); 62 | // i2c.begin(); 63 | template 64 | void scan(THEWIRE i2c) { 65 | byte error, address; 66 | int nDevices; 67 | 68 | nDevices = 0; 69 | for (address = 1; address < 127; address++) { 70 | // The i2c_scanner uses the return value of 71 | // the Write.endTransmisstion to see if 72 | // a device did acknowledge to the address. 73 | i2c.beginTransmission(address); 74 | error = i2c.endTransmission(); 75 | 76 | if (error == 0) { 77 | Serial.print(" I2C device found at address 0x"); 78 | if (address < 16) Serial.print("0"); 79 | Serial.print(address, HEX); 80 | Serial.println(" !"); 81 | 82 | nDevices++; 83 | } else if (error == 4) { 84 | Serial.print(" Unknown error at address 0x"); 85 | if (address < 16) Serial.print("0"); 86 | Serial.println(address, HEX); 87 | } 88 | } 89 | if (nDevices == 0) Serial.println("No I2C devices found"); 90 | } 91 | 92 | 93 | void setup() { 94 | pinMode(22, OUTPUT); 95 | 96 | Serial.begin(115200); 97 | while (!Serial) 98 | ; // Leonardo: wait for serial monitor 99 | Serial.println("\nI2C Scanner"); 100 | digitalWrite(22, HIGH); 101 | } 102 | 103 | 104 | void loop() { 105 | Serial.println("Hardware I2C Objects:"); 106 | scan(Wire); 107 | Serial.print("Software I2C Objects on SDA=:"); 108 | Serial.print(softwareSDA); 109 | Serial.print(", SCL="); 110 | Serial.print(softwareSCL); 111 | Serial.println(":"); 112 | scan(softWire); 113 | Serial.println(); 114 | 115 | delay(5000); // wait 5 seconds for next scan 116 | } 117 | -------------------------------------------------------------------------------- /extras/i2c_warmUp/i2c_warmUp.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} i2c_warmUp.ino 3 | * @brief Testing sketch to see how long an attached I2C device takes to 4 | * begin to respond to commands. 5 | * 6 | * @m_examplenavigation{page_extra_helper_sketches,} 7 | * ======================================================================= */ 8 | 9 | #include 10 | #include 11 | 12 | int address = 0x66; 13 | 14 | uint32_t start; 15 | bool firstSuccess = true; 16 | bool firstE1 = true; 17 | bool firstE2 = true; 18 | bool firstE3 = true; 19 | bool firstE4 = true; 20 | 21 | int i2cStatus = 4; 22 | const char commands[4] = "iri"; 23 | uint8_t index = 0; 24 | 25 | void printTime() { 26 | Serial.print("I2C device replied at address 0x"); 27 | if (address < 16) Serial.print("0"); 28 | Serial.print(address, HEX); 29 | Serial.print(" after "); 30 | Serial.print(millis() - start); 31 | Serial.print(" ms, code: "); 32 | Serial.println(i2cStatus); 33 | } 34 | 35 | 36 | void setup() { 37 | Wire.begin(); 38 | 39 | Serial.begin(115200); 40 | while (!Serial) 41 | ; 42 | Serial.println("I2C Warm Up Timing Test"); 43 | } 44 | 45 | 46 | void loop() { 47 | // Make sure we start un-powered 48 | Serial.print("Wait"); 49 | pinMode(22, OUTPUT); 50 | digitalWrite(22, LOW); 51 | for (uint32_t dstart = millis(); millis() - dstart < 5000L;) { 52 | Serial.print("."); 53 | delay(250); 54 | } 55 | Serial.println("."); 56 | start = millis(); 57 | 58 | // Serial.print("Attempting to write: "); 59 | // Serial.println(commands[index]); 60 | 61 | digitalWrite(22, HIGH); 62 | 63 | bool gotResult = false; 64 | while (!gotResult) { 65 | Wire.beginTransmission(address); 66 | Wire.write(commands[index]); 67 | i2cStatus = Wire.endTransmission(); 68 | 69 | switch (i2cStatus) { 70 | case 0: 71 | printTime(); 72 | Serial.print(commands[index]); 73 | Serial.println(" successfully written"); 74 | start = millis(); 75 | while (true) { 76 | Wire.requestFrom(address, 40, true); 77 | uint8_t code = Wire.read(); 78 | if (code == 1) { 79 | Serial.print("Result available after "); 80 | Serial.print(millis() - start); 81 | Serial.print(" ms: "); 82 | Serial.println(Wire.readStringUntil('\0')); 83 | gotResult = true; 84 | break; 85 | } 86 | } 87 | break; 88 | case 1: 89 | if (firstE1) { 90 | printTime(); 91 | Serial.println("Data is too long for transmit buffer."); 92 | firstE1 = false; 93 | } 94 | break; 95 | case 2: 96 | if (firstE2) { 97 | printTime(); 98 | Serial.println("Received NACK on transmit of address"); 99 | firstE2 = false; 100 | } 101 | break; 102 | case 3: 103 | if (firstE3) { 104 | printTime(); 105 | Serial.println(" Received NACK on transmit of data"); 106 | firstE3 = false; 107 | } 108 | break; 109 | case 4: 110 | default: 111 | if (firstE4) { 112 | printTime(); 113 | Serial.println("Unknown error occurred"); 114 | firstE4 = false; 115 | } 116 | break; 117 | } 118 | // Serial.println("resending"); 119 | } 120 | 121 | Serial.print("Moving to next character - "); 122 | index++; // go to next character 123 | if (index == 3) index = 0; // reset 124 | } 125 | -------------------------------------------------------------------------------- /extras/interrupt_counter/interrupt_counter.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} interrupt_counter.ino 3 | * @brief Testing sketch counting pin change interrupts. 4 | * 5 | * @m_examplenavigation{page_extra_helper_sketches,} 6 | * ======================================================================= */ 7 | 8 | #include 9 | 10 | #define EI_ARDUINO_INTERRUPTED_PIN 11 | #include 12 | 13 | const uint8_t firstInterruptPin = 2; 14 | const uint8_t lastInterruptPin = 31; 15 | int interrupCounts[lastInterruptPin - firstInterruptPin + 1]; 16 | 17 | char buffer[12]; 18 | void pinInterrupt() { 19 | interrupCounts[arduinoInterruptedPin - firstInterruptPin] = 20 | interrupCounts[arduinoInterruptedPin - firstInterruptPin] + 1; 21 | Serial.print(arduinoInterruptedPin); 22 | Serial.print("-->"); 23 | Serial.println(arduinoPinState); 24 | } 25 | 26 | 27 | void setup() { 28 | Serial.begin(115200); 29 | Serial.print("Time, "); 30 | for (int i = 0; i < lastInterruptPin - firstInterruptPin; i++) { 31 | sprintf(buffer, "%3d", i + firstInterruptPin); 32 | Serial.print(buffer); 33 | Serial.print(","); 34 | } 35 | sprintf(buffer, "%3d", lastInterruptPin); 36 | Serial.println(buffer); 37 | for (int i = 0; i <= lastInterruptPin - firstInterruptPin; i++) { 38 | pinMode(i + firstInterruptPin, INPUT); 39 | enableInterrupt(i + firstInterruptPin, pinInterrupt, CHANGE); 40 | interrupCounts[i] = 0; 41 | } 42 | } 43 | 44 | void loop() { 45 | sprintf(buffer, "%8u", millis()); 46 | Serial.print(buffer); 47 | Serial.print(","); 48 | for (int i = 0; i < lastInterruptPin - firstInterruptPin; i++) { 49 | sprintf(buffer, "%3d", interrupCounts[i]); 50 | Serial.print(buffer); 51 | Serial.print(","); 52 | } 53 | sprintf(buffer, "%3d", 54 | interrupCounts[lastInterruptPin - firstInterruptPin]); 55 | Serial.println(buffer); 56 | Serial.print("Pin 21 is "); 57 | Serial.println(digitalRead(21)); 58 | delay(1000); 59 | } 60 | -------------------------------------------------------------------------------- /extras/mega_serial_spy/mega_serial_spy.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} mega_serial_spy.ino 3 | * @brief Testing sketch to run on an Arduino Mega to print all output from 4 | * connected serial ports to the terminal. 5 | * 6 | * @m_examplenavigation{page_extra_helper_sketches,} 7 | * ======================================================================= */ 8 | 9 | #include 10 | 11 | void changeBauds(void) { 12 | Serial.read(); 13 | for (uint8_t i = 1; i < 4; i++) { 14 | Serial.print("Select a baud rate for Serial"); 15 | Serial.print(i); 16 | Serial.println(" to monitor at:"); 17 | Serial.println(""); 18 | Serial.println("[1] - 9600"); 19 | Serial.println("[2] - 57600"); 20 | Serial.println("[3] - 115200"); 21 | Serial.println("[4] - 74880"); 22 | Serial.println(""); 23 | Serial.println( 24 | "Enter you selection in the Serial Monitor and press "); 25 | Serial.println(""); 26 | 27 | String user_input = ""; 28 | int selection = 0; 29 | 30 | // Wait for user feedback, then parse feedback one byte at a time 31 | while ((Serial.peek() != 255) && !selection) { 32 | char incoming = Serial.read(); 33 | if (isDigit(incoming)) { 34 | // Append the current digit to the string placeholder 35 | user_input += static_cast(incoming); 36 | } 37 | // Parse the string on new-line 38 | if (incoming == '\n') { selection = user_input.toInt(); } 39 | delay(2); 40 | } 41 | 42 | uint32_t baud = 9600; 43 | 44 | if (selection) { 45 | switch (selection) { 46 | case 1: 47 | default: baud = 9600; break; 48 | case 2: baud = 57600; break; 49 | case 3: baud = 115200; break; 50 | case 4: baud = 74880; break; 51 | } 52 | } 53 | 54 | Serial.print("Starting Serial"); 55 | Serial.print(i); 56 | Serial.print(" at "); 57 | Serial.print(baud); 58 | Serial.println(" baud."); 59 | 60 | switch (i) { 61 | case 1: Serial1.begin(baud); break; 62 | case 2: Serial2.begin(baud); break; 63 | case 3: Serial3.begin(baud); break; 64 | } 65 | } 66 | } 67 | 68 | 69 | void setup() { 70 | Serial.begin(115200); 71 | 72 | // Wait for the Serial Monitor to open 73 | while (!Serial) { 74 | // Delay required to avoid RTOS task switching problems 75 | delay(1); 76 | } 77 | 78 | delay(500); // Short delay for cosmetic reasons 79 | Serial.println(""); 80 | Serial.println("Serial port Spy\r\n"); 81 | Serial.println("-----------------------------------------------------------" 82 | "--------------------"); 83 | changeBauds(); 84 | } 85 | 86 | void loop() { 87 | // From HW UART1 to USB 88 | while (Serial1.available()) Serial.write(Serial1.read()); 89 | // From HW UART2 to USB 90 | while (Serial2.available()) Serial.write(Serial2.read()); 91 | // From HW UART4 to USB 92 | while (Serial3.available()) Serial.write(Serial3.read()); 93 | 94 | if (Serial.available()) changeBauds(); 95 | } 96 | -------------------------------------------------------------------------------- /extras/oneWireSearch/oneWireSearch.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} oneWireSearch.ino 3 | * @author Rob Tillaart 4 | * VERSION: 0.1.02 5 | * @brief scan for 1-Wire devices + code snippet generator 6 | * DATE: 2015-june-30 7 | * URL: http://forum.arduino.cc/index.php?topic=333923 8 | * 9 | * inspired by 10 | * http://www.hacktronics.com/Tutorials/arduino-1-wire-address-finder.html 11 | * 12 | * Released to the public domain 13 | * 14 | * 0.1.00 initial version 15 | * 0.1.01 first published version 16 | * 0.1.02 small output changes 17 | * 18 | * @m_examplenavigation{page_extra_helper_sketches,} 19 | * ======================================================================= */ 20 | 21 | #include 22 | #include 23 | 24 | uint8_t findDevices(int pin) { 25 | OneWire ow(pin); 26 | 27 | uint8_t address[8]; 28 | uint8_t count = 0; 29 | 30 | 31 | if (ow.search(address)) { 32 | Serial.print("\nuint8_t pin"); 33 | Serial.print(pin, DEC); 34 | Serial.println("[][8] = {"); 35 | do { 36 | count++; 37 | Serial.print(" {"); 38 | for (uint8_t i = 0; i < 8; i++) { 39 | Serial.print("0x"); 40 | if (address[i] < 0x10) Serial.print("0"); 41 | Serial.print(address[i], HEX); 42 | if (i < 7) Serial.print(", "); 43 | } 44 | Serial.println("},"); 45 | } while (ow.search(address)); 46 | 47 | Serial.println("};"); 48 | Serial.print("// nr devices found: "); 49 | Serial.println(count); 50 | } 51 | 52 | return count; 53 | } 54 | 55 | void setup() { 56 | Serial.begin(9600); 57 | Serial.println( 58 | "//\n// Start oneWireSearch.ino \n// -----------------------"); 59 | 60 | // Power the sensors; 61 | pinMode(22, OUTPUT); 62 | digitalWrite(22, HIGH); 63 | delay(2000); 64 | 65 | for (uint8_t pin = 2; pin < 37; pin++) { findDevices(pin); } 66 | Serial.println("\n//\n// End oneWireSearch.ino \n// ---------------------"); 67 | 68 | // Cut power 69 | digitalWrite(22, LOW); 70 | } 71 | 72 | void loop() {} 73 | -------------------------------------------------------------------------------- /extras/powerOn/powerOn.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} powerOn.ino 3 | * @brief Testing sketch that simply turns on power to the sensors on the 4 | * Mayfly. 5 | * 6 | * @m_examplenavigation{page_extra_helper_sketches,} 7 | * ======================================================================= */ 8 | 9 | #include 10 | 11 | int8_t powerPin = 22; 12 | 13 | void setup() { 14 | pinMode(powerPin, OUTPUT); 15 | digitalWrite(powerPin, HIGH); 16 | pinMode(A5, OUTPUT); 17 | digitalWrite(A5, HIGH); 18 | pinMode(10, OUTPUT); 19 | digitalWrite(10, HIGH); 20 | } 21 | 22 | void loop() {} 23 | -------------------------------------------------------------------------------- /extras/resetBee/resetBee.ino: -------------------------------------------------------------------------------- 1 | /** ========================================================================= 2 | * @example{lineno} resetBee.ino 3 | * @brief Testing sketch to fully reset an XBee 4 | * 5 | * @m_examplenavigation{page_extra_helper_sketches,} 6 | * ======================================================================= */ 7 | 8 | #include 9 | 10 | void setup() { 11 | Serial.begin(9600); 12 | Serial1.begin(9600); 13 | pinMode(23, OUTPUT); 14 | digitalWrite(23, LOW); 15 | delay(1000); 16 | Serial1.print("+++"); 17 | delay(1000); 18 | Serial1.print("ATRE\r"); 19 | Serial1.print("ATWR\r"); 20 | Serial1.print("ATAC\r"); 21 | Serial1.print("ATGT\r"); 22 | Serial1.print("ATFR\r"); 23 | Serial1.print("ATCN\r"); 24 | } 25 | 26 | void loop() { 27 | while (Serial.available()) { Serial1.write(Serial.read()); } 28 | while (Serial1.available()) { Serial.write(Serial1.read()); } 29 | } 30 | -------------------------------------------------------------------------------- /extras/sdi12_address_change/sdi12_address_change.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @example{lineno} sdi12_address_change.ino 3 | * @copyright Stroud Water Research Center 4 | * This example is published under the BSD-3 license. 5 | * @author Kevin M.Smith 6 | * @date August 2013 7 | * 8 | * @brief Copy of SDI-12 Example B: Changing the Address of your SDI-12 Sensor 9 | * 10 | * The SDI-12 specification is available at: http://www.sdi-12.org/ 11 | * The library is available at: https://github.com/EnviroDIY/Arduino-SDI-12 12 | * 13 | * @m_examplenavigation{page_extra_helper_sketches,} 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | 20 | #define SERIAL_BAUD 115200 // The baud rate for the output serial port 21 | #define DATA_PIN 7 // The pin of the SDI-12 data bus 22 | #define POWER_PIN 22 // The // Sensor power pin (or -1 if not switching power) 23 | 24 | // Define the SDI-12 bus 25 | SDI12 mySDI12(DATA_PIN); 26 | 27 | String myCommand = ""; // empty to start 28 | char oldAddress = '!'; // invalid address as placeholder 29 | 30 | 31 | // this checks for activity at a particular address 32 | // expects a char, '0'-'9', 'a'-'z', or 'A'-'Z' 33 | boolean 34 | checkActive(byte i) { // this checks for activity at a particular address 35 | Serial.print("Checking address "); 36 | Serial.print(static_cast(i)); 37 | Serial.print("..."); 38 | myCommand = ""; 39 | myCommand += 40 | static_cast(i); // sends basic 'acknowledge' command [address][!] 41 | myCommand += "!"; 42 | 43 | for (int j = 0; j < 3; j++) { // goes through three rapid contact attempts 44 | mySDI12.sendCommand(myCommand); 45 | delay(30); 46 | if (mySDI12.available()) { // If we here anything, assume we have an 47 | // active sensor 48 | Serial.println("Occupied"); 49 | mySDI12.clearBuffer(); 50 | return true; 51 | } else { 52 | Serial.println("Vacant"); // otherwise it is vacant. 53 | mySDI12.clearBuffer(); 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | 60 | void setup() { 61 | Serial.begin(SERIAL_BAUD); 62 | while (!Serial) { 63 | // wait 64 | } 65 | 66 | // Enable interrupts for the recieve pin 67 | pinMode(DATA_PIN, INPUT_PULLUP); 68 | enableInterrupt(DATA_PIN, SDI12::handleInterrupt, CHANGE); 69 | 70 | Serial.println("Opening SDI-12 bus..."); 71 | mySDI12.begin(); 72 | delay(500); // allow things to settle 73 | 74 | // Power the sensors; 75 | if (POWER_PIN > 0) { 76 | Serial.println("Powering up sensors..."); 77 | pinMode(POWER_PIN, OUTPUT); 78 | digitalWrite(POWER_PIN, HIGH); 79 | delay(200); 80 | } 81 | } 82 | 83 | void loop() { 84 | boolean found = false; // have we identified the sensor yet? 85 | 86 | for (byte i = '0'; i <= '9'; i++) { // scan address space 0-9 87 | if (found) break; 88 | if (checkActive(i)) { 89 | found = true; 90 | oldAddress = i; 91 | } 92 | } 93 | 94 | for (byte i = 'a'; i <= 'z'; i++) { // scan address space a-z 95 | if (found) break; 96 | if (checkActive(i)) { 97 | found = true; 98 | oldAddress = i; 99 | } 100 | } 101 | 102 | for (byte i = 'A'; i <= 'Z'; i++) { // scan address space A-Z 103 | if (found) break; 104 | if (checkActive(i)) { 105 | found = true; 106 | oldAddress = i; 107 | } 108 | } 109 | 110 | if (!found) { 111 | Serial.println( 112 | "No sensor detected. Check physical connections."); // couldn't 113 | // find a 114 | // sensor. 115 | // check 116 | // connections.. 117 | } else { 118 | Serial.print("Sensor active at address "); // found a sensor! 119 | Serial.print(oldAddress); 120 | Serial.println("."); 121 | 122 | Serial.println("Enter new address."); // prompt for a new address 123 | while (!Serial.available()) { 124 | // wait 125 | } 126 | char newAdd = Serial.read(); 127 | 128 | // wait for valid response 129 | while (((newAdd < '0') || (newAdd > '9')) && 130 | ((newAdd < 'a') || (newAdd > 'z')) && 131 | ((newAdd < 'A') || (newAdd > 'Z'))) { 132 | if (!(newAdd == '\n') || (newAdd == '\r') || (newAdd == ' ')) { 133 | Serial.println("Not a valid address. Please enter '0'-'9', " 134 | "'a'-'A', or 'z'-'Z'."); 135 | } 136 | while (!Serial.available()) { 137 | // wait 138 | } 139 | newAdd = Serial.read(); 140 | } 141 | 142 | // the syntax of the change address command 143 | // is:[currentAddress]A[newAddress]! 144 | Serial.println("Readdressing sensor."); 145 | myCommand = ""; 146 | myCommand += static_cast(oldAddress); 147 | myCommand += "A"; 148 | myCommand += static_cast(newAdd); 149 | myCommand += "!"; 150 | mySDI12.sendCommand(myCommand); 151 | 152 | // wait for the response then throw it away by clearing the buffer with 153 | // clearBuffer() 154 | delay(300); 155 | mySDI12.clearBuffer(); 156 | 157 | Serial.println("Success. Rescanning for verification."); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ModularSensors 2 | version=0.36.0 3 | author=Sara Damiano , Shannon Hicks 4 | maintainer=Sara Damiano 5 | sentence=A library that allows access to multiple sensors through a unified interface. 6 | paragraph=This allows the user to simply access many sensors to log the data or send the data to data repositories like the EnviroDIY data portal. 7 | category=Sensors 8 | url=https://github.com/EnviroDIY/ModularSensors 9 | architectures=avr,samd 10 | includes=LoggerBase.h 11 | depends=EnviroDIY_DS3231, RTCZero, EnableInterrupt, SdFat, TinyGSM, PubSubClient, Adafruit BusIO, Adafruit Unified Sensor, Adafruit ADS1X15, Adafruit AM2315, Adafruit BME280 Library, DHT sensor library, Adafruit INA219, Adafruit MPL115A2, Adafruit SHT4x Library, OneWire, DallasTemperature, SDI-12, SensorModbusMaster, KellerModbus, YosemitechModbus 12 | -------------------------------------------------------------------------------- /src/ModSensorInterrupts.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ModSensorInterrupts.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief A work-around for the AVR interrupt library not supporting SAMD 9 | * boards. That libary isn't necessary for them. 10 | */ 11 | 12 | 13 | // Header Guards 14 | #ifndef SRC_MODSENSORINTERRUPTS_H_ 15 | #define SRC_MODSENSORINTERRUPTS_H_ 16 | 17 | #include 18 | 19 | #if defined(__AVR__) || defined(ARDUINO_ARCH_AVR) 20 | // #define LIBCALL_ENABLEINTERRUPT // To prevent compiler/linker crashes 21 | #include // To handle external and pin change interrupts 22 | #else 23 | /** 24 | * @brief This define renames attachInterrupt as enableInterrupt to simplify 25 | * switching between AVR and non-AVR processors. 26 | */ 27 | #define enableInterrupt(pin, userFunc, mode) \ 28 | attachInterrupt(pin, userFunc, mode) 29 | /** 30 | * @brief This define renames detachInterrupt as disableInterrupt to simplify 31 | * switching between AVR and non-AVR processors. 32 | */ 33 | #define disableInterrupt(pin) detachInterrupt(pin) 34 | #endif 35 | 36 | 37 | #endif // SRC_MODSENSORINTERRUPTS_H_ 38 | -------------------------------------------------------------------------------- /src/ModularSensors.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ModularSensors.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief A simple include file for the Arduino command line interface (CLI). 9 | */ 10 | 11 | // Header Guards 12 | #ifndef SRC_MODULARSENSORS_H_ 13 | #define SRC_MODULARSENSORS_H_ 14 | 15 | /** 16 | * @brief The current library version number 17 | * https://semver.org/ 18 | * Add hypen '-' and alpha number for a branches unique tracking number 19 | * A pre-release version will always be indicated as slightly ahead of the 20 | * EnviroDIY branch that it is based on. 21 | */ 22 | #define MODULAR_SENSORS_VERSION "0.36.0" 23 | 24 | // To support interrupts 25 | #include "ModSensorInterrupts.h" 26 | 27 | // To get all of the base classes for ModularSensors, include LoggerBase. 28 | // NOTE: Individual sensor definitions must be included separately. 29 | #include "LoggerBase.h" 30 | 31 | #endif // SRC_MODULARSENSORS_H_ 32 | -------------------------------------------------------------------------------- /src/WatchDogs/WatchDogAVR.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WatchDogAVR.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the extendedWatchDogAVR class. 9 | */ 10 | 11 | #include "WatchDogAVR.h" 12 | 13 | // Be careful to use a platform-specific conditional include to only make the 14 | // code visible for the appropriate platform. Arduino will try to compile and 15 | // link all .cpp files regardless of platform. 16 | #if defined(__AVR__) || defined(ARDUINO_ARCH_AVR) 17 | 18 | #include 19 | #include 20 | 21 | volatile uint32_t extendedWatchDogAVR::_barksUntilReset = 0; 22 | 23 | extendedWatchDogAVR::extendedWatchDogAVR() {} 24 | extendedWatchDogAVR::~extendedWatchDogAVR() { 25 | disableWatchDog(); 26 | } 27 | 28 | 29 | // One-time initialization of watchdog timer. 30 | void extendedWatchDogAVR::setupWatchDog(uint32_t resetTime_s) { 31 | _resetTime_s = resetTime_s; 32 | extendedWatchDogAVR::_barksUntilReset = _resetTime_s / 8; 33 | MS_DBG(F("Watch-dog timeout is set for"), _resetTime_s, 34 | F("sec with the interrupt firing"), 35 | extendedWatchDogAVR::_barksUntilReset, F("times before the reset.")); 36 | } 37 | 38 | 39 | void extendedWatchDogAVR::enableWatchDog() { 40 | MS_DBG(F("Enabling watch dog...")); 41 | 42 | // The next section is timing critical so interrupts are disabled. 43 | cli(); 44 | // First clear any previous watchdog reset. 45 | MCUSR &= ~(1 << WDRF); 46 | 47 | // Put timer in interrupt-only mode: 48 | // WDTCSR - Watchdog Timer Control Register 49 | 50 | // Set WDCE and WDE to enable changes. 51 | // If changes aren't enabled, we cannot change the prescaler 52 | WDTCSR |= 0b00011000; // Set Bit 4 – WDCE: Watchdog Change Enable 53 | // Set Bit 3 – WDE: Watchdog System Reset Enable 54 | // bitwise OR assignment (leaves other bits unchanged) 55 | 56 | // Now can set the full register including the prescaler 57 | WDTCSR = 0b01100001; 58 | // Bit 7: WDIF (Watchdog Interrupt Flag) - 0 (Read only) 59 | // Bit 6: WDIE (Watchdog Interrupt Enable) - 1 (Enabled) 60 | // Bit 5: WDP3 (Watchdog Timer Prescaler) - see delay interval patterns 61 | // Bit 4: WDCE (Watchdog Change Enable) - 0 (disable further changes) 62 | // Bit 3: WDE (Watchdog System Reset Enable) - 0 (Clear?) 63 | // Bits 2:0 Watchdog timer prescaler [WDP2:0] - see delay interval patterns 64 | 65 | // delay interval patterns: 66 | // 16 ms: 0bxx0xx000 67 | // 500 ms: 0bxx0xx101 68 | // 1 second: 0bxx0xx110 69 | // 2 seconds: 0bxx0xx111 70 | // 4 seconds: 0bxx1xx000 71 | // 8 seconds: 0bxx1xx001 72 | 73 | sei(); // re-enable interrupts 74 | // wdt_reset(); // this is not needed...timer starts without it 75 | 76 | extendedWatchDogAVR::_barksUntilReset = _resetTime_s / 8; 77 | MS_DBG(F("The watch dog is enabled in interrupt-only mode.")); 78 | MS_DBG(F("The interrupt will fire"), extendedWatchDogAVR::_barksUntilReset, 79 | F("times before the system resets.")); 80 | } 81 | 82 | 83 | void extendedWatchDogAVR::disableWatchDog() { 84 | // Disable the watchdog 85 | wdt_disable(); 86 | } 87 | 88 | 89 | void extendedWatchDogAVR::resetWatchDog() { 90 | extendedWatchDogAVR::_barksUntilReset = _resetTime_s / 8; 91 | // Reset the watchdog. 92 | wdt_reset(); 93 | } 94 | 95 | 96 | /** 97 | * @brief ISR for watchdog early warning 98 | */ 99 | ISR(WDT_vect) { 100 | extendedWatchDogAVR::_barksUntilReset--; // Increament down the counter, 101 | // makes multi cycle WDT possible 102 | // MS_DBG(F("\nWatchdog interrupt!"), 103 | // extendedWatchDogAVR::_barksUntilReset); 104 | if (extendedWatchDogAVR::_barksUntilReset <= 0) { 105 | MCUSR = 0; // reset flags 106 | 107 | // Put timer in reset-only mode: 108 | WDTCSR |= 0b00011000; // Enter config mode. 109 | WDTCSR = 0b00001000 | 110 | 0b000000; // clr WDIE (interrupt enable...7th from left) 111 | // set WDE (reset enable...4th from left), and set delay 112 | // interval reset system in 16 ms... unless wdt_disable() 113 | // in loop() is reached first 114 | 115 | // wdt_reset(); // not needed 116 | } else { 117 | wdt_reset(); // start timer again (still in interrupt-only mode) 118 | } 119 | } 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/WatchDogs/WatchDogAVR.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WatchDogAVR.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Contains the extendedWatchDogAVR class 9 | * 10 | * Code for this is taken from this forum post: 11 | * https://forum.arduino.cc/index.php?topic=248263.0 12 | */ 13 | 14 | // Header Guards 15 | #ifndef SRC_WATCHDOGS_WATCHDOGAVR_H_ 16 | #define SRC_WATCHDOGS_WATCHDOGAVR_H_ 17 | 18 | // Debugging Statement 19 | // #define MS_WATCHDOGAVR_DEBUG 20 | 21 | #ifdef MS_WATCHDOGAVR_DEBUG 22 | #define MS_DEBUGGING_STD "WatchDogAVR" 23 | #endif 24 | 25 | // Included Dependencies 26 | #include "ModSensorDebugger.h" 27 | #undef MS_DEBUGGING_STD 28 | 29 | /** 30 | * @brief The extendedWatchDogAVR class uses the pre-reset interrupt to of the 31 | * built in AVR watchdog to extend the allowable time between resets of the 32 | * watchdog's clock up to multiple minute timescales. 33 | * 34 | * The standard watchdog on an AVR processor has a maximum period of 8s without 35 | * a reset of the watchdog clock before the processor is restarted. 36 | * 37 | * Code for this is taken from this forum post: 38 | * https://forum.arduino.cc/index.php?topic=248263.0 39 | * 40 | * @ingroup base_classes 41 | */ 42 | class extendedWatchDogAVR { 43 | public: 44 | /** 45 | * @brief Construct a new extended watch dog object for AVR processors. 46 | */ 47 | extendedWatchDogAVR(); 48 | /** 49 | * @brief Destroy the extended watch dog object for AVR processors. 50 | */ 51 | ~extendedWatchDogAVR(); 52 | 53 | /** 54 | * @brief One-time initialization of watchdog timer. 55 | * 56 | * @param resetTime_s The length of time in seconds between resets of the 57 | * watchdog before the entire board is reset. 58 | */ 59 | void setupWatchDog(uint32_t resetTime_s); 60 | /** 61 | * @brief Enable the watchdog. 62 | */ 63 | void enableWatchDog(); 64 | /** 65 | * @brief Disable the watchdog. 66 | */ 67 | void disableWatchDog(); 68 | 69 | /** 70 | * @brief Reset the watchdog's clock to prevent the board from resetting. 71 | */ 72 | void resetWatchDog(); 73 | 74 | 75 | /** 76 | * @brief The number of times the pre-reset interrupt is allowed to fire 77 | * before the watchdog reset is allowed. 78 | */ 79 | static volatile uint32_t _barksUntilReset; 80 | 81 | private: 82 | /** 83 | * @brief Internal reference to the number of seconds of silence before the 84 | * module is reset. 85 | */ 86 | uint32_t _resetTime_s; 87 | }; 88 | 89 | #endif // SRC_WATCHDOGS_WATCHDOGAVR_H_ 90 | -------------------------------------------------------------------------------- /src/WatchDogs/WatchDogSAMD.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file WatchDogSAMD.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Contains the extendedWatchDogSAMD class. 9 | * 10 | * Code for this is taken from the Adafruit SleepyDog library: 11 | * https://github.com/adafruit/Adafruit_SleepyDog/ and this library: 12 | * https://github.com/javos65/WDTZero 13 | */ 14 | 15 | // Header Guards 16 | #ifndef SRC_WATCHDOGS_WATCHDOGSAMD_H_ 17 | #define SRC_WATCHDOGS_WATCHDOGSAMD_H_ 18 | 19 | // Debugging Statement 20 | // #define MS_WATCHDOGSAMD_DEBUG 21 | 22 | #ifdef MS_WATCHDOGSAMD_DEBUG 23 | #define MS_DEBUGGING_STD "WatchDogSAMD" 24 | #endif 25 | 26 | // Included Dependencies 27 | #include "ModSensorDebugger.h" 28 | #undef MS_DEBUGGING_STD 29 | 30 | /** 31 | * @brief ISR handler for watchdog timer early warning (WDT EW ) interrupt 32 | */ 33 | void WDT_Handler(void); 34 | 35 | /** 36 | * @brief The extendedWatchDogSAMD class uses the early warning interrupt to of 37 | * the built in SAMD watchdog to extend the allowable time between resets of the 38 | * watchdog's clock up to multiple minute timescales. 39 | * 40 | * Code for this is taken from the Adafruit SleepyDog library: 41 | * https://github.com/adafruit/Adafruit_SleepyDog/ and this library: 42 | * https://github.com/javos65/WDTZero 43 | * 44 | * @ingroup base_classes 45 | */ 46 | class extendedWatchDogSAMD { 47 | public: 48 | /** 49 | * @brief Construct a new extended watch dog object for SAMD processors. 50 | */ 51 | extendedWatchDogSAMD(); 52 | /** 53 | * @brief Destroy the extended watch dog object for SAMD processors. 54 | */ 55 | ~extendedWatchDogSAMD(); 56 | 57 | /** 58 | * @brief One-time initialization of watchdog timer. 59 | * 60 | * @param resetTime_s The length of time in seconds between resets of the 61 | * watchdog before the entire board is reset. 62 | */ 63 | void setupWatchDog(uint32_t resetTime_s); 64 | /** 65 | * @brief Enable the watchdog. 66 | */ 67 | void enableWatchDog(); 68 | /** 69 | * @brief Disable the watchdog. 70 | */ 71 | void disableWatchDog(); 72 | 73 | /** 74 | * @brief Reset the watchdog's clock to prevent the board from resetting. 75 | */ 76 | void resetWatchDog(); 77 | 78 | 79 | /** 80 | * @brief The number of times the pre-reset interrupt is allowed to fire 81 | * before the watchdog reset is allowed. 82 | */ 83 | static volatile uint32_t _barksUntilReset; 84 | 85 | private: 86 | /** 87 | * @brief Wait for the SAMD processor bit sync to finish.+ 88 | */ 89 | void inline waitForWDTBitSync(); 90 | /** 91 | * @brief Internal reference to the number of seconds of silence before the 92 | * module is reset. 93 | */ 94 | uint32_t _resetTime_s; 95 | }; 96 | 97 | #endif // SRC_WATCHDOGS_WATCHDOGSAMD_H_ 98 | -------------------------------------------------------------------------------- /src/modems/DigiXBee.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file DigiXBee.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the DigiXBee class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "DigiXBee.h" 13 | 14 | 15 | // Constructor 16 | DigiXBee::DigiXBee(int8_t powerPin, int8_t statusPin, bool useCTSStatus, 17 | int8_t modemResetPin, int8_t modemSleepRqPin) 18 | : loggerModem(powerPin, statusPin, !useCTSStatus, modemResetPin, 19 | XBEE_RESET_LEVEL, XBEE_RESET_PULSE_MS, modemSleepRqPin, 20 | XBEE_WAKE_LEVEL, XBEE_WAKE_PULSE_MS, XBEE_STATUS_TIME_MS, 21 | XBEE_DISCONNECT_TIME_MS, XBEE_WAKE_DELAY_MS, 22 | XBEE_ATRESPONSE_TIME_MS) {} 23 | 24 | // Destructor 25 | DigiXBee::~DigiXBee() {} 26 | 27 | 28 | // Create the wake and sleep methods for the modem 29 | // These can be functions of any type and must return a boolean 30 | // After enabling pin sleep, the sleep request pin is held `LOW` to keep the 31 | // XBee on. Enable pin sleep in the setup function or using XCTU prior to 32 | // connecting the XBee 33 | bool DigiXBee::modemWakeFxn(void) { 34 | if (_modemSleepRqPin >= 0) { 35 | // Don't go to sleep if there's not a wake pin! 36 | MS_DBG(F("Setting pin"), _modemSleepRqPin, 37 | _wakeLevel ? F("HIGH") : F("LOW"), F("to wake"), _modemName); 38 | digitalWrite(_modemSleepRqPin, _wakeLevel); 39 | return true; 40 | } else { 41 | return true; 42 | } 43 | } 44 | 45 | 46 | bool DigiXBee::modemSleepFxn(void) { 47 | if (_modemSleepRqPin >= 0) { 48 | MS_DBG(F("Setting pin"), _modemSleepRqPin, 49 | !_wakeLevel ? F("HIGH") : F("LOW"), F("to put"), _modemName, 50 | F("to sleep")); 51 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 52 | return true; 53 | } else { 54 | return true; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/modems/EspressifESP32.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EspressifESP32.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Contains the EspressifESP32 subclass of loggerModem which is merely a 9 | * typedef for the EspressifESP8266 class. 10 | */ 11 | /* clang-format off */ 12 | 13 | // Header Guards 14 | #ifndef SRC_MODEMS_ESPRESSIFESP32_H_ 15 | #define SRC_MODEMS_ESPRESSIFESP32_H_ 16 | 17 | // Included the ESP8266 18 | #include "EspressifESP8266.h" 19 | 20 | #endif // SRC_MODEMS_ESPRESSIFESP32_H_ 21 | -------------------------------------------------------------------------------- /src/modems/EspressifESP8266.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EspressifESP8266.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the EspressifESP8266 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "EspressifESP8266.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructors 16 | EspressifESP8266::EspressifESP8266(Stream* modemStream, int8_t powerPin, 17 | int8_t modemResetPin, const char* ssid, 18 | const char* pwd) 19 | : loggerModem(powerPin, -1, ESP8266_STATUS_LEVEL, modemResetPin, 20 | ESP8266_RESET_LEVEL, ESP8266_RESET_PULSE_MS, -1, 21 | ESP8266_WAKE_LEVEL, ESP8266_WAKE_PULSE_MS, 22 | ESP8266_STATUS_TIME_MS, ESP8266_DISCONNECT_TIME_MS, 23 | ESP8266_WAKE_DELAY_MS, ESP8266_ATRESPONSE_TIME_MS), 24 | #ifdef MS_ESPRESSIFESP8266_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _modemStream(modemStream), 32 | _ssid(ssid), 33 | _pwd(pwd) { 34 | } 35 | 36 | // Destructor 37 | EspressifESP8266::~EspressifESP8266() {} 38 | 39 | MS_IS_MODEM_AWAKE(EspressifESP8266); 40 | MS_MODEM_WAKE(EspressifESP8266); 41 | 42 | MS_MODEM_CONNECT_INTERNET(EspressifESP8266, ESP8266_RECONNECT_TIME_MS); 43 | MS_MODEM_DISCONNECT_INTERNET(EspressifESP8266); 44 | MS_MODEM_IS_INTERNET_AVAILABLE(EspressifESP8266); 45 | 46 | MS_MODEM_GET_NIST_TIME(EspressifESP8266); 47 | 48 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(EspressifESP8266); 49 | MS_MODEM_GET_MODEM_BATTERY_DATA(EspressifESP8266); 50 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(EspressifESP8266); 51 | 52 | // A helper function to wait for the esp to boot and immediately change some 53 | // settings We'll use this in the wake function 54 | bool EspressifESP8266::ESPwaitForBoot(void) { 55 | // Wait for boot - finished when characters start coming 56 | // NOTE: After every "hard" reset (either power off or via RST-B), the ESP 57 | // sends out a boot log from the ROM on UART1 at 74880 baud. We're not 58 | // going to worry about the odd baud rate since we're simply throwing the 59 | // characters away. 60 | MS_DBG(F("Waiting for boot-up message from ESP8266")); 61 | delay(200); // It will take at least this long 62 | uint32_t start = millis(); 63 | bool success = false; 64 | while (!_modemStream->available() && millis() - start < 1000) { 65 | // wait 66 | } 67 | if (_modemStream->available()) { 68 | success = true; 69 | // Read the boot log to empty it from the serial buffer 70 | while (_modemStream->available()) { 71 | _modemStream->read(); 72 | delay(2); 73 | } 74 | } 75 | return success; 76 | } 77 | 78 | // Create the wake and sleep methods for the modem 79 | // These can be functions of any type and must return a boolean 80 | bool EspressifESP8266::modemWakeFxn(void) { 81 | bool success = true; 82 | if (_powerPin >= 0) { // Turns on when power is applied 83 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 84 | success &= ESPwaitForBoot(); 85 | if (_modemSleepRqPin >= 0) { 86 | digitalWrite(_modemSleepRqPin, _wakeLevel); 87 | } 88 | return success; 89 | } else if (_modemResetPin >= 0) { 90 | MS_DBG(F("Sending a reset pulse to pin"), _modemResetPin, 91 | F("to wake ESP8266 from deep sleep")); 92 | digitalWrite(_modemResetPin, LOW); 93 | delay(_resetPulse_ms); 94 | digitalWrite(_modemResetPin, HIGH); 95 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 96 | success &= ESPwaitForBoot(); 97 | if (_modemSleepRqPin >= 0) { 98 | digitalWrite(_modemSleepRqPin, _wakeLevel); 99 | } 100 | return success; 101 | } else if (_modemSleepRqPin >= 0) { 102 | MS_DBG(F("Setting pin"), _modemSleepRqPin, 103 | _wakeLevel ? F("HIGH") : F("LOW"), 104 | F("to wake ESP8266 from light sleep")); 105 | digitalWrite(_modemSleepRqPin, _wakeLevel); 106 | return success; 107 | } else { 108 | return true; 109 | } 110 | } 111 | 112 | bool EspressifESP8266::modemSleepFxn(void) { 113 | // Use this if you have an MCU pin connected to the ESP's reset pin to wake 114 | // from deep sleep. We'll also put it in deep sleep before yanking power. 115 | if (_modemResetPin >= 0 || _powerPin >= 0) { 116 | MS_DBG(F("Requesting deep sleep for ESP8266")); 117 | bool retVal = gsmModem.poweroff(); 118 | if (_modemSleepRqPin >= 0) { 119 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 120 | } 121 | return retVal; 122 | } else { // DON'T go to sleep if we can't wake up! 123 | return true; 124 | } 125 | } 126 | 127 | // Set up the light-sleep status pin, if applicable 128 | bool EspressifESP8266::extraModemSetup(void) { 129 | if (_modemSleepRqPin >= 0) { digitalWrite(_modemSleepRqPin, !_wakeLevel); } 130 | gsmModem.init(); 131 | gsmClient.init(&gsmModem); 132 | _modemName = gsmModem.getModemName(); 133 | return true; 134 | } 135 | -------------------------------------------------------------------------------- /src/modems/QuectelBG96.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file QuectelBG96.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the QuectelBG96 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "QuectelBG96.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructor 16 | QuectelBG96::QuectelBG96(Stream* modemStream, int8_t powerPin, int8_t statusPin, 17 | int8_t modemResetPin, int8_t modemSleepRqPin, 18 | const char* apn) 19 | : loggerModem(powerPin, statusPin, BG96_STATUS_LEVEL, modemResetPin, 20 | BG96_RESET_LEVEL, BG96_RESET_PULSE_MS, modemSleepRqPin, 21 | BG96_WAKE_LEVEL, BG96_WAKE_PULSE_MS, BG96_STATUS_TIME_MS, 22 | BG96_DISCONNECT_TIME_MS, BG96_WAKE_DELAY_MS, 23 | BG96_ATRESPONSE_TIME_MS), 24 | #ifdef MS_QUECTELBG96_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _apn(apn) { 32 | } 33 | 34 | // Destructor 35 | QuectelBG96::~QuectelBG96() {} 36 | 37 | MS_MODEM_EXTRA_SETUP(QuectelBG96); 38 | MS_IS_MODEM_AWAKE(QuectelBG96); 39 | MS_MODEM_WAKE(QuectelBG96); 40 | 41 | MS_MODEM_CONNECT_INTERNET(QuectelBG96); 42 | MS_MODEM_DISCONNECT_INTERNET(QuectelBG96); 43 | MS_MODEM_IS_INTERNET_AVAILABLE(QuectelBG96); 44 | 45 | MS_MODEM_GET_NIST_TIME(QuectelBG96); 46 | 47 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(QuectelBG96); 48 | MS_MODEM_GET_MODEM_BATTERY_DATA(QuectelBG96); 49 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(QuectelBG96); 50 | 51 | // Create the wake and sleep methods for the modem 52 | // These can be functions of any type and must return a boolean 53 | bool QuectelBG96::modemWakeFxn(void) { 54 | // Must power on and then pulse on 55 | if (_modemSleepRqPin >= 0) { 56 | MS_DBG(F("Sending a"), _wakePulse_ms, F("ms"), 57 | _wakeLevel ? F("HIGH") : F("LOW"), F("wake-up pulse on pin"), 58 | _modemSleepRqPin, F("for"), _modemName); 59 | digitalWrite(_modemSleepRqPin, _wakeLevel); 60 | delay(_wakePulse_ms); // ≥100ms 61 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 62 | return gsmModem.waitResponse(10000L, GF("RDY")) == 1; 63 | } 64 | return true; 65 | } 66 | 67 | 68 | bool QuectelBG96::modemSleepFxn(void) { 69 | if (_modemSleepRqPin >= 0) { 70 | // BG96 must have access to `PWRKEY` pin to sleep 71 | // Easiest to just go to sleep with the AT command rather than using 72 | // pins 73 | return gsmModem.poweroff(); 74 | } 75 | return true; // DON'T go to sleep if we can't wake up! 76 | } 77 | 78 | bool QuectelBG96::modemHardReset(void) { 79 | digitalWrite(_modemSleepRqPin, !_wakeLevel); // set the wake pin high 80 | bool success = loggerModem::modemHardReset(); 81 | if (success) { return gsmModem.waitResponse(10000L, GF("RDY")) == 1; } 82 | return false; 83 | } 84 | -------------------------------------------------------------------------------- /src/modems/SIMComSIM7000.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SIMComSIM7000.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the SIMComSIM7000 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "SIMComSIM7000.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructor 16 | SIMComSIM7000::SIMComSIM7000(Stream* modemStream, int8_t powerPin, 17 | int8_t statusPin, int8_t modemResetPin, 18 | int8_t modemSleepRqPin, const char* apn) 19 | : loggerModem(powerPin, statusPin, SIM7000_STATUS_LEVEL, modemResetPin, 20 | SIM7000_RESET_LEVEL, SIM7000_RESET_PULSE_MS, modemSleepRqPin, 21 | SIM7000_WAKE_LEVEL, SIM7000_WAKE_PULSE_MS, 22 | SIM7000_STATUS_TIME_MS, SIM7000_DISCONNECT_TIME_MS, 23 | SIM7000_WAKE_DELAY_MS, SIM7000_ATRESPONSE_TIME_MS), 24 | #ifdef MS_SIMCOMSIM7000_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _apn(apn) { 32 | } 33 | 34 | // Destructor 35 | SIMComSIM7000::~SIMComSIM7000() {} 36 | 37 | MS_MODEM_EXTRA_SETUP(SIMComSIM7000); 38 | MS_IS_MODEM_AWAKE(SIMComSIM7000); 39 | MS_MODEM_WAKE(SIMComSIM7000); 40 | 41 | MS_MODEM_CONNECT_INTERNET(SIMComSIM7000); 42 | MS_MODEM_DISCONNECT_INTERNET(SIMComSIM7000); 43 | MS_MODEM_IS_INTERNET_AVAILABLE(SIMComSIM7000); 44 | 45 | MS_MODEM_GET_NIST_TIME(SIMComSIM7000); 46 | 47 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(SIMComSIM7000); 48 | MS_MODEM_GET_MODEM_BATTERY_DATA(SIMComSIM7000); 49 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(SIMComSIM7000); 50 | 51 | // Create the wake and sleep methods for the modem 52 | // These can be functions of any type and must return a boolean 53 | bool SIMComSIM7000::modemWakeFxn(void) { 54 | // Must power on and then pulse on 55 | if (_modemSleepRqPin >= 0) { 56 | MS_DBG(F("Sending a"), _wakePulse_ms, F("ms"), 57 | _wakeLevel ? F("HIGH") : F("LOW"), F("wake-up pulse on pin"), 58 | _modemSleepRqPin, F("for"), _modemName); 59 | digitalWrite(_modemSleepRqPin, _wakeLevel); 60 | delay(_wakePulse_ms); // >1s 61 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 62 | } 63 | return true; 64 | } 65 | 66 | 67 | bool SIMComSIM7000::modemSleepFxn(void) { 68 | if (_modemSleepRqPin >= 0) { 69 | // Must have access to `PWRKEY` pin to sleep 70 | // Easiest to just go to sleep with the AT command rather than using 71 | // pins 72 | MS_DBG(F("Asking SIM7000 to power down")); 73 | return gsmModem.poweroff(); 74 | } else { // DON'T go to sleep if we can't wake up! 75 | return true; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/modems/SIMComSIM7080.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SIMComSIM7080.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the SIMComSIM7080 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "SIMComSIM7080.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructor 16 | SIMComSIM7080::SIMComSIM7080(Stream* modemStream, int8_t powerPin, 17 | int8_t statusPin, int8_t modemSleepRqPin, 18 | const char* apn) 19 | : loggerModem(powerPin, statusPin, SIM7080_STATUS_LEVEL, modemSleepRqPin, 20 | SIM7080_RESET_LEVEL, SIM7080_RESET_PULSE_MS, modemSleepRqPin, 21 | SIM7080_WAKE_LEVEL, SIM7080_WAKE_PULSE_MS, 22 | SIM7080_STATUS_TIME_MS, SIM7080_DISCONNECT_TIME_MS, 23 | SIM7080_WAKE_DELAY_MS, SIM7080_ATRESPONSE_TIME_MS), 24 | #ifdef MS_SIMCOMSIM7080_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _apn(apn) { 32 | } 33 | 34 | // Destructor 35 | SIMComSIM7080::~SIMComSIM7080() {} 36 | 37 | bool SIMComSIM7080::extraModemSetup(void) { 38 | bool success = gsmModem.init(); 39 | gsmClient.init(&gsmModem); 40 | _modemName = gsmModem.getModemName(); 41 | 42 | // The modem is liable to crash if the send buffer overflows and TinyGSM 43 | // offers no way to know when that might happen. Reduce the chance of 44 | // problems by maxing out the send buffer size. This size should accommodate 45 | // a completely full 8K LogBuffer and a crappy connection. 46 | gsmModem.sendAT(F("+CACFG=\"SNDBUF\",29200")); 47 | gsmModem.waitResponse(); 48 | 49 | // Enable the netlight indicator 50 | gsmModem.sendAT(F("+CNETLIGHT=1")); 51 | gsmModem.waitResponse(); 52 | // Enable netlight indication of GPRS status 53 | // Enable, the netlight will be forced to enter into 64ms on/300ms off 54 | // blinking state in GPRS data transmission service.Otherwise, the netlight 55 | // state is not restricted. 56 | gsmModem.sendAT(F("+CNETLIGHT=1")); 57 | gsmModem.waitResponse(); 58 | 59 | // Enable the battery check functionality 60 | gsmModem.sendAT(F("+CBATCHK=1")); 61 | gsmModem.waitResponse(); 62 | 63 | return success; 64 | } 65 | 66 | MS_IS_MODEM_AWAKE(SIMComSIM7080); 67 | MS_MODEM_WAKE(SIMComSIM7080); 68 | 69 | MS_MODEM_CONNECT_INTERNET(SIMComSIM7080); 70 | MS_MODEM_DISCONNECT_INTERNET(SIMComSIM7080); 71 | MS_MODEM_IS_INTERNET_AVAILABLE(SIMComSIM7080); 72 | 73 | MS_MODEM_GET_NIST_TIME(SIMComSIM7080); 74 | 75 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(SIMComSIM7080); 76 | MS_MODEM_GET_MODEM_BATTERY_DATA(SIMComSIM7080); 77 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(SIMComSIM7080); 78 | 79 | // Create the wake and sleep methods for the modem 80 | // These can be functions of any type and must return a boolean 81 | bool SIMComSIM7080::modemWakeFxn(void) { 82 | // Must power on and then pulse on 83 | if (_modemSleepRqPin >= 0) { 84 | MS_DBG(F("Sending a"), _wakePulse_ms, F("ms"), 85 | _wakeLevel ? F("HIGH") : F("LOW"), F("wake-up pulse on pin"), 86 | _modemSleepRqPin, F("for"), _modemName); 87 | digitalWrite(_modemSleepRqPin, _wakeLevel); 88 | delay(_wakePulse_ms); // >1s 89 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 90 | int ready_response = gsmModem.waitResponse(30000L, GF("SMS Ready"), 91 | GF("+CPIN: NOT INSERTED")); 92 | if (ready_response == 1) { 93 | MS_DBG(F("Got SMS Ready indicating modem is awake and ready")); 94 | } else if (ready_response == 2) { 95 | MS_DBG(F("Got +CPIN: NOT INSERTED indicating modem is awake and " 96 | "ready but has no SIM card")); 97 | } else { 98 | MS_DBG(F("Didn't get expected finish URC for modem wake!")); 99 | } 100 | 101 | return ready_response == 1 || ready_response == 2; 102 | } 103 | return true; 104 | } 105 | 106 | 107 | bool SIMComSIM7080::modemSleepFxn(void) { 108 | if (_modemSleepRqPin >= 0) { 109 | // Must have access to `PWRKEY` pin to sleep 110 | // Easiest to just go to sleep with the AT command rather than using 111 | // pins 112 | MS_DBG(F("Asking SIM7080 to power down")); 113 | return gsmModem.poweroff(); 114 | } else { // DON'T go to sleep if we can't wake up! 115 | return true; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/modems/SIMComSIM800.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SIMComSIM800.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the SIMComSIM800 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "SIMComSIM800.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructor 16 | SIMComSIM800::SIMComSIM800(Stream* modemStream, int8_t powerPin, 17 | int8_t statusPin, int8_t modemResetPin, 18 | int8_t modemSleepRqPin, const char* apn) 19 | : loggerModem(powerPin, statusPin, SIM800_STATUS_LEVEL, modemResetPin, 20 | SIM800_RESET_LEVEL, SIM800_RESET_PULSE_MS, modemSleepRqPin, 21 | SIM800_WAKE_LEVEL, SIM800_WAKE_PULSE_MS, 22 | SIM800_STATUS_TIME_MS, SIM800_DISCONNECT_TIME_MS, 23 | SIM800_WAKE_DELAY_MS, SIM800_ATRESPONSE_TIME_MS), 24 | #ifdef MS_SIMCOMSIM800_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _apn(apn) { 32 | } 33 | 34 | // Destructor 35 | SIMComSIM800::~SIMComSIM800() {} 36 | 37 | MS_MODEM_EXTRA_SETUP(SIMComSIM800); 38 | MS_IS_MODEM_AWAKE(SIMComSIM800); 39 | MS_MODEM_WAKE(SIMComSIM800); 40 | 41 | MS_MODEM_CONNECT_INTERNET(SIMComSIM800); 42 | MS_MODEM_DISCONNECT_INTERNET(SIMComSIM800); 43 | MS_MODEM_IS_INTERNET_AVAILABLE(SIMComSIM800); 44 | 45 | MS_MODEM_GET_NIST_TIME(SIMComSIM800); 46 | 47 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(SIMComSIM800); 48 | MS_MODEM_GET_MODEM_BATTERY_DATA(SIMComSIM800); 49 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(SIMComSIM800); 50 | 51 | // Create the wake and sleep methods for the modem 52 | // These can be functions of any type and must return a boolean 53 | bool SIMComSIM800::modemWakeFxn(void) { 54 | // Must power on and then pulse on 55 | if (_modemSleepRqPin >= 0) { 56 | MS_DBG(F("Sending a"), _wakePulse_ms, F("ms"), 57 | _wakeLevel ? F("HIGH") : F("LOW"), F("wake-up pulse on pin"), 58 | _modemSleepRqPin, F("for"), _modemName); 59 | digitalWrite(_modemSleepRqPin, _wakeLevel); 60 | delay(_wakePulse_ms); // >1s 61 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 62 | } 63 | return true; 64 | } 65 | 66 | 67 | bool SIMComSIM800::modemSleepFxn(void) { 68 | if (_modemSleepRqPin >= 0) { 69 | // Must have access to `PWRKEY` pin to sleep 70 | // Easiest to just go to sleep with the AT command rather than using 71 | // pins 72 | MS_DBG(F("Asking SIM800 to power down")); 73 | return gsmModem.poweroff(); 74 | } else { // DON'T go to sleep if we can't wake up! 75 | return true; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/modems/Sodaq2GBeeR6.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Sodaq2GBeeR6.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the Sodaq2GBeeR6 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "Sodaq2GBeeR6.h" 13 | 14 | 15 | // Constructor 16 | Sodaq2GBeeR6::Sodaq2GBeeR6(Stream* modemStream, int8_t powerPin, 17 | int8_t statusPin, const char* apn) 18 | : SIMComSIM800(modemStream, powerPin, statusPin, -1, -1, apn) { 19 | setVRefPin(-1); 20 | } 21 | 22 | 23 | // Constructor 24 | Sodaq2GBeeR6::Sodaq2GBeeR6(Stream* modemStream, int8_t vRefPin, 25 | int8_t statusPin, int8_t powerPin, const char* apn) 26 | : SIMComSIM800(modemStream, powerPin, statusPin, -1, -1, apn) { 27 | setVRefPin(vRefPin); 28 | } 29 | 30 | // Destructor 31 | Sodaq2GBeeR6::~Sodaq2GBeeR6() {} 32 | 33 | // Create the wake and sleep methods for the modem 34 | // These can be functions of any type and must return a boolean 35 | bool Sodaq2GBeeR6::modemWakeFxn(void) { 36 | if (_vRefPin >= 0) { 37 | MS_DBG(F("Enabling voltage reference for GPRSBeeR6 on pin"), _vRefPin); 38 | digitalWrite(_vRefPin, HIGH); 39 | } 40 | return true; 41 | } 42 | 43 | 44 | bool Sodaq2GBeeR6::modemSleepFxn(void) { 45 | // Ask the SIM800 to shut down nicely 46 | MS_DBG(F("Asking SIM800 on GPRSBeeR6 to power down")); 47 | bool success = gsmModem.poweroff(); 48 | if (_vRefPin >= 0) { 49 | MS_DBG(F("Disabling voltage reference for GPRSBeeR6 on pin"), _vRefPin); 50 | digitalWrite(_vRefPin, LOW); 51 | } 52 | return success; 53 | } 54 | 55 | bool Sodaq2GBeeR6::extraModemSetup(void) { 56 | bool success = gsmModem.init(); 57 | gsmClient.init(&gsmModem); 58 | _modemName = gsmModem.getModemName(); 59 | if (_vRefPin >= 0) pinMode(_vRefPin, OUTPUT); 60 | return success; 61 | } 62 | 63 | void Sodaq2GBeeR6::setVRefPin(int8_t vRefPin) { 64 | _vRefPin = vRefPin; 65 | } 66 | -------------------------------------------------------------------------------- /src/modems/SodaqUBeeU201.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SodaqUBeeU201.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the SodaqUBeeU201 class. 9 | */ 10 | 11 | // Included Dependencies 12 | #include "SodaqUBeeU201.h" 13 | #include "LoggerModemMacros.h" 14 | 15 | // Constructor 16 | SodaqUBeeU201::SodaqUBeeU201(Stream* modemStream, int8_t powerPin, 17 | int8_t statusPin, int8_t modemResetPin, 18 | int8_t modemSleepRqPin, const char* apn) 19 | : loggerModem(powerPin, statusPin, U201_STATUS_LEVEL, modemResetPin, 20 | U201_RESET_LEVEL, U201_RESET_PULSE_MS, modemSleepRqPin, 21 | U201_WAKE_LEVEL, U201_WAKE_PULSE_MS, U201_STATUS_TIME_MS, 22 | U201_DISCONNECT_TIME_MS, U201_WAKE_DELAY_MS, 23 | U201_ATRESPONSE_TIME_MS), 24 | #ifdef MS_SODAQUBEEU201_DEBUG_DEEP 25 | _modemATDebugger(*modemStream, DEEP_DEBUGGING_SERIAL_OUTPUT), 26 | gsmModem(_modemATDebugger), 27 | #else 28 | gsmModem(*modemStream), 29 | #endif 30 | gsmClient(gsmModem), 31 | _apn(apn) { 32 | } 33 | 34 | // Destructor 35 | SodaqUBeeU201::~SodaqUBeeU201() {} 36 | 37 | MS_IS_MODEM_AWAKE(SodaqUBeeU201); 38 | MS_MODEM_WAKE(SodaqUBeeU201); 39 | 40 | MS_MODEM_CONNECT_INTERNET(SodaqUBeeU201); 41 | MS_MODEM_DISCONNECT_INTERNET(SodaqUBeeU201); 42 | MS_MODEM_IS_INTERNET_AVAILABLE(SodaqUBeeU201); 43 | 44 | MS_MODEM_GET_NIST_TIME(SodaqUBeeU201); 45 | 46 | MS_MODEM_GET_MODEM_SIGNAL_QUALITY(SodaqUBeeU201); 47 | MS_MODEM_GET_MODEM_BATTERY_DATA(SodaqUBeeU201); 48 | MS_MODEM_GET_MODEM_TEMPERATURE_DATA(SodaqUBeeU201); 49 | 50 | // Create the wake and sleep methods for the modem 51 | // These can be functions of any type and must return a boolean 52 | bool SodaqUBeeU201::modemWakeFxn(void) { 53 | // SARA/LISA U2/G2 and SARA G3 series turn on when power is applied 54 | // No pulsing required in this case 55 | if (_powerPin >= 0) { return true; } 56 | if (_modemSleepRqPin >= 0) { 57 | MS_DBG(F("Sending a"), _wakePulse_ms, F("ms"), 58 | _wakeLevel ? F("HIGH") : F("LOW"), F("wake-up pulse on pin"), 59 | _modemSleepRqPin, F("for Sodaq UBee U201")); 60 | digitalWrite(_modemSleepRqPin, _wakeLevel); 61 | // 50-80µs pulse for wake on SARA/LISA U2/G2 62 | delayMicroseconds(_wakePulse_ms); 63 | digitalWrite(_modemSleepRqPin, !_wakeLevel); 64 | return true; 65 | } else { 66 | return true; 67 | } 68 | } 69 | 70 | 71 | bool SodaqUBeeU201::modemSleepFxn(void) { 72 | if (_powerPin >= 0 || _modemSleepRqPin >= 0) { 73 | // will go on with power on 74 | // Easiest to just go to sleep with the AT command rather than using 75 | // pins 76 | MS_DBG(F("Asking u-blox SARA U201 to power down")); 77 | return gsmModem.poweroff(); 78 | } else { // DON'T go to sleep if we can't wake up! 79 | return true; 80 | } 81 | } 82 | 83 | bool SodaqUBeeU201::extraModemSetup(void) { 84 | bool success = gsmModem.init(); 85 | gsmClient.init(&gsmModem); 86 | _modemName = gsmModem.getModemName(); 87 | // Turn on network indicator light 88 | // Pin 16 = GPIO1, function 2 = network status indication 89 | gsmModem.sendAT(GF("+UGPIOC=16,2")); 90 | gsmModem.waitResponse(); 91 | return success; 92 | } 93 | -------------------------------------------------------------------------------- /src/sensors/AOSongAM2315.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AOSongAM2315.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the AOSongAM2315 class. 9 | */ 10 | 11 | #include "AOSongAM2315.h" 12 | 13 | 14 | // The constructor - because this is I2C, only need the power pin 15 | // This sensor has a set I2C address of 0XB8 16 | AOSongAM2315::AOSongAM2315(TwoWire* theI2C, int8_t powerPin, 17 | uint8_t measurementsToAverage) 18 | : Sensor("AOSongAM2315", AM2315_NUM_VARIABLES, AM2315_WARM_UP_TIME_MS, 19 | AM2315_STABILIZATION_TIME_MS, AM2315_MEASUREMENT_TIME_MS, powerPin, 20 | -1, measurementsToAverage), 21 | _i2c(theI2C) { 22 | am2315ptr = new Adafruit_AM2315(_i2c); 23 | } 24 | AOSongAM2315::AOSongAM2315(int8_t powerPin, uint8_t measurementsToAverage) 25 | : Sensor("AOSongAM2315", AM2315_NUM_VARIABLES, AM2315_WARM_UP_TIME_MS, 26 | AM2315_STABILIZATION_TIME_MS, AM2315_MEASUREMENT_TIME_MS, powerPin, 27 | -1, measurementsToAverage, AM2315_INC_CALC_VARIABLES), 28 | _i2c(&Wire) { 29 | am2315ptr = new Adafruit_AM2315(_i2c); 30 | } 31 | AOSongAM2315::~AOSongAM2315() {} 32 | 33 | 34 | String AOSongAM2315::getSensorLocation(void) { 35 | return F("I2C_0xB8"); 36 | } 37 | 38 | 39 | bool AOSongAM2315::setup(void) { 40 | _i2c->begin(); // Start the wire library (sensor power not required) 41 | // Eliminate any potential extra waits in the wire library 42 | // These waits would be caused by a readBytes or parseX being called 43 | // on wire after the Wire buffer has emptied. The default stream 44 | // functions - used by wire - wait a timeout period after reading the 45 | // end of the buffer to see if an interrupt puts something into the 46 | // buffer. In the case of the Wire library, that will never happen and 47 | // the timeout period is a useless delay. 48 | _i2c->setTimeout(0); 49 | return Sensor::setup(); // this will set pin modes and the setup status bit 50 | } 51 | 52 | 53 | bool AOSongAM2315::addSingleMeasurementResult(void) { 54 | // Initialize float variables 55 | float temp_val = -9999; 56 | float humid_val = -9999; 57 | bool ret_val = false; 58 | 59 | // Check a measurement was *successfully* started (status bit 6 set) 60 | // Only go on to get a result if it was 61 | if (bitRead(_sensorStatus, 6)) { 62 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 63 | 64 | ret_val = am2315ptr->readTemperatureAndHumidity(&temp_val, &humid_val); 65 | 66 | if (!ret_val || isnan(temp_val)) temp_val = -9999; 67 | if (!ret_val || isnan(humid_val)) humid_val = -9999; 68 | 69 | MS_DBG(F(" Temp:"), temp_val, F("°C")); 70 | MS_DBG(F(" Humidity:"), humid_val, '%'); 71 | } else { 72 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 73 | } 74 | 75 | verifyAndAddMeasurementResult(AM2315_TEMP_VAR_NUM, temp_val); 76 | verifyAndAddMeasurementResult(AM2315_HUMIDITY_VAR_NUM, humid_val); 77 | 78 | // Unset the time stamp for the beginning of this measurement 79 | _millisMeasurementRequested = 0; 80 | // Unset the status bits for a measurement request (bits 5 & 6) 81 | _sensorStatus &= 0b10011111; 82 | 83 | return ret_val; 84 | } 85 | -------------------------------------------------------------------------------- /src/sensors/AOSongDHT.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AOSongDHT.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the AOSongDHT class. 9 | */ 10 | 11 | #include "AOSongDHT.h" 12 | 13 | 14 | // The constructor - need the power pin, data pin, and type of DHT 15 | AOSongDHT::AOSongDHT(int8_t powerPin, int8_t dataPin, const uint8_t type, 16 | uint8_t measurementsToAverage) 17 | : Sensor("AOSongDHT", DHT_NUM_VARIABLES, DHT_WARM_UP_TIME_MS, 18 | DHT_STABILIZATION_TIME_MS, DHT_MEASUREMENT_TIME_MS, powerPin, 19 | dataPin, measurementsToAverage, DHT_INC_CALC_VARIABLES), 20 | dht_internal(dataPin, type), 21 | _dhtType(type) {} 22 | 23 | // Destructor - does nothing. 24 | AOSongDHT::~AOSongDHT() {} 25 | 26 | 27 | bool AOSongDHT::setup(void) { 28 | dht_internal.begin(); // Start up the sensor (only sets pin modes, sensor 29 | // power not required) 30 | return Sensor::setup(); // this will set pin modes and the setup status bit 31 | } 32 | 33 | 34 | String AOSongDHT::getSensorName(void) { 35 | switch (_dhtType) { 36 | case 11: return "AOSongDHT11"; 37 | case 12: return "AOSongDHT12"; 38 | case 21: return "AOSongDHT21"; // DHT 21 or AM2301 39 | default: return "AOSongDHT22"; 40 | } 41 | } 42 | 43 | 44 | bool AOSongDHT::addSingleMeasurementResult(void) { 45 | bool success = false; 46 | 47 | // Initialize float variables 48 | float humid_val = -9999; 49 | float temp_val = -9999; 50 | float hi_val = -9999; 51 | 52 | // Check a measurement was *successfully* started (status bit 6 set) 53 | // Only go on to get a result if it was 54 | if (bitRead(_sensorStatus, 6)) { 55 | // Reading temperature or humidity takes about 250 milliseconds! 56 | // Make 5 attempts to get a decent reading 57 | for (uint8_t i = 0; i < 5; i++) { 58 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 59 | // First read the humidity 60 | humid_val = dht_internal.readHumidity(); 61 | // Read temperature as Celsius (the default) 62 | temp_val = dht_internal.readTemperature(); 63 | // Check if any reads failed 64 | // If they are NaN (not a number) then something went wrong 65 | if (!isnan(humid_val) && !isnan(temp_val)) { 66 | // Compute heat index in Celsius (isFahreheit = false) 67 | hi_val = dht_internal.computeHeatIndex(temp_val, humid_val, 68 | false); 69 | MS_DBG(F(" Temp:"), temp_val, F("°C")); 70 | MS_DBG(F(" Humidity:"), humid_val, '%'); 71 | MS_DBG(F(" Calculated Heat Index:"), hi_val, F("°C")); 72 | success = true; 73 | break; 74 | } else { 75 | if (i < 4) { 76 | MS_DBG(F(" Failed to read from DHT sensor, Retrying...")); 77 | delay(100); 78 | } else { 79 | MS_DBG(F(" Failed to read from DHT sensor!")); 80 | if (isnan(humid_val)) humid_val = -9999; 81 | if (isnan(temp_val)) temp_val = -9999; 82 | } 83 | } 84 | } 85 | } else { 86 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 87 | } 88 | 89 | // Store the results in the sensorValues array 90 | verifyAndAddMeasurementResult(DHT_TEMP_VAR_NUM, temp_val); 91 | verifyAndAddMeasurementResult(DHT_HUMIDITY_VAR_NUM, humid_val); 92 | verifyAndAddMeasurementResult(DHT_HI_VAR_NUM, hi_val); 93 | 94 | // Unset the time stamp for the beginning of this measurement 95 | _millisMeasurementRequested = 0; 96 | // Unset the status bits for a measurement request (bits 5 & 6) 97 | _sensorStatus &= 0b10011111; 98 | 99 | return success; 100 | } 101 | -------------------------------------------------------------------------------- /src/sensors/AlphasenseCO2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AlphasenseCO2.cpp 3 | * @copyright 2017-2022 Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino 5 | * @copyright 2017-2023 Stroud Water Research Center 6 | * Part of the EnviroDIY ModularSensors library for Arduino 7 | * @author Written by Anthony Aufdenkampe 8 | * and Bella Henkel 9 | * Adapted from ApogeeSQ212.h and 10 | * https://github.com/bellahenkel/Soil-Sensing-Device 11 | * 12 | * @brief Implements the AlphasenseCO2 class. 13 | */ 14 | 15 | 16 | #include "AlphasenseCO2.h" 17 | #include 18 | 19 | 20 | // The constructor - need the power pin and the data pin 21 | AlphasenseCO2::AlphasenseCO2(int8_t powerPin, aco2_adsDiffMux_t adsDiffMux, 22 | uint8_t i2cAddress, uint8_t measurementsToAverage) 23 | : Sensor("AlphasenseCO2", ALPHASENSE_CO2_NUM_VARIABLES, 24 | ALPHASENSE_CO2_WARM_UP_TIME_MS, 25 | ALPHASENSE_CO2_STABILIZATION_TIME_MS, 26 | ALPHASENSE_CO2_MEASUREMENT_TIME_MS, powerPin, -1, 27 | measurementsToAverage, ALPHASENSE_CO2_INC_CALC_VARIABLES), 28 | _adsDiffMux(adsDiffMux), 29 | _i2cAddress(i2cAddress) {} 30 | 31 | // Destructor 32 | AlphasenseCO2::~AlphasenseCO2() {} 33 | 34 | 35 | String AlphasenseCO2::getSensorLocation(void) { 36 | #ifndef MS_USE_ADS1015 37 | String sensorLocation = F("ADS1115_0x"); 38 | #else 39 | String sensorLocation = F("ADS1015_0x"); 40 | #endif 41 | sensorLocation += String(_i2cAddress, HEX); 42 | sensorLocation += F("; differential between channels 2 and 3"); 43 | return sensorLocation; 44 | } 45 | 46 | 47 | bool AlphasenseCO2::addSingleMeasurementResult(void) { 48 | // Variables to store the results in 49 | int16_t adcCounts = -9999; 50 | float adcVoltage = -9999; 51 | float co2Current = -9999; 52 | float calibResult = -9999; 53 | 54 | // Check a measurement was *successfully* started (status bit 6 set) 55 | // Only go on to get a result if it was 56 | if (bitRead(_sensorStatus, 6)) { 57 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 58 | 59 | // Create an Auxillary ADD object 60 | // We create and set up the ADC object here so that each sensor using 61 | // the ADC may set the gain appropriately without effecting others. 62 | #ifndef MS_USE_ADS1015 63 | Adafruit_ADS1115 ads; // Use this for the 16-bit version 64 | #else 65 | Adafruit_ADS1015 ads; // Use this for the 12-bit version 66 | #endif 67 | // ADS Library default settings: 68 | // - TI1115 (16 bit) 69 | // - single-shot mode (powers down between conversions) 70 | // - 128 samples per second (8ms conversion time) 71 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 72 | // - TI1015 (12 bit) 73 | // - single-shot mode (powers down between conversions) 74 | // - 1600 samples per second (625µs conversion time) 75 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 76 | 77 | // Bump the gain up to 1x = +/- 4.096V range 78 | // Sensor return range is 0-2.5V, but the next gain option is 2x which 79 | // only allows up to 2.048V 80 | ads.setGain(GAIN_ONE); 81 | // Begin ADC 82 | ads.begin(_i2cAddress); 83 | 84 | // Read Analog to Digital Converter (ADC) 85 | // Taking this reading includes the 8ms conversion delay. 86 | // Measure the voltage differential across the two voltage pins 87 | adcCounts = ads.readADC_Differential_2_3(); 88 | // Convert ADC counts value to voltage (V) 89 | adcVoltage = ads.computeVolts(adcCounts); 90 | MS_DBG(F(" ads.readADC_Differential_2_3() converted to volts:"), 91 | adcVoltage); 92 | 93 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 94 | // Skip results out of range 95 | // Convert voltage to current (mA) - assuming a 250 Ohm resistor is 96 | // in series 97 | co2Current = (adcVoltage / 250) * 1000; 98 | // Convert current to ppm (using a formula recommended by the sensor 99 | // manufacturer) 100 | calibResult = 312.5 * co2Current - 1250; 101 | MS_DBG(F(" calibResult:"), calibResult); 102 | } else { 103 | // set invalid voltages back to -9999 104 | adcVoltage = -9999; 105 | } 106 | } else { 107 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 108 | } 109 | 110 | verifyAndAddMeasurementResult(ALPHASENSE_CO2_VAR_NUM, calibResult); 111 | verifyAndAddMeasurementResult(ALPHASENSE_CO2_VOLTAGE_VAR_NUM, adcVoltage); 112 | 113 | // Unset the time stamp for the beginning of this measurement 114 | _millisMeasurementRequested = 0; 115 | // Unset the status bits for a measurement request (bits 5 & 6) 116 | _sensorStatus &= 0b10011111; 117 | 118 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 119 | return true; 120 | } else { 121 | return false; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/sensors/AnalogElecConductivity.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AnalogElecConductivity.cpp 3 | * @copyright Stroud Water Research Center and Neil Hancock 4 | * Part of the EnviroDIY ModularSensors library 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Neil Hancock ; Edited by Sara 7 | * Geleskie Damiano 8 | * 9 | * @brief This encapsulates an Electrical Conductivity sensors using an anlog 10 | * input and onboard ADC and ADC ref. 11 | */ 12 | 13 | #include "AnalogElecConductivity.h" 14 | 15 | // For Mayfly version; the battery resistor depends on it 16 | AnalogElecConductivity::AnalogElecConductivity(int8_t powerPin, int8_t dataPin, 17 | float Rseries_ohms, 18 | float sensorEC_Konst, 19 | uint8_t measurementsToAverage) 20 | : Sensor("AnalogElecConductivity", ANALOGELECCONDUCTIVITY_NUM_VARIABLES, 21 | ANALOGELECCONDUCTIVITY_WARM_UP_TIME_MS, 22 | ANALOGELECCONDUCTIVITY_STABILIZATION_TIME_MS, 23 | ANALOGELECCONDUCTIVITY_MEASUREMENT_TIME_MS, powerPin, dataPin, 24 | measurementsToAverage, ANALOGELECCONDUCTIVITY_INC_CALC_VARIABLES), 25 | _Rseries_ohms(Rseries_ohms), 26 | _sensorEC_Konst(sensorEC_Konst) {} 27 | // Destructor 28 | AnalogElecConductivity::~AnalogElecConductivity() {} 29 | 30 | String AnalogElecConductivity::getSensorLocation(void) { 31 | String sensorLocation = F("anlgEc Proc Data/Pwr"); 32 | sensorLocation += String(_dataPin) + "/" + String(_powerPin); 33 | return sensorLocation; 34 | } 35 | 36 | 37 | float AnalogElecConductivity::readEC() { 38 | return readEC(_dataPin); 39 | } 40 | 41 | 42 | float AnalogElecConductivity::readEC(uint8_t analogPinNum) { 43 | uint32_t sensorEC_adc; 44 | float Rwater_ohms; // literal value of water 45 | float EC_uScm = -9999; // units are uS per cm 46 | 47 | // Set the resolution for the processor ADC, only applies to SAMD boards. 48 | #if !defined(ARDUINO_ARCH_AVR) 49 | analogReadResolution(ANALOG_EC_ADC_RESOLUTION); 50 | #endif // ARDUINO_ARCH_AVR 51 | // Set the analog reference mode for the voltage measurement. 52 | // If possible, to get the best results, an external reference should be 53 | // used. 54 | analogReference(ANALOG_EC_ADC_REFERENCE_MODE); 55 | 56 | // First measure the analog voltage. 57 | // The return value from analogRead() is IN BITS NOT IN VOLTS!! 58 | // Take a priming reading. 59 | // First reading will be low - discard 60 | analogRead(analogPinNum); 61 | // Take the reading we'll keep 62 | sensorEC_adc = analogRead(analogPinNum); 63 | MS_DEEP_DBG("adc bits=", sensorEC_adc); 64 | 65 | if (0 == sensorEC_adc) { 66 | // Prevent underflow, can never be ANALOG_EC_ADC_RANGE 67 | sensorEC_adc = 1; 68 | } 69 | 70 | // Estimate Resistance of Liquid 71 | 72 | // see the header for an explanation of this calculation 73 | Rwater_ohms = _Rseries_ohms / 74 | ((static_cast(ANALOG_EC_ADC_RANGE) / 75 | static_cast(sensorEC_adc)) - 76 | 1); 77 | MS_DEEP_DBG("ohms=", Rwater_ohms); 78 | 79 | // Convert to EC 80 | EC_uScm = 1000000 / (Rwater_ohms * _sensorEC_Konst); 81 | MS_DEEP_DBG("cond=", EC_uScm); 82 | 83 | return EC_uScm; 84 | } 85 | 86 | 87 | bool AnalogElecConductivity::addSingleMeasurementResult(void) { 88 | float sensorEC_uScm = -9999; 89 | 90 | if (bitRead(_sensorStatus, 6)) { 91 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 92 | 93 | sensorEC_uScm = readEC(_dataPin); 94 | MS_DBG(F("Water EC (uSm/cm)"), sensorEC_uScm); 95 | } else { 96 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 97 | } 98 | 99 | verifyAndAddMeasurementResult(ANALOGELECCONDUCTIVITY_EC_VAR_NUM, 100 | sensorEC_uScm); 101 | 102 | // Unset the time stamp for the beginning of this measurement 103 | _millisMeasurementRequested = 0; 104 | // Unset the status bits for a measurement request (bits 5 & 6) 105 | _sensorStatus &= 0b10011111; 106 | 107 | // Return true when finished 108 | return true; 109 | } 110 | -------------------------------------------------------------------------------- /src/sensors/ApogeeSQ212.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ApogeeSQ212.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Anthony Aufdenkampe 7 | * Edited by Sara Geleskie Damiano 8 | * Adapted from CampbellOBS3.cpp by Sara Geleskie Damiano 9 | * 10 | * 11 | * @brief Implements the ApogeeSQ212 class. 12 | */ 13 | 14 | 15 | #include "ApogeeSQ212.h" 16 | #include 17 | 18 | 19 | // The constructor - need the power pin and the data pin 20 | ApogeeSQ212::ApogeeSQ212(int8_t powerPin, uint8_t adsChannel, 21 | uint8_t i2cAddress, uint8_t measurementsToAverage) 22 | : Sensor("ApogeeSQ212", SQ212_NUM_VARIABLES, SQ212_WARM_UP_TIME_MS, 23 | SQ212_STABILIZATION_TIME_MS, SQ212_MEASUREMENT_TIME_MS, powerPin, 24 | -1, measurementsToAverage, SQ212_INC_CALC_VARIABLES), 25 | _adsChannel(adsChannel), 26 | _i2cAddress(i2cAddress) {} 27 | 28 | // Destructor 29 | ApogeeSQ212::~ApogeeSQ212() {} 30 | 31 | 32 | String ApogeeSQ212::getSensorLocation(void) { 33 | #ifndef MS_USE_ADS1015 34 | String sensorLocation = F("ADS1115_0x"); 35 | #else 36 | String sensorLocation = F("ADS1015_0x"); 37 | #endif 38 | sensorLocation += String(_i2cAddress, HEX); 39 | sensorLocation += F("_Channel"); 40 | sensorLocation += String(_adsChannel); 41 | return sensorLocation; 42 | } 43 | 44 | 45 | bool ApogeeSQ212::addSingleMeasurementResult(void) { 46 | // Variables to store the results in 47 | int16_t adcCounts = -9999; 48 | float adcVoltage = -9999; 49 | float calibResult = -9999; 50 | 51 | // Check a measurement was *successfully* started (status bit 6 set) 52 | // Only go on to get a result if it was 53 | if (bitRead(_sensorStatus, 6)) { 54 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 55 | 56 | // Create an Auxillary ADD object 57 | // We create and set up the ADC object here so that each sensor using 58 | // the ADC may set the gain appropriately without effecting others. 59 | #ifndef MS_USE_ADS1015 60 | Adafruit_ADS1115 ads; // Use this for the 16-bit version 61 | #else 62 | Adafruit_ADS1015 ads; // Use this for the 12-bit version 63 | #endif 64 | // ADS Library default settings: 65 | // - TI1115 (16 bit) 66 | // - single-shot mode (powers down between conversions) 67 | // - 128 samples per second (8ms conversion time) 68 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 69 | // - TI1015 (12 bit) 70 | // - single-shot mode (powers down between conversions) 71 | // - 1600 samples per second (625µs conversion time) 72 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 73 | 74 | // Bump the gain up to 1x = +/- 4.096V range 75 | // Sensor return range is 0-2.5V, but the next gain option is 2x which 76 | // only allows up to 2.048V 77 | ads.setGain(GAIN_ONE); 78 | // Begin ADC 79 | ads.begin(_i2cAddress); 80 | 81 | // Read Analog to Digital Converter (ADC) 82 | // Taking this reading includes the 8ms conversion delay. 83 | // Measure the ADC raw count 84 | adcCounts = ads.readADC_SingleEnded(_adsChannel); 85 | // Convert ADC raw counts value to voltage (V) 86 | adcVoltage = ads.computeVolts(adcCounts); 87 | MS_DBG(F(" ads.readADC_SingleEnded("), _adsChannel, F("):"), 88 | adcVoltage); 89 | 90 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 91 | // Skip results out of range 92 | // Apogee SQ-212 Calibration Factor = 1.0 μmol m-2 s-1 per mV 93 | calibResult = 1000 * adcVoltage * SQ212_CALIBRATION_FACTOR; 94 | MS_DBG(F(" calibResult:"), calibResult); 95 | } else { 96 | // set invalid voltages back to -9999 97 | adcVoltage = -9999; 98 | } 99 | } else { 100 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 101 | } 102 | 103 | verifyAndAddMeasurementResult(SQ212_PAR_VAR_NUM, calibResult); 104 | verifyAndAddMeasurementResult(SQ212_VOLTAGE_VAR_NUM, adcVoltage); 105 | 106 | // Unset the time stamp for the beginning of this measurement 107 | _millisMeasurementRequested = 0; 108 | // Unset the status bits for a measurement request (bits 5 & 6) 109 | _sensorStatus &= 0b10011111; 110 | 111 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 112 | return true; 113 | } else { 114 | return false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/sensors/AtlasScientificCO2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtlasScientificCO2.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Initial developement for Atlas Sensors was done by Adam Gold 7 | * Files were edited by Sara Damiano 8 | * 9 | * @brief Implements the AtlasScientificCO2 class. 10 | */ 11 | 12 | // Included Dependencies 13 | #include "AtlasScientificCO2.h" 14 | 15 | // Constructors 16 | AtlasScientificCO2::AtlasScientificCO2(TwoWire* theI2C, int8_t powerPin, 17 | uint8_t i2cAddressHex, 18 | uint8_t measurementsToAverage) 19 | : AtlasParent(theI2C, powerPin, i2cAddressHex, measurementsToAverage, 20 | "AtlasScientificCO2", ATLAS_CO2_NUM_VARIABLES, 21 | ATLAS_CO2_WARM_UP_TIME_MS, ATLAS_CO2_STABILIZATION_TIME_MS, 22 | ATLAS_CO2_MEASUREMENT_TIME_MS, ATLAS_CO2_INC_CALC_VARIABLES) { 23 | } 24 | AtlasScientificCO2::AtlasScientificCO2(int8_t powerPin, uint8_t i2cAddressHex, 25 | uint8_t measurementsToAverage) 26 | : AtlasParent(powerPin, i2cAddressHex, measurementsToAverage, 27 | "AtlasScientificCO2", ATLAS_CO2_NUM_VARIABLES, 28 | ATLAS_CO2_WARM_UP_TIME_MS, ATLAS_CO2_STABILIZATION_TIME_MS, 29 | ATLAS_CO2_MEASUREMENT_TIME_MS, ATLAS_CO2_INC_CALC_VARIABLES) { 30 | } 31 | 32 | // Destructor 33 | AtlasScientificCO2::~AtlasScientificCO2() {} 34 | 35 | 36 | // Setup 37 | bool AtlasScientificCO2::setup() { 38 | bool success = 39 | Sensor::setup(); // this will set pin modes and the setup status bit 40 | 41 | // This sensor needs power for setup! 42 | // We want to turn on all possible measurement parameters 43 | bool wasOn = checkPowerOn(); 44 | if (!wasOn) { powerUp(); } 45 | waitForWarmUp(); 46 | 47 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 48 | F("to report temperature with CO2")); 49 | _i2c->beginTransmission(_i2cAddressHex); 50 | // Enable temperature 51 | success &= static_cast(_i2c->write((const uint8_t*)"O,t,1", 5)); 52 | success &= !static_cast(_i2c->endTransmission()); 53 | // NOTE: The return of 0 from endTransmission indicates success 54 | success &= waitForProcessing(); 55 | 56 | if (!success) { 57 | // Set the status error bit (bit 7) 58 | _sensorStatus |= 0b10000000; 59 | // UN-set the set-up bit (bit 0) since setup failed! 60 | _sensorStatus &= 0b11111110; 61 | } 62 | 63 | // Turn the power back off it it had been turned on 64 | if (!wasOn) { powerDown(); } 65 | 66 | return success; 67 | } 68 | -------------------------------------------------------------------------------- /src/sensors/AtlasScientificDO.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtlasScientificDO.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Initial developement for Atlas Sensors was done by Adam Gold 7 | * Files were edited by Sara Damiano 8 | * 9 | * @brief Implements the AtlasScientificCO2 class. 10 | */ 11 | 12 | // Included Dependencies 13 | #include "AtlasScientificDO.h" 14 | 15 | // Constructors 16 | AtlasScientificDO::AtlasScientificDO(TwoWire* theI2C, int8_t powerPin, 17 | uint8_t i2cAddressHex, 18 | uint8_t measurementsToAverage) 19 | : AtlasParent(theI2C, powerPin, i2cAddressHex, measurementsToAverage, 20 | "AtlasScientificDO", ATLAS_DO_NUM_VARIABLES, 21 | ATLAS_DO_WARM_UP_TIME_MS, ATLAS_DO_STABILIZATION_TIME_MS, 22 | ATLAS_DO_MEASUREMENT_TIME_MS, ATLAS_DO_INC_CALC_VARIABLES) {} 23 | AtlasScientificDO::AtlasScientificDO(int8_t powerPin, uint8_t i2cAddressHex, 24 | uint8_t measurementsToAverage) 25 | : AtlasParent(powerPin, i2cAddressHex, measurementsToAverage, 26 | "AtlasScientificDO", ATLAS_DO_NUM_VARIABLES, 27 | ATLAS_DO_WARM_UP_TIME_MS, ATLAS_DO_STABILIZATION_TIME_MS, 28 | ATLAS_DO_MEASUREMENT_TIME_MS, ATLAS_DO_INC_CALC_VARIABLES) {} 29 | 30 | // Destructor 31 | AtlasScientificDO::~AtlasScientificDO() {} 32 | 33 | 34 | // Setup 35 | bool AtlasScientificDO::setup() { 36 | bool success = 37 | Sensor::setup(); // this will set pin modes and the setup status bit 38 | 39 | // This sensor needs power for setup! 40 | // We want to turn on all possible measurement parameters 41 | bool wasOn = checkPowerOn(); 42 | if (!wasOn) { powerUp(); } 43 | waitForWarmUp(); 44 | 45 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 46 | F("to report O2 concentration")); 47 | _i2c->beginTransmission(_i2cAddressHex); 48 | // Enable concentration in mg/L 49 | success &= static_cast(_i2c->write((const uint8_t*)"O,mg,1", 6)); 50 | success &= !static_cast(_i2c->endTransmission()); 51 | success &= waitForProcessing(); 52 | 53 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 54 | F("to report O2 % saturation")); 55 | _i2c->beginTransmission(_i2cAddressHex); 56 | // Enable percent saturation 57 | success &= static_cast(_i2c->write((const uint8_t*)"O,%,1", 5)); 58 | success &= !static_cast(_i2c->endTransmission()); 59 | success &= waitForProcessing(); 60 | 61 | if (!success) { 62 | // Set the status error bit (bit 7) 63 | _sensorStatus |= 0b10000000; 64 | // UN-set the set-up bit (bit 0) since setup failed! 65 | _sensorStatus &= 0b11111110; 66 | } 67 | 68 | // Turn the power back off it it had been turned on 69 | if (!wasOn) { powerDown(); } 70 | 71 | return success; 72 | } 73 | -------------------------------------------------------------------------------- /src/sensors/AtlasScientificEC.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file AtlasScientificEC.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Initial developement for Atlas Sensors was done by Adam Gold 7 | * Files were edited by Sara Damiano 8 | * 9 | * @brief Implements the AtlasScientificEC class. 10 | */ 11 | 12 | // Included Dependencies 13 | #include "AtlasScientificEC.h" 14 | 15 | // Constructors 16 | AtlasScientificEC::AtlasScientificEC(TwoWire* theI2C, int8_t powerPin, 17 | uint8_t i2cAddressHex, 18 | uint8_t measurementsToAverage) 19 | : AtlasParent(theI2C, powerPin, i2cAddressHex, measurementsToAverage, 20 | "AtlasScientificEC", ATLAS_COND_NUM_VARIABLES, 21 | ATLAS_COND_WARM_UP_TIME_MS, ATLAS_COND_STABILIZATION_TIME_MS, 22 | ATLAS_COND_MEASUREMENT_TIME_MS, 23 | ATLAS_COND_INC_CALC_VARIABLES) {} 24 | AtlasScientificEC::AtlasScientificEC(int8_t powerPin, uint8_t i2cAddressHex, 25 | uint8_t measurementsToAverage) 26 | : AtlasParent(powerPin, i2cAddressHex, measurementsToAverage, 27 | "AtlasScientificEC", ATLAS_COND_NUM_VARIABLES, 28 | ATLAS_COND_WARM_UP_TIME_MS, ATLAS_COND_STABILIZATION_TIME_MS, 29 | ATLAS_COND_MEASUREMENT_TIME_MS, 30 | ATLAS_COND_INC_CALC_VARIABLES) {} 31 | 32 | // Destructor 33 | AtlasScientificEC::~AtlasScientificEC() {} 34 | 35 | 36 | // Setup 37 | bool AtlasScientificEC::setup() { 38 | bool success = 39 | Sensor::setup(); // this will set pin modes and the setup status bit 40 | 41 | // This sensor needs power for setup! 42 | // We want to turn on all possible measurement parameters 43 | bool wasOn = checkPowerOn(); 44 | if (!wasOn) { powerUp(); } 45 | waitForWarmUp(); 46 | 47 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 48 | F("to report conductivity")); 49 | _i2c->beginTransmission(_i2cAddressHex); 50 | // Enable conductivity 51 | success &= static_cast(_i2c->write((const uint8_t*)"O,EC,1", 6)); 52 | success &= !static_cast(_i2c->endTransmission()); 53 | success &= waitForProcessing(); 54 | 55 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 56 | F("to report total dissolved solids")); 57 | _i2c->beginTransmission(_i2cAddressHex); 58 | // Enable total dissolved solids 59 | success &= static_cast(_i2c->write((const uint8_t*)"O,TDS,1", 7)); 60 | success &= !static_cast(_i2c->endTransmission()); 61 | success &= waitForProcessing(); 62 | 63 | MS_DBG(F("Asking"), getSensorNameAndLocation(), F("to report salinity")); 64 | _i2c->beginTransmission(_i2cAddressHex); 65 | // Enable salinity 66 | success &= static_cast(_i2c->write((const uint8_t*)"O,S,1", 5)); 67 | success &= !static_cast(_i2c->endTransmission()); 68 | success &= waitForProcessing(); 69 | 70 | MS_DBG(F("Asking"), getSensorNameAndLocation(), 71 | F("to report specific gravity")); 72 | _i2c->beginTransmission(_i2cAddressHex); 73 | // Enable specific gravity 74 | success &= static_cast(_i2c->write((const uint8_t*)"O,SG,1", 6)); 75 | success &= !static_cast(_i2c->endTransmission()); 76 | success &= waitForProcessing(); 77 | 78 | if (!success) { 79 | // Set the status error bit (bit 7) 80 | _sensorStatus |= 0b10000000; 81 | // UN-set the set-up bit (bit 0) since setup failed! 82 | _sensorStatus &= 0b11111110; 83 | } 84 | 85 | // Turn the power back off it it had been turned on 86 | if (!wasOn) { powerDown(); } 87 | 88 | return success; 89 | } 90 | -------------------------------------------------------------------------------- /src/sensors/CampbellOBS3.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CampbellOBS3.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the CampbellOBS3 class. 9 | */ 10 | 11 | 12 | #include "CampbellOBS3.h" 13 | #include 14 | 15 | 16 | // The constructor - need the power pin, the data pin, and the calibration info 17 | CampbellOBS3::CampbellOBS3(int8_t powerPin, uint8_t adsChannel, 18 | float x2_coeff_A, float x1_coeff_B, float x0_coeff_C, 19 | uint8_t i2cAddress, uint8_t measurementsToAverage) 20 | : Sensor("CampbellOBS3", OBS3_NUM_VARIABLES, OBS3_WARM_UP_TIME_MS, 21 | OBS3_STABILIZATION_TIME_MS, OBS3_MEASUREMENT_TIME_MS, powerPin, -1, 22 | measurementsToAverage, OBS3_INC_CALC_VARIABLES), 23 | _adsChannel(adsChannel), 24 | _x2_coeff_A(x2_coeff_A), 25 | _x1_coeff_B(x1_coeff_B), 26 | _x0_coeff_C(x0_coeff_C), 27 | _i2cAddress(i2cAddress) {} 28 | // Destructor 29 | CampbellOBS3::~CampbellOBS3() {} 30 | 31 | 32 | String CampbellOBS3::getSensorLocation(void) { 33 | #ifndef MS_USE_ADS1015 34 | String sensorLocation = F("ADS1115_0x"); 35 | #else 36 | String sensorLocation = F("ADS1015_0x"); 37 | #endif 38 | sensorLocation += String(_i2cAddress, HEX); 39 | sensorLocation += F("_Channel"); 40 | sensorLocation += String(_adsChannel); 41 | return sensorLocation; 42 | } 43 | 44 | 45 | bool CampbellOBS3::addSingleMeasurementResult(void) { 46 | // Variables to store the results in 47 | int16_t adcCounts = -9999; 48 | float adcVoltage = -9999; 49 | float calibResult = -9999; 50 | 51 | // Check a measurement was *successfully* started (status bit 6 set) 52 | // Only go on to get a result if it was 53 | if (bitRead(_sensorStatus, 6)) { 54 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 55 | 56 | // Create an Auxillary ADD object 57 | // We create and set up the ADC object here so that each sensor using 58 | // the ADC may set the gain appropriately without effecting others. 59 | #ifndef MS_USE_ADS1015 60 | Adafruit_ADS1115 ads; // Use this for the 16-bit version 61 | #else 62 | Adafruit_ADS1015 ads; // Use this for the 12-bit version 63 | #endif 64 | // ADS Library default settings: 65 | // - TI1115 (16 bit) 66 | // - single-shot mode (powers down between conversions) 67 | // - 128 samples per second (8ms conversion time) 68 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 69 | // - TI1015 (12 bit) 70 | // - single-shot mode (powers down between conversions) 71 | // - 1600 samples per second (625µs conversion time) 72 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 73 | 74 | // Bump the gain up to 1x = +/- 4.096V range 75 | // Sensor return range is 0-2.5V, but the next gain option is 2x which 76 | // only allows up to 2.048V 77 | ads.setGain(GAIN_ONE); 78 | // Begin ADC 79 | ads.begin(_i2cAddress); 80 | 81 | // Print out the calibration curve 82 | MS_DBG(F(" Input calibration Curve:"), _x2_coeff_A, F("x^2 +"), 83 | _x1_coeff_B, F("x +"), _x0_coeff_C); 84 | 85 | // Read Analog to Digital Converter (ADC) 86 | // Taking this reading includes the 8ms conversion delay. 87 | // Measure the ADC raw count 88 | adcCounts = ads.readADC_SingleEnded(_adsChannel); 89 | // Convert ADC raw counts value to voltage (V) 90 | adcVoltage = ads.computeVolts(adcCounts); 91 | MS_DBG(F(" ads.readADC_SingleEnded("), _adsChannel, F("):"), 92 | adcVoltage); 93 | 94 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 95 | // Skip results out of range 96 | // Apply the unique calibration curve for the given sensor 97 | calibResult = (_x2_coeff_A * sq(adcVoltage)) + 98 | (_x1_coeff_B * adcVoltage) + _x0_coeff_C; 99 | MS_DBG(F(" calibResult:"), calibResult); 100 | } else { // set invalid voltages back to -9999 101 | adcVoltage = -9999; 102 | } 103 | } else { 104 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 105 | } 106 | 107 | verifyAndAddMeasurementResult(OBS3_TURB_VAR_NUM, calibResult); 108 | verifyAndAddMeasurementResult(OBS3_VOLTAGE_VAR_NUM, adcVoltage); 109 | 110 | // Unset the time stamp for the beginning of this measurement 111 | _millisMeasurementRequested = 0; 112 | // Unset the status bits for a measurement request (bits 5 & 6) 113 | _sensorStatus &= 0b10011111; 114 | 115 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 116 | return true; 117 | } else { 118 | return false; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/sensors/Decagon5TM.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Decagon5TM.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the Decagon5TM class. 9 | */ 10 | 11 | #include "Decagon5TM.h" 12 | 13 | bool Decagon5TM::getResults(void) { 14 | // Set up the float variables for receiving data 15 | float ea = -9999; 16 | float temp = -9999; 17 | 18 | // Check if this the currently active SDI-12 Object 19 | bool wasActive = _SDI12Internal.isActive(); 20 | // If it wasn't active, activate it now. 21 | // Use begin() instead of just setActive() to ensure timer is set 22 | // correctly. 23 | if (!wasActive) _SDI12Internal.begin(); 24 | // Empty the buffer 25 | _SDI12Internal.clearBuffer(); 26 | 27 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 28 | String getDataCommand = ""; 29 | getDataCommand += _SDI12address; 30 | // SDI-12 command to get data [address][D][dataOption][!] 31 | getDataCommand += "D0!"; 32 | _SDI12Internal.sendCommand(getDataCommand, _extraWakeTime); 33 | delay(30); // It just needs this little delay 34 | MS_DEEP_DBG(F(" >>>"), getDataCommand); 35 | 36 | // Wait for the first few charaters to arrive. The response from a data 37 | // request should always have more than three characters 38 | uint32_t start = millis(); 39 | while (_SDI12Internal.available() < 3 && (millis() - start) < 1500) { 40 | // wait 41 | } 42 | // read the returned address to remove it from the buffer 43 | auto returnedAddress = static_cast(_SDI12Internal.read()); 44 | // print out a warning if the address doesn't match up 45 | if (returnedAddress != _SDI12address) { 46 | MS_DBG(F("Warning, expecting data from"), _SDI12address, 47 | F("but got data from"), returnedAddress); 48 | } 49 | // Start printing out the returned data 50 | MS_DEEP_DBG(F(" <<<"), returnedAddress); 51 | 52 | // read the '+' out of the buffer, and print it if we're debugging 53 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 54 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 55 | #else 56 | // if we're not debugging, just read the character to make sure 57 | // it's removed from the buffer 58 | _SDI12Internal.read(); 59 | #endif 60 | 61 | // First variable returned is the Dialectric E 62 | ea = _SDI12Internal.parseFloat(SKIP_NONE); 63 | MS_DEEP_DBG(F(" <<<"), String(ea, 10)); 64 | 65 | // read the next '+' out of the buffer 66 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 67 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 68 | #else 69 | _SDI12Internal.read(); 70 | #endif 71 | 72 | // Now read the temperature 73 | temp = _SDI12Internal.parseFloat(SKIP_NONE); 74 | MS_DEEP_DBG(F(" <<<"), String(temp, 10)); 75 | 76 | // read and dump anything else 77 | while (_SDI12Internal.available()) { 78 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 79 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 80 | #else 81 | _SDI12Internal.read(); 82 | #endif 83 | } 84 | 85 | // Empty the buffer again 86 | _SDI12Internal.clearBuffer(); 87 | 88 | // De-activate the SDI-12 Object 89 | // Use end() instead of just forceHold to un-set the timers 90 | if (!wasActive) _SDI12Internal.end(); 91 | 92 | MS_DBG(F("Raw dielectric permittivity:"), ea); 93 | MS_DBG(F("Raw Temperature Value:"), temp); 94 | 95 | 96 | // Set up the float variables for calculated variable 97 | float VWC = -9999; 98 | 99 | 100 | // Calculate the VWC from EA using the Topp equation 101 | // range check 102 | if (ea < 0 || ea > 350) { 103 | MS_DBG(F("WARNING: Ea results out of range (0-350)! Cannot calculate " 104 | "VWC")); 105 | ea = -9999; 106 | } 107 | // calculate 108 | if (ea != -9999) { 109 | VWC = (4.3e-6 * (ea * ea * ea)) - (5.5e-4 * (ea * ea)) + 110 | (2.92e-2 * ea) - 5.3e-2; 111 | VWC *= 100; // Convert to actual percent 112 | MS_DBG(F("Calculated VWC:"), ea); 113 | if (VWC < 0) { 114 | VWC = 0; 115 | MS_DBG(F("Setting negative VWC to 0")); 116 | } 117 | if (VWC > 100) { 118 | VWC = 100; 119 | MS_DBG(F("Setting VWC >100 to 100")); 120 | } 121 | } 122 | 123 | // VWC = 3.879e-4*raw-0.6956; // equation for mineral soils 124 | 125 | // range check on temp; range is - 40°C to + 50°C 126 | if (temp < -50 || temp > 60) { 127 | temp = -9999; 128 | MS_DBG(F("WARNING: temperature results out of range (-50-60)!")); 129 | } 130 | 131 | verifyAndAddMeasurementResult(TM_TEMP_VAR_NUM, temp); 132 | verifyAndAddMeasurementResult(TM_EA_VAR_NUM, ea); 133 | verifyAndAddMeasurementResult(TM_VWC_VAR_NUM, VWC); 134 | 135 | return temp != -9999; 136 | } 137 | -------------------------------------------------------------------------------- /src/sensors/EverlightALSPT19.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file EverlightALSPT19.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the EverlightALSPT19 class. 9 | */ 10 | 11 | #include "EverlightALSPT19.h" 12 | 13 | 14 | // The constructor - because this is I2C, only need the power pin 15 | // This sensor has a set I2C address of 0XB8 16 | EverlightALSPT19::EverlightALSPT19(int8_t powerPin, int8_t dataPin, 17 | float supplyVoltage, float loadResistor, 18 | uint8_t measurementsToAverage) 19 | : Sensor("Everlight ALS-PT19", ALSPT19_NUM_VARIABLES, 20 | ALSPT19_WARM_UP_TIME_MS, ALSPT19_STABILIZATION_TIME_MS, 21 | ALSPT19_MEASUREMENT_TIME_MS, powerPin, dataPin, 22 | measurementsToAverage), 23 | _supplyVoltage(supplyVoltage), 24 | _loadResistor(loadResistor) {} 25 | EverlightALSPT19::EverlightALSPT19(uint8_t measurementsToAverage) 26 | : Sensor("Everlight ALS-PT19", ALSPT19_NUM_VARIABLES, 27 | ALSPT19_WARM_UP_TIME_MS, ALSPT19_STABILIZATION_TIME_MS, 28 | ALSPT19_MEASUREMENT_TIME_MS, MAYFLY_ALS_POWER_PIN, 29 | MAYFLY_ALS_DATA_PIN, measurementsToAverage, 30 | ALSPT19_INC_CALC_VARIABLES), 31 | _supplyVoltage(MAYFLY_ALS_SUPPLY_VOLTAGE), 32 | _loadResistor(MAYFLY_ALS_LOADING_RESISTANCE) {} 33 | EverlightALSPT19::~EverlightALSPT19() {} 34 | 35 | 36 | bool EverlightALSPT19::addSingleMeasurementResult(void) { 37 | // Initialize float variables 38 | float volt_val = -9999; 39 | float current_val = -9999; 40 | float lux_val = -9999; 41 | 42 | // Check a measurement was *successfully* started (status bit 6 set) 43 | // Only go on to get a result if it was 44 | if (bitRead(_sensorStatus, 6)) { 45 | // Set the resolution for the processor ADC, only applies to SAMD 46 | // boards. 47 | #if !defined(ARDUINO_ARCH_AVR) 48 | analogReadResolution(ALSPT19_ADC_RESOLUTION); 49 | #endif // ARDUINO_ARCH_AVR 50 | // Set the analog reference mode for the voltage measurement. 51 | // If possible, to get the best results, an external reference should be 52 | // used. 53 | analogReference(ALSPT19_ADC_REFERENCE_MODE); 54 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 55 | 56 | // First measure the analog voltage. 57 | // The return value from analogRead() is IN BITS NOT IN VOLTS!! 58 | // Take a priming reading. 59 | // First reading will be low - discard 60 | analogRead(_dataPin); 61 | // Take the reading we'll keep 62 | uint32_t sensor_adc = analogRead(_dataPin); 63 | MS_DEEP_DBG(" ADC Bits:", sensor_adc); 64 | 65 | if (0 == sensor_adc) { 66 | // Prevent underflow, can never be ALSPT19_ADC_RANGE 67 | sensor_adc = 1; 68 | } 69 | // convert bits to volts 70 | volt_val = (_supplyVoltage / static_cast(ALSPT19_ADC_MAX)) * 71 | static_cast(sensor_adc); 72 | // convert volts to current 73 | // resistance is entered in kΩ and we want µA 74 | current_val = (volt_val / (_loadResistor * 1000)) * 1e6; 75 | // convert current to illuminance 76 | // from sensor datasheet, typical 200µA current for 1000 Lux 77 | lux_val = current_val * (1000. / 200.); 78 | 79 | 80 | MS_DBG(F(" Voltage:"), volt_val, F("V")); 81 | MS_DBG(F(" Current:"), current_val, F("µA")); 82 | MS_DBG(F(" Illuminance:"), lux_val, F("lux")); 83 | } else { 84 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 85 | } 86 | 87 | verifyAndAddMeasurementResult(ALSPT19_VOLTAGE_VAR_NUM, volt_val); 88 | verifyAndAddMeasurementResult(ALSPT19_CURRENT_VAR_NUM, current_val); 89 | verifyAndAddMeasurementResult(ALSPT19_ILLUMINANCE_VAR_NUM, lux_val); 90 | 91 | // Unset the time stamp for the beginning of this measurement 92 | _millisMeasurementRequested = 0; 93 | // Unset the status bits for a measurement request (bits 5 & 6) 94 | _sensorStatus &= 0b10011111; 95 | 96 | return true; 97 | } 98 | -------------------------------------------------------------------------------- /src/sensors/ExternalVoltage.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ExternalVoltage.h * 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Bobby Schulz 7 | * Edited by Sara Geleskie Damiano 8 | * Adapted from CampbellOBS3.h by Sara Geleskie Damiano 9 | * 10 | * 11 | * @brief This file only serves to support backwards compatibility for the 12 | * renamed TIADS1x15. 13 | */ 14 | // Header Guards 15 | #ifndef SRC_SENSORS_EXTERNALVOLTAGE_H_ 16 | #define SRC_SENSORS_EXTERNALVOLTAGE_H_ 17 | 18 | // Included the new file name 19 | #include "TIADS1x15.h" 20 | 21 | #endif // SRC_SENSORS_EXTERNALVOLTAGE_H_ 22 | -------------------------------------------------------------------------------- /src/sensors/FreescaleMPL115A2.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file FreescaleMPL115A2.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Bobby Schulz 7 | * Edited by Sara Geleskie Damiano 8 | * 9 | * @brief Implements the FreescaleMPL115A2 class. 10 | */ 11 | 12 | #include "FreescaleMPL115A2.h" 13 | 14 | 15 | // The constructor - because this is I2C, only need the power pin 16 | // This sensor has a set I2C address of 0x60. 17 | FreescaleMPL115A2::FreescaleMPL115A2(TwoWire* theI2C, int8_t powerPin, 18 | uint8_t measurementsToAverage) 19 | : Sensor("FreescaleMPL115A2", MPL115A2_NUM_VARIABLES, 20 | MPL115A2_WARM_UP_TIME_MS, MPL115A2_STABILIZATION_TIME_MS, 21 | MPL115A2_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage), 22 | _i2c(theI2C) {} 23 | FreescaleMPL115A2::FreescaleMPL115A2(int8_t powerPin, 24 | uint8_t measurementsToAverage) 25 | : Sensor("FreescaleMPL115A2", MPL115A2_NUM_VARIABLES, 26 | MPL115A2_WARM_UP_TIME_MS, MPL115A2_STABILIZATION_TIME_MS, 27 | MPL115A2_MEASUREMENT_TIME_MS, powerPin, -1, measurementsToAverage, 28 | MPL115A2_INC_CALC_VARIABLES), 29 | _i2c(&Wire) {} 30 | // Destructor 31 | FreescaleMPL115A2::~FreescaleMPL115A2() {} 32 | 33 | 34 | String FreescaleMPL115A2::getSensorLocation(void) { 35 | return F("I2C_0x60"); 36 | } 37 | 38 | 39 | bool FreescaleMPL115A2::setup(void) { 40 | bool retVal = 41 | Sensor::setup(); // this will set pin modes and the setup status bit 42 | 43 | // This sensor needs power for setup! 44 | // The MPL115A2's begin() reads required coefficients from the sensor. 45 | bool wasOn = checkPowerOn(); 46 | if (!wasOn) { powerUp(); } 47 | waitForWarmUp(); 48 | 49 | // Run the sensor begin() 50 | // This doesn't return anything to indicate failure or success, we just have 51 | // to hope 52 | mpl115a2_internal.begin(_i2c); 53 | 54 | // Turn the power back off it it had been turned on 55 | if (!wasOn) { powerDown(); } 56 | 57 | return retVal; 58 | } 59 | 60 | 61 | bool FreescaleMPL115A2::addSingleMeasurementResult(void) { 62 | // Initialize float variables 63 | float temp = -9999; 64 | float press = -9999; 65 | 66 | // Check a measurement was *successfully* started (status bit 6 set) 67 | // Only go on to get a result if it was 68 | if (bitRead(_sensorStatus, 6)) { 69 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 70 | 71 | // Read values 72 | mpl115a2_internal.getPT(&press, &temp); 73 | 74 | if (isnan(temp)) temp = -9999; 75 | if (isnan(press)) press = -9999; 76 | 77 | if (press > 115.0 || temp < -40.0) { 78 | temp = -9999; 79 | press = -9999; 80 | } 81 | 82 | MS_DBG(F(" Temperature:"), temp); 83 | MS_DBG(F(" Pressure:"), press); 84 | } else { 85 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 86 | } 87 | 88 | verifyAndAddMeasurementResult(MPL115A2_TEMP_VAR_NUM, temp); 89 | verifyAndAddMeasurementResult(MPL115A2_PRESSURE_VAR_NUM, press); 90 | 91 | // Unset the time stamp for the beginning of this measurement 92 | _millisMeasurementRequested = 0; 93 | // Unset the status bits for a measurement request (bits 5 & 6) 94 | _sensorStatus &= 0b10011111; 95 | 96 | // no way of knowing if successful, just return true 97 | return true; 98 | } 99 | -------------------------------------------------------------------------------- /src/sensors/MaximDS3231.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MaximDS3231.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the MaximDS18 class. 9 | */ 10 | 11 | #include 12 | #include "MaximDS3231.h" 13 | 14 | // Only input is the number of readings to average 15 | MaximDS3231::MaximDS3231(uint8_t measurementsToAverage) 16 | : Sensor("MaximDS3231", DS3231_NUM_VARIABLES, DS3231_WARM_UP_TIME_MS, 17 | DS3231_STABILIZATION_TIME_MS, DS3231_MEASUREMENT_TIME_MS, -1, -1, 18 | measurementsToAverage, DS3231_INC_CALC_VARIABLES) {} 19 | // Destructor 20 | MaximDS3231::~MaximDS3231() {} 21 | 22 | String MaximDS3231::getSensorLocation(void) { 23 | return F("I2C_0x68"); 24 | } 25 | 26 | 27 | bool MaximDS3231::setup(void) { 28 | rtc.begin(); // NOTE: This also turns off interrupts on the RTC! 29 | return Sensor::setup(); // this will set pin modes and the setup status bit 30 | // The clock should be continuously powered, so we never need to worry about 31 | // power up 32 | } 33 | 34 | 35 | // Sending the device a request to start temp conversion. 36 | bool MaximDS3231::startSingleMeasurement(void) { 37 | // Sensor::startSingleMeasurement() checks that if it's awake/active and 38 | // sets the timestamp and status bits. If it returns false, there's no 39 | // reason to go on. 40 | if (!Sensor::startSingleMeasurement()) return false; 41 | 42 | // force a temperature sampling and conversion 43 | // this function already has a forced wait for the conversion to complete 44 | // TODO(SRGDamia1): Test how long the conversion takes, update DS3231 lib 45 | // accordingly! 46 | MS_DBG(F("Forcing new temperature reading by DS3231")); 47 | rtc.convertTemperature(false); 48 | 49 | return true; 50 | } 51 | 52 | 53 | bool MaximDS3231::addSingleMeasurementResult(void) { 54 | // get the temperature value 55 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 56 | float tempVal = rtc.getTemperature(); 57 | MS_DBG(F(" Temp:"), tempVal, F("°C")); 58 | 59 | verifyAndAddMeasurementResult(DS3231_TEMP_VAR_NUM, tempVal); 60 | 61 | // Unset the time stamp for the beginning of this measurement 62 | _millisMeasurementRequested = 0; 63 | // Unset the status bits for a measurement request (bits 5 & 6) 64 | _sensorStatus &= 0b10011111; 65 | 66 | // Return true when finished 67 | return true; 68 | } 69 | -------------------------------------------------------------------------------- /src/sensors/MeaSpecMS5803.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MeaSpecMS5803.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Anthony Aufdenkampe with help from Beth 7 | * Fisher, Evan Host and Bobby Schulz. 8 | * Edited by Sara Geleskie Damiano 9 | * 10 | * @brief Implements the MeaSpecMS5803 class. 11 | */ 12 | 13 | #include "MeaSpecMS5803.h" 14 | 15 | 16 | // The constructor - because this is I2C, only need the power pin 17 | MeaSpecMS5803::MeaSpecMS5803(int8_t powerPin, uint8_t i2cAddressHex, 18 | int16_t maxPressure, uint8_t measurementsToAverage) 19 | : Sensor("MeaSpecMS5803", MS5803_NUM_VARIABLES, MS5803_WARM_UP_TIME_MS, 20 | MS5803_STABILIZATION_TIME_MS, MS5803_MEASUREMENT_TIME_MS, powerPin, 21 | -1, measurementsToAverage, MS5803_INC_CALC_VARIABLES), 22 | _i2cAddressHex(i2cAddressHex), 23 | _maxPressure(maxPressure) {} 24 | // Destructor 25 | MeaSpecMS5803::~MeaSpecMS5803() {} 26 | 27 | 28 | String MeaSpecMS5803::getSensorLocation(void) { 29 | String address = F("I2C_0x"); 30 | address += String(_i2cAddressHex, HEX); 31 | return address; 32 | } 33 | 34 | 35 | bool MeaSpecMS5803::setup(void) { 36 | bool retVal = 37 | Sensor::setup(); // this will set pin modes and the setup status bit 38 | 39 | // This sensor needs power for setup! 40 | bool wasOn = checkPowerOn(); 41 | if (!wasOn) { powerUp(); } 42 | waitForWarmUp(); 43 | 44 | // This doesn't return anything to indicate failure or success, we just have 45 | // to hope 46 | MS5803_internal.begin(_i2cAddressHex, _maxPressure); 47 | MS5803_internal.reset(); 48 | 49 | // Turn the power back off it it had been turned on 50 | if (!wasOn) { powerDown(); } 51 | 52 | return retVal; 53 | } 54 | 55 | 56 | bool MeaSpecMS5803::addSingleMeasurementResult(void) { 57 | bool success = false; 58 | 59 | // Initialize float variables 60 | float temp = -9999; 61 | float press = -9999; 62 | 63 | // Check a measurement was *successfully* started (status bit 6 set) 64 | // Only go on to get a result if it was 65 | if (bitRead(_sensorStatus, 6)) { 66 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 67 | // Read values 68 | // NOTE: These functions actually include the request to begin 69 | // a measurement and the wait for said measurement to finish. 70 | // It's pretty fast (max of 11 ms) so we'll just wait. 71 | temp = MS5803_internal.getTemperature(CELSIUS, ADC_512); 72 | press = MS5803_internal.getPressure(ADC_4096); 73 | 74 | if (isnan(temp)) temp = -9999; 75 | if (isnan(press)) press = -9999; 76 | if (temp < -50 || temp > 95) { // Range is -40°C to +85°C 77 | temp = -9999; 78 | press = -9999; 79 | } 80 | if (press == 0) { // Returns 0 when disconnected, which is highly 81 | // unlikely to be a real value. 82 | temp = -9999; 83 | press = -9999; 84 | } 85 | 86 | MS_DBG(F(" Temperature:"), temp); 87 | MS_DBG(F(" Pressure:"), press); 88 | } else { 89 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 90 | } 91 | 92 | verifyAndAddMeasurementResult(MS5803_TEMP_VAR_NUM, temp); 93 | verifyAndAddMeasurementResult(MS5803_PRESSURE_VAR_NUM, press); 94 | 95 | // Unset the time stamp for the beginning of this measurement 96 | _millisMeasurementRequested = 0; 97 | // Unset the status bits for a measurement request (bits 5 & 6) 98 | _sensorStatus &= 0b10011111; 99 | 100 | return success; 101 | } 102 | -------------------------------------------------------------------------------- /src/sensors/MeterTeros11.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MeterTeros11.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Anthony Aufdenkampe 7 | * Edited by Sara Geleskie Damiano 8 | * 9 | * @brief Implements the MeterTeros11 class. 10 | */ 11 | 12 | #include "MeterTeros11.h" 13 | 14 | 15 | bool MeterTeros11::getResults(void) { 16 | // Set up the float variables for receiving data 17 | float raw = -9999; 18 | float temp = -9999; 19 | 20 | // Check if this the currently active SDI-12 Object 21 | bool wasActive = _SDI12Internal.isActive(); 22 | // If it wasn't active, activate it now. 23 | // Use begin() instead of just setActive() to ensure timer is set 24 | // correctly. 25 | if (!wasActive) _SDI12Internal.begin(); 26 | // Empty the buffer 27 | _SDI12Internal.clearBuffer(); 28 | 29 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 30 | String getDataCommand = ""; 31 | getDataCommand += _SDI12address; 32 | // SDI-12 command to get data [address][D][dataOption][!] 33 | getDataCommand += "D0!"; 34 | _SDI12Internal.sendCommand(getDataCommand, _extraWakeTime); 35 | delay(30); // It just needs this little delay 36 | MS_DEEP_DBG(F(" >>>"), getDataCommand); 37 | 38 | // Wait for the first few charaters to arrive. The response from a data 39 | // request should always have more than three characters 40 | uint32_t start = millis(); 41 | while (_SDI12Internal.available() < 3 && (millis() - start) < 1500) { 42 | // wait 43 | } 44 | // read the returned address to remove it from the buffer 45 | auto returnedAddress = static_cast(_SDI12Internal.read()); 46 | // print out a warning if the address doesn't match up 47 | if (returnedAddress != _SDI12address) { 48 | MS_DBG(F("Warning, expecting data from"), _SDI12address, 49 | F("but got data from"), returnedAddress); 50 | } 51 | // Start printing out the returned data 52 | MS_DEEP_DBG(F(" <<<"), returnedAddress); 53 | 54 | // read the '+' out of the buffer, and print it if we're debugging 55 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 56 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 57 | #else 58 | _SDI12Internal.read(); 59 | #endif 60 | 61 | // Read the raw VWC counts 62 | raw = _SDI12Internal.parseFloat(SKIP_NONE); 63 | MS_DEEP_DBG(F(" <<<"), String(raw, 10)); 64 | 65 | // read the next '+' out of the buffer 66 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 67 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 68 | #else 69 | _SDI12Internal.read(); 70 | #endif 71 | 72 | // Now read the temperature 73 | temp = _SDI12Internal.parseFloat(SKIP_NONE); 74 | MS_DEEP_DBG(F(" <<<"), String(temp, 10)); 75 | 76 | // read and dump anything else 77 | while (_SDI12Internal.available()) { 78 | #ifdef MS_SDI12SENSORS_DEBUG_DEEP 79 | MS_DEEP_DBG(F(" <<<"), static_cast(_SDI12Internal.read())); 80 | #else 81 | _SDI12Internal.read(); 82 | #endif 83 | } 84 | 85 | // Empty the buffer again 86 | _SDI12Internal.clearBuffer(); 87 | 88 | // De-activate the SDI-12 Object 89 | // Use end() instead of just forceHold to un-set the timers 90 | if (!wasActive) _SDI12Internal.end(); 91 | 92 | MS_DBG(F("Raw VWC Counts:"), raw); 93 | MS_DBG(F("Raw Temperature Value:"), temp); 94 | 95 | // Set up the float variables for calculated variable 96 | float ea = -9999; 97 | float VWC = -9999; 98 | 99 | // Calculate the dielectric EA from the raw count value. 100 | // Equation 8 from the Teros 11 user manual: 101 | // http://publications.metergroup.com/Manuals/20587_TEROS11-12_Manual_Web.pdf 102 | if (raw < 0 || raw > 5000) { 103 | MS_DBG( 104 | F("WARNING: raw results out of range (0-5000)! Cannot calculate " 105 | "Ea or VWC")); 106 | raw = -9999; 107 | } 108 | if (raw != -9999) { 109 | ea = ((2.887e-9 * (raw * raw * raw)) - (2.08e-5 * (raw * raw)) + 110 | (5.276e-2 * raw) - 43.39) * 111 | ((2.887e-9 * (raw * raw * raw)) - (2.08e-5 * (raw * raw)) + 112 | (5.276e-2 * raw) - 43.39); 113 | MS_DBG(F("Calculated Ea:"), ea); 114 | } 115 | 116 | // Calculate the VWC from EA using the Topp equation 117 | // range check 118 | if (ea < 0 || ea > 350) { 119 | MS_DBG(F("WARNING: Ea results out of range (0-350)! Cannot calculate " 120 | "VWC")); 121 | ea = -9999; 122 | } 123 | // calculate 124 | if (ea != -9999) { 125 | VWC = (4.3e-6 * (ea * ea * ea)) - (5.5e-4 * (ea * ea)) + 126 | (2.92e-2 * ea) - 5.3e-2; 127 | VWC *= 100; // Convert to actual percent 128 | MS_DBG(F("Calculated VWC:"), ea); 129 | if (VWC < 0) { 130 | VWC = 0; 131 | MS_DBG(F("Setting negative VWC to 0")); 132 | } 133 | if (VWC > 100) { 134 | VWC = 100; 135 | MS_DBG(F("Setting VWC >100 to 100")); 136 | } 137 | } 138 | 139 | // VWC = 3.879e-4*raw-0.6956; // equation for mineral soils 140 | 141 | // range check on temp; range is - 40°C to + 50°C 142 | if (temp < -50 || temp > 60) { 143 | temp = -9999; 144 | MS_DBG(F("WARNING: temperature results out of range (-50-60)!")); 145 | } 146 | 147 | verifyAndAddMeasurementResult(TEROS11_COUNT_VAR_NUM, raw); 148 | verifyAndAddMeasurementResult(TEROS11_TEMP_VAR_NUM, temp); 149 | verifyAndAddMeasurementResult(TEROS11_EA_VAR_NUM, ea); 150 | verifyAndAddMeasurementResult(TEROS11_VWC_VAR_NUM, VWC); 151 | 152 | return temp != -9999; 153 | } 154 | -------------------------------------------------------------------------------- /src/sensors/TIADS1x15.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TIADS1x15.cpp * 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Bobby Schulz 7 | * Edited by Sara Geleskie Damiano 8 | * Adapted from CampbellOBS3.h by Sara Geleskie Damiano 9 | * 10 | * 11 | * @brief Implements the TIADS1x15 class. 12 | */ 13 | 14 | 15 | #include "TIADS1x15.h" 16 | #include 17 | 18 | 19 | // The constructor - need the power pin the data pin, and gain if non standard 20 | TIADS1x15::TIADS1x15(int8_t powerPin, uint8_t adsChannel, float gain, 21 | uint8_t i2cAddress, uint8_t measurementsToAverage) 22 | : Sensor("TIADS1x15", TIADS1X15_NUM_VARIABLES, TIADS1X15_WARM_UP_TIME_MS, 23 | TIADS1X15_STABILIZATION_TIME_MS, TIADS1X15_MEASUREMENT_TIME_MS, 24 | powerPin, -1, measurementsToAverage, TIADS1X15_INC_CALC_VARIABLES), 25 | _adsChannel(adsChannel), 26 | _gain(gain), 27 | _i2cAddress(i2cAddress) {} 28 | // Destructor 29 | TIADS1x15::~TIADS1x15() {} 30 | 31 | 32 | String TIADS1x15::getSensorLocation(void) { 33 | #ifndef MS_USE_ADS1015 34 | String sensorLocation = F("ADS1115_0x"); 35 | #else 36 | String sensorLocation = F("ADS1015_0x"); 37 | #endif 38 | sensorLocation += String(_i2cAddress, HEX); 39 | sensorLocation += F("_Channel"); 40 | sensorLocation += String(_adsChannel); 41 | return sensorLocation; 42 | } 43 | 44 | 45 | bool TIADS1x15::addSingleMeasurementResult(void) { 46 | // Variables to store the results in 47 | int16_t adcCounts = -9999; 48 | float adcVoltage = -9999; 49 | float calibResult = -9999; 50 | 51 | // Check a measurement was *successfully* started (status bit 6 set) 52 | // Only go on to get a result if it was 53 | if (bitRead(_sensorStatus, 6)) { 54 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 55 | 56 | // Create an Auxillary ADD object 57 | // We create and set up the ADC object here so that each sensor using 58 | // the ADC may set the gain appropriately without effecting others. 59 | #ifndef MS_USE_ADS1015 60 | Adafruit_ADS1115 ads; // Use this for the 16-bit version 61 | #else 62 | Adafruit_ADS1015 ads; // Use this for the 12-bit version 63 | #endif 64 | // ADS Library default settings: 65 | // - TI1115 (16 bit) 66 | // - single-shot mode (powers down between conversions) 67 | // - 128 samples per second (8ms conversion time) 68 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 69 | // - TI1015 (12 bit) 70 | // - single-shot mode (powers down between conversions) 71 | // - 1600 samples per second (625µs conversion time) 72 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 73 | 74 | // Bump the gain up to 1x = +/- 4.096V range 75 | ads.setGain(GAIN_ONE); 76 | // Begin ADC 77 | ads.begin(_i2cAddress); 78 | 79 | // Read Analog to Digital Converter (ADC) 80 | // Taking this reading includes the 8ms conversion delay. 81 | // Measure the ADC raw count 82 | adcCounts = ads.readADC_SingleEnded(_adsChannel); 83 | // Convert ADC raw counts value to voltage (V) 84 | adcVoltage = ads.computeVolts(adcCounts); 85 | MS_DBG(F(" ads.readADC_SingleEnded("), _adsChannel, F("):"), 86 | adcVoltage); 87 | 88 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 89 | // Skip results out of range 90 | // Apply the gain calculation, with a defualt gain of 10 V/V Gain 91 | calibResult = adcVoltage * _gain; 92 | MS_DBG(F(" calibResult:"), calibResult); 93 | } else { // set invalid voltages back to -9999 94 | adcVoltage = -9999; 95 | } 96 | } else { 97 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 98 | } 99 | 100 | verifyAndAddMeasurementResult(TIADS1X15_VAR_NUM, calibResult); 101 | 102 | // Unset the time stamp for the beginning of this measurement 103 | _millisMeasurementRequested = 0; 104 | // Unset the status bits for a measurement request (bits 5 & 6) 105 | _sensorStatus &= 0b10011111; 106 | 107 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/sensors/TIINA219.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TIINA219.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Written By: Neil Hancock 7 | * Edited by Sara Geleskie Damiano 8 | * 9 | * @brief Implements the TIINA219 class. 10 | */ 11 | 12 | #include "TIINA219.h" 13 | 14 | // The constructors 15 | TIINA219::TIINA219(TwoWire* theI2C, int8_t powerPin, uint8_t i2cAddressHex, 16 | uint8_t measurementsToAverage) 17 | : Sensor("TIINA219", INA219_NUM_VARIABLES, INA219_WARM_UP_TIME_MS, 18 | INA219_STABILIZATION_TIME_MS, INA219_MEASUREMENT_TIME_MS, powerPin, 19 | -1, measurementsToAverage), 20 | _i2cAddressHex(i2cAddressHex), 21 | _i2c(theI2C) {} 22 | TIINA219::TIINA219(int8_t powerPin, uint8_t i2cAddressHex, 23 | uint8_t measurementsToAverage) 24 | : Sensor("TIINA219", INA219_NUM_VARIABLES, INA219_WARM_UP_TIME_MS, 25 | INA219_STABILIZATION_TIME_MS, INA219_MEASUREMENT_TIME_MS, powerPin, 26 | -1, measurementsToAverage, INA219_INC_CALC_VARIABLES), 27 | _i2cAddressHex(i2cAddressHex), 28 | _i2c(&Wire) {} 29 | // Destructor 30 | TIINA219::~TIINA219() {} 31 | 32 | 33 | String TIINA219::getSensorLocation(void) { 34 | String address = F("I2C_0x"); 35 | address += String(_i2cAddressHex, HEX); 36 | return address; 37 | } 38 | 39 | 40 | bool TIINA219::setup(void) { 41 | bool wasOn; 42 | Sensor::setup(); // this will set pin modes and the setup status bit 43 | 44 | // This sensor needs power for setup! 45 | wasOn = checkPowerOn(); 46 | if (!wasOn) { 47 | powerUp(); 48 | waitForWarmUp(); 49 | } 50 | 51 | ina219_phy.begin(_i2c); 52 | 53 | // Turn the power back off it it had been turned on 54 | if (!wasOn) { powerDown(); } 55 | 56 | return true; 57 | } 58 | 59 | 60 | bool TIINA219::wake(void) { 61 | // Sensor::wake() checks if the power pin is on and sets the wake timestamp 62 | // and status bits. If it returns false, there's no reason to go on. 63 | if (!Sensor::wake()) return false; 64 | 65 | // Begin/Init needs to be rerun after every power-up to set the calibration 66 | // coefficient for the INA219 (see p21 of datasheet) 67 | ina219_phy.begin(_i2c); 68 | 69 | return true; 70 | } 71 | 72 | 73 | bool TIINA219::addSingleMeasurementResult(void) { 74 | bool success = false; 75 | 76 | // Initialize float variables 77 | float current_mA = -9999; 78 | float busV_V = -9999; 79 | float power_mW = -9999; 80 | 81 | // Check a measurement was *successfully* started (status bit 6 set) 82 | // Only go on to get a result if it was 83 | if (bitRead(_sensorStatus, 6)) { 84 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 85 | 86 | // Read values 87 | current_mA = ina219_phy.getCurrent_mA(); 88 | if (isnan(current_mA)) current_mA = -9999; 89 | busV_V = ina219_phy.getBusVoltage_V(); 90 | if (isnan(busV_V)) busV_V = -9999; 91 | power_mW = ina219_phy.getPower_mW(); 92 | if (isnan(power_mW)) power_mW = -9999; 93 | 94 | success = true; 95 | 96 | MS_DBG(F(" Current [mA]:"), current_mA); 97 | MS_DBG(F(" Bus Voltage [V]:"), busV_V); 98 | MS_DBG(F(" Power [mW]:"), power_mW); 99 | } else { 100 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 101 | } 102 | 103 | verifyAndAddMeasurementResult(INA219_CURRENT_MA_VAR_NUM, current_mA); 104 | verifyAndAddMeasurementResult(INA219_BUS_VOLTAGE_VAR_NUM, busV_V); 105 | verifyAndAddMeasurementResult(INA219_POWER_MW_VAR_NUM, power_mW); 106 | 107 | 108 | // Unset the time stamp for the beginning of this measurement 109 | _millisMeasurementRequested = 0; 110 | // Unset the status bits for a measurement request (bits 5 & 6) 111 | _sensorStatus &= 0b10011111; 112 | 113 | return success; 114 | } 115 | -------------------------------------------------------------------------------- /src/sensors/TallyCounterI2C.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TallyCounterI2C.h 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Anthony Aufdenkampe 7 | * Edited by Sara Geleskie Damiano 8 | * 9 | * @brief Implements the TallyCounterI2C sensor subclass. 10 | */ 11 | 12 | #include "TallyCounterI2C.h" 13 | 14 | 15 | // The constructor 16 | TallyCounterI2C::TallyCounterI2C(int8_t powerPin, uint8_t i2cAddressHex) 17 | : Sensor("TallyCounterI2C", TALLY_NUM_VARIABLES, TALLY_WARM_UP_TIME_MS, 18 | TALLY_STABILIZATION_TIME_MS, TALLY_MEASUREMENT_TIME_MS, powerPin, 19 | -1, 1, TALLY_INC_CALC_VARIABLES), 20 | _i2cAddressHex(i2cAddressHex) {} 21 | // Destructor 22 | TallyCounterI2C::~TallyCounterI2C() {} 23 | 24 | 25 | String TallyCounterI2C::getSensorLocation(void) { 26 | String address = F("I2C_0x"); 27 | address += String(_i2cAddressHex, HEX); 28 | return address; 29 | } 30 | 31 | 32 | bool TallyCounterI2C::setup(void) { 33 | bool retVal = 34 | Sensor::setup(); // this will set pin modes and the setup status bit 35 | 36 | // This sensor needs power for setup! 37 | bool wasOn = checkPowerOn(); 38 | if (!wasOn) { powerUp(); } 39 | waitForWarmUp(); 40 | 41 | // Run begin fxn because it returns true or false for success in contact 42 | // Make 5 attempts 43 | uint8_t ntries = 0; 44 | bool success = false; 45 | uint8_t Stat = false; // Used to test for connectivity to Tally device 46 | while (!success && ntries < 5) { 47 | Stat = counter_internal.begin(); 48 | counter_internal.Sleep(); // Engage auto-sleep mode between event 49 | // counts 50 | counter_internal.Clear(); // Clear count to ensure valid first reading 51 | if (Stat == 0) success = true; 52 | ntries++; 53 | } 54 | if (!success) { 55 | // Set the status error bit (bit 7) 56 | _sensorStatus |= 0b10000000; 57 | // UN-set the set-up bit (bit 0) since setup failed! 58 | _sensorStatus &= 0b11111110; 59 | } 60 | retVal &= success; 61 | 62 | // Turn the power back off it it had been turned on 63 | if (!wasOn) { powerDown(); } 64 | 65 | return retVal; 66 | } 67 | 68 | 69 | bool TallyCounterI2C::addSingleMeasurementResult(void) { 70 | bool success = false; 71 | 72 | // Initialize variables 73 | int16_t events = -9999; // Number of events 74 | 75 | // Check a measurement was *successfully* started (status bit 6 set) 76 | // Only go on to get a result if it was 77 | if (bitRead(_sensorStatus, 6)) { 78 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 79 | 80 | // Read values 81 | // Read data from counter before clear 82 | 83 | events = counter_internal.Peek(); 84 | if (isnan(events)) events = -9999; 85 | 86 | // Assume that if negative a failed response 87 | // May also return a very negative temp when receiving a bad response 88 | if (events < 0) { 89 | MS_DBG(F("All values 0 or bad, assuming sensor non-response!")); 90 | events = -9999; 91 | } else { 92 | success = true; 93 | } 94 | 95 | // Clear count value 96 | counter_internal.Clear(); 97 | 98 | if (events < 0) 99 | events = -9999; // If negetive value results, return failure 100 | 101 | MS_DBG(F(" Events:"), events); 102 | 103 | } else { 104 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 105 | } 106 | 107 | verifyAndAddMeasurementResult(TALLY_EVENTS_VAR_NUM, events); 108 | 109 | // Unset the time stamp for the beginning of this measurement 110 | _millisMeasurementRequested = 0; 111 | // Unset the status bits for a measurement request (bits 5 & 6) 112 | _sensorStatus &= 0b10011111; 113 | 114 | return success; 115 | } 116 | -------------------------------------------------------------------------------- /src/sensors/TurnerCyclops.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TurnerCyclops.cpp 3 | * @copyright Stroud Water Research Center 4 | * Part of the EnviroDIY ModularSensors library for Arduino. 5 | * This library is published under the BSD-3 license. 6 | * @author Sara Geleskie Damiano 7 | * 8 | * @brief Implements the TurnerCyclops class. 9 | */ 10 | 11 | 12 | #include "TurnerCyclops.h" 13 | #include 14 | 15 | 16 | // The constructor - need the power pin, the data pin, and the calibration info 17 | TurnerCyclops::TurnerCyclops(int8_t powerPin, uint8_t adsChannel, 18 | float conc_std, float volt_std, float volt_blank, 19 | uint8_t i2cAddress, uint8_t measurementsToAverage) 20 | : Sensor("TurnerCyclops", CYCLOPS_NUM_VARIABLES, CYCLOPS_WARM_UP_TIME_MS, 21 | CYCLOPS_STABILIZATION_TIME_MS, CYCLOPS_MEASUREMENT_TIME_MS, 22 | powerPin, -1, measurementsToAverage, CYCLOPS_INC_CALC_VARIABLES), 23 | _adsChannel(adsChannel), 24 | _conc_std(conc_std), 25 | _volt_std(volt_std), 26 | _volt_blank(volt_blank), 27 | _i2cAddress(i2cAddress) {} 28 | // Destructor 29 | TurnerCyclops::~TurnerCyclops() {} 30 | 31 | 32 | String TurnerCyclops::getSensorLocation(void) { 33 | #ifndef MS_USE_ADS1015 34 | String sensorLocation = F("ADS1115_0x"); 35 | #else 36 | String sensorLocation = F("ADS1015_0x"); 37 | #endif 38 | sensorLocation += String(_i2cAddress, HEX); 39 | sensorLocation += F("_Channel"); 40 | sensorLocation += String(_adsChannel); 41 | return sensorLocation; 42 | } 43 | 44 | 45 | bool TurnerCyclops::addSingleMeasurementResult(void) { 46 | // Variables to store the results in 47 | int16_t adcCounts = -9999; 48 | float adcVoltage = -9999; 49 | float calibResult = -9999; 50 | 51 | // Check a measurement was *successfully* started (status bit 6 set) 52 | // Only go on to get a result if it was 53 | if (bitRead(_sensorStatus, 6)) { 54 | MS_DBG(getSensorNameAndLocation(), F("is reporting:")); 55 | 56 | // Create an Auxillary ADD object 57 | // We create and set up the ADC object here so that each sensor using 58 | // the ADC may set the gain appropriately without effecting others. 59 | #ifndef MS_USE_ADS1015 60 | Adafruit_ADS1115 ads; // Use this for the 16-bit version 61 | #else 62 | Adafruit_ADS1015 ads; // Use this for the 12-bit version 63 | #endif 64 | // ADS Library default settings: 65 | // - TI1115 (16 bit) 66 | // - single-shot mode (powers down between conversions) 67 | // - 128 samples per second (8ms conversion time) 68 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 69 | // - TI1015 (12 bit) 70 | // - single-shot mode (powers down between conversions) 71 | // - 1600 samples per second (625µs conversion time) 72 | // - 2/3 gain +/- 6.144V range (limited to VDD +0.3V max) 73 | 74 | // Bump the gain up to 1x = +/- 4.096V range 75 | // Sensor return range is 0-2.5V, but the next gain option is 2x which 76 | // only allows up to 2.048V 77 | ads.setGain(GAIN_ONE); 78 | // Begin ADC 79 | ads.begin(_i2cAddress); 80 | 81 | // Print out the calibration curve 82 | MS_DBG(F(" Input calibration Curve:"), _volt_std, F("V at"), _conc_std, 83 | F(". "), _volt_blank, F("V blank.")); 84 | 85 | // Read Analog to Digital Converter (ADC) 86 | // Taking this reading includes the 8ms conversion delay. 87 | // Measure the ADC raw count 88 | adcCounts = ads.readADC_SingleEnded(_adsChannel); 89 | // Convert ADC raw counts value to voltage (V) 90 | adcVoltage = ads.computeVolts(adcCounts); 91 | MS_DBG(F(" ads.readADC_SingleEnded("), _adsChannel, F("):"), 92 | adcVoltage); 93 | 94 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 95 | // Skip results out of range 96 | // Apply the unique calibration curve for the given sensor 97 | calibResult = (_conc_std / (_volt_std - _volt_blank)) * 98 | (adcVoltage - _volt_blank); 99 | MS_DBG(F(" calibResult:"), calibResult); 100 | } else { // set invalid voltages back to -9999 101 | adcVoltage = -9999; 102 | } 103 | } else { 104 | MS_DBG(getSensorNameAndLocation(), F("is not currently measuring!")); 105 | } 106 | 107 | verifyAndAddMeasurementResult(CYCLOPS_VAR_NUM, calibResult); 108 | verifyAndAddMeasurementResult(CYCLOPS_VOLTAGE_VAR_NUM, adcVoltage); 109 | 110 | // Unset the time stamp for the beginning of this measurement 111 | _millisMeasurementRequested = 0; 112 | // Unset the status bits for a measurement request (bits 5 & 6) 113 | _sensorStatus &= 0b10011111; 114 | 115 | if (adcVoltage < 3.6 && adcVoltage > -0.3) { 116 | return true; 117 | } else { 118 | return false; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/src.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * @dir src 3 | * 4 | * @brief Contains the source code for ModularSensors. 5 | * 6 | * The main directory contains the parent classes and sub-directories containing sub-classes 7 | */ 8 | /** 9 | * @dir src/modems 10 | * 11 | * @brief Contains the source code for all loggerModem subclasses. 12 | */ 13 | /** 14 | * @dir src/publishers 15 | * 16 | * @brief Contains the source code for all dataPublisher subclasses. 17 | */ 18 | /** 19 | * @dir src/sensors 20 | * 21 | * @brief Contains the source code for all Sensor subclasses, including the Sensor subclasses which themselves are the parent classes to other Sensors. 22 | */ 23 | /** 24 | * @dir src/WatchDogs 25 | * 26 | * @brief Contains the source code for setting up the watch-dog reset on the various supported processors. 27 | */ 28 | /** 29 | * @defgroup base_classes Primary Object Classes 30 | * These are the primary object classes used by the ModularSensors library. 31 | * One parent class exists for each of the major terms used to describe objects in the library. 32 | * @see @ref page_library_terminology 33 | */ --------------------------------------------------------------------------------