├── .github ├── dependabot.yml └── workflows │ ├── check-arduino.yml │ ├── compile-examples.yml │ ├── render-documentation.yml │ ├── spell-check.yml │ └── sync-labels.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── README.md ├── api.md └── assets │ ├── H7_deep_sleep_peripherals_off.png │ ├── H7_deep_sleep_peripherals_on.png │ ├── H7_lite_deep_sleep_peripherals_off.png │ ├── H7_lite_deep_sleep_peripherals_on.png │ └── normal_usage.png ├── examples ├── Battery │ └── Battery.ino ├── Charger │ └── Charger.ino ├── Standby_WakeFromPin │ └── Standby_WakeFromPin.ino ├── Standby_WakeFromRTC_C33 │ └── Standby_WakeFromRTC_C33.ino └── Standby_WakeFromRTC_H7 │ └── Standby_WakeFromRTC_H7.ino ├── library.properties └── src ├── Arduino_PowerManagement.h ├── Battery.cpp ├── Battery.h ├── BatteryConstants.h ├── Board.cpp ├── Board.h ├── Charger.cpp ├── Charger.h ├── MAX1726Driver.h └── WireUtils.h /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | labels: 10 | - "topic: infrastructure" 11 | schedule: 12 | interval: daily -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Run Arduino Lint 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v2 23 | with: 24 | compliance: specification 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/per1234/.github/blob/main/workflow-templates/compile-examples-private.md 2 | name: Compile Examples 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/compile-examples.ya?ml" 9 | - "library.properties" 10 | - "examples/**" 11 | - "src/**" 12 | pull_request: 13 | paths: 14 | - ".github/workflows/compile-examples.ya?ml" 15 | - "library.properties" 16 | - "examples/**" 17 | - "src/**" 18 | schedule: 19 | # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). 20 | - cron: "0 8 * * TUE" 21 | workflow_dispatch: 22 | repository_dispatch: 23 | 24 | env: 25 | UNIVERSAL_SKETCH_PATHS: | 26 | - examples/Battery 27 | - examples/Charger 28 | SKETCHES_REPORTS_PATH: sketches-reports 29 | SKETCHES_REPORTS_ARTIFACT_NAME: sketches-reports 30 | 31 | jobs: 32 | build: 33 | name: ${{ matrix.board.fqbn }} 34 | runs-on: ubuntu-latest 35 | permissions: 36 | contents: read 37 | pull-requests: read 38 | 39 | strategy: 40 | fail-fast: false 41 | 42 | matrix: 43 | board: 44 | - fqbn: arduino:mbed_portenta:envie_m7 45 | platforms: | 46 | - name: arduino:mbed_portenta 47 | artifact-name-suffix: arduino-mbed_portenta-envie_m7 48 | additional-sketch-paths: | 49 | - examples/Standby_WakeFromRTC_H7 50 | - examples/Standby_WakeFromPin 51 | - fqbn: arduino:renesas_portenta:portenta_c33 52 | platforms: | 53 | - name: arduino:renesas_portenta 54 | artifact-name-suffix: arduino-renesas_portenta-portenta_c33 55 | additional-sketch-paths: | 56 | - examples/Standby_WakeFromRTC_C33 57 | - examples/Standby_WakeFromPin 58 | - fqbn: arduino:mbed_nicla:nicla_vision 59 | platforms: | 60 | - name: arduino:mbed_nicla 61 | artifact-name-suffix: arduino-mbed_nicla-nicla_vision 62 | 63 | steps: 64 | - name: Checkout repository 65 | uses: actions/checkout@v4 66 | 67 | - name: Compile examples 68 | uses: arduino/compile-sketches@v1 69 | with: 70 | github-token: ${{ secrets.GITHUB_TOKEN }} 71 | fqbn: ${{ matrix.board.fqbn }} 72 | platforms: ${{ matrix.board.platforms }} 73 | libraries: | 74 | - source-path: ./ 75 | - name: Arduino_PF1550 76 | - name: Arduino_LowPowerPortentaH7 77 | - source-url: https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33.git 78 | sketch-paths: | 79 | ${{ env.UNIVERSAL_SKETCH_PATHS }} 80 | ${{ matrix.board.additional-sketch-paths }} 81 | enable-deltas-report: true 82 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 83 | 84 | - name: Save sketches report as workflow artifact 85 | uses: actions/upload-artifact@v4 86 | with: 87 | if-no-files-found: error 88 | name: sketches-report-${{ matrix.board.artifact-name-suffix }} 89 | path: ${{ env.SKETCHES_REPORTS_PATH }} 90 | -------------------------------------------------------------------------------- /.github/workflows/render-documentation.yml: -------------------------------------------------------------------------------- 1 | name: Render Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - ".github/workflows/render-documentation.ya?ml" 9 | - "examples/**" 10 | - "src/**" 11 | pull_request: 12 | branches: 13 | - main 14 | paths: 15 | - ".github/workflows/render-documentation.ya?ml" 16 | - "examples/**" 17 | - "src/**" 18 | workflow_dispatch: 19 | 20 | jobs: 21 | render-docs: 22 | permissions: 23 | contents: write 24 | uses: arduino/render-docs-github-action/.github/workflows/render-docs.yml@main 25 | with: 26 | source-path: './src' 27 | target-path: './docs/api.md' 28 | fail-on-warnings: true -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/per1234/.github/blob/main/workflow-templates/spell-check.md 2 | name: Spell Check 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | pull_request: 8 | schedule: 9 | # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. 10 | - cron: "0 8 * * TUE" 11 | workflow_dispatch: 12 | repository_dispatch: 13 | 14 | jobs: 15 | spellcheck: 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | - name: Spell check 25 | uses: codespell-project/actions-codespell@v2 -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/actions/using-workflows/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | permissions: 28 | contents: read 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v4 33 | 34 | - name: Download JSON schema for labels configuration file 35 | id: download-schema 36 | uses: carlosperate/download-file-action@v2 37 | with: 38 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 39 | location: ${{ runner.temp }}/label-configuration-schema 40 | 41 | - name: Install JSON schema validator 42 | run: | 43 | sudo npm install \ 44 | --global \ 45 | ajv-cli \ 46 | ajv-formats 47 | 48 | - name: Validate local labels configuration 49 | run: | 50 | # See: https://github.com/ajv-validator/ajv-cli#readme 51 | ajv validate \ 52 | --all-errors \ 53 | -c ajv-formats \ 54 | -s "${{ steps.download-schema.outputs.file-path }}" \ 55 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 56 | 57 | download: 58 | needs: check 59 | runs-on: ubuntu-latest 60 | permissions: {} 61 | 62 | strategy: 63 | matrix: 64 | filename: 65 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 66 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 67 | - universal.yml 68 | 69 | steps: 70 | - name: Download 71 | uses: carlosperate/download-file-action@v2 72 | with: 73 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 74 | 75 | - name: Pass configuration files to next job via workflow artifact 76 | uses: actions/upload-artifact@v4 77 | with: 78 | path: | 79 | *.yaml 80 | *.yml 81 | if-no-files-found: error 82 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 83 | 84 | sync: 85 | needs: download 86 | runs-on: ubuntu-latest 87 | permissions: 88 | contents: read 89 | issues: write 90 | 91 | steps: 92 | - name: Set environment variables 93 | run: | 94 | # See: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable 95 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 96 | 97 | - name: Determine whether to dry run 98 | id: dry-run 99 | if: > 100 | github.event_name == 'pull_request' || 101 | ( 102 | ( 103 | github.event_name == 'push' || 104 | github.event_name == 'workflow_dispatch' 105 | ) && 106 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 107 | ) 108 | run: | 109 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 110 | # configuration. 111 | echo "flag=--dry-run" >> $GITHUB_OUTPUT 112 | 113 | - name: Checkout repository 114 | uses: actions/checkout@v4 115 | 116 | - name: Download configuration files artifact 117 | uses: actions/download-artifact@v4 118 | with: 119 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 120 | path: ${{ env.CONFIGURATIONS_FOLDER }} 121 | 122 | - name: Remove unneeded artifact 123 | uses: geekyeggo/delete-artifact@v5 124 | with: 125 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 126 | 127 | - name: Merge label configuration files 128 | run: | 129 | # Merge all configuration files 130 | shopt -s extglob 131 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 132 | 133 | - name: Install github-label-sync 134 | run: sudo npm install --global github-label-sync 135 | 136 | - name: Sync labels 137 | env: 138 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 139 | run: | 140 | # See: https://github.com/Financial-Times/github-label-sync 141 | github-label-sync \ 142 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 143 | ${{ steps.dry-run.outputs.flag }} \ 144 | ${{ github.repository }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | examples/**/build/ 2 | .vscode/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at https://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚡ Arduino PowerManagement 2 | 3 | [![Check Arduino](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/check-arduino.yml) [![Compile Examples](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/compile-examples.yml) [![Spell Check](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/spell-check.yml) [![Sync Labels](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/sync-labels.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_PowerManagement/actions/workflows/sync-labels.yml) 4 | 5 | The Arduino Pro Power Management Library serves as a comprehensive and sophisticated power management toolkit tailored for Arduino Pro boards, encompassing the Portenta H7, Portenta C33, and Nicla Vision. 6 | 7 | This library streamlines the interaction with complex hardware components such as the [PF1550 Power Management Integrated Circuit](https://www.nxp.com/docs/en/data-sheet/PF1550.pdf) (PMIC) and the [MAX17262 LiPo Fuel Gauge](https://www.analog.com/media/en/technical-documentation/data-sheets/MAX17262.pdf) and presents a user-friendly, functionality-oriented interface. 8 | 9 | ## ✅ Supported Boards 10 | 11 | - Arduino Portenta H7 12 | - Arduino Nicla Vision (Low power features not supported yet) 13 | - Arduino Portenta C33 14 | 15 | ## Core Components of the Library 16 | The library is organized into three primary parts, each addressing specific aspects of power management: 17 | 18 | ### 🔋 Battery 19 | Dedicated to battery management, this class enables real-time monitoring of battery usage and health. By keeping track of various battery parameters, users can optimize their applications for better energy efficiency and battery longevity. 20 | 21 | ### 🔌 Charger 22 | Focused on battery charging dynamics, this class provides a detailed insight into charging parameters. Users can monitor and adjust these parameters, ensuring optimal charging conditions and extending the lifespan of the battery. By default the charger is configured to work with 3.7V batteries which get charged at 4.2V. If you want to use a different type of battery, please take a look at the [API documentation](./docs/api.md). 23 | 24 | ### ⚙️ Board 25 | This class centralizes control over the power distribution within the board. It allows users to enable or disable specific power rails, catering to the needs of energy-efficient designs. 26 | In addition to power rail management and battery handling, the library provides power-saving features: 27 | 28 | - **Sleep** 29 | This mode offers a significant reduction in power consumption while maintaining a state of minimal activity. 30 | It's ideal for applications requiring periodic wake-ups or brief intervals of inactivity. This sleep mode resumes the operation from the last operation. 31 | You can manually enable this sleep mode on the Portenta C33. On the H7 and Nicla Vision however, this is automatically handled by mbed. There is a separate (deep) sleep mode on Portenta H7 and Nicla Vision that can be enabled through their [low power libraries](https://github.com/arduino-libraries/Arduino_LowPowerPortentaH7). 32 | 33 | - **Standby** 34 | For scenarios demanding drastic power conservation, the Standby Mode drastically reduces the board's power usage. It's suitable for long-duration, battery-dependent applications where occasional wake-ups are sufficient. This mode restarts the board on wakeup, effectively running the `setup()` function again. 35 | 36 | #### Low Power Measurements 37 | Here's a table with the current consumption you can expect from each board in Sleep and Standby modes. Clicking on any of the values in the table above will send you to an image of the measurements for that specific scenario. 38 | 39 | | | Arduino Portenta C33 | Arduino Portenta H7 Lite | Arduino Portenta H7 | 40 | |--- |--- |--- |--- | 41 | | Without power optimisations | [41.37 mA](https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33/blob/main/docs/assets/normal_usage_blink.png) | [123.86 mA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/normal_usage.png) | [123.86 mA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/normal_usage.png) | 42 | | Standby consumption with peripherals off | [58.99 μA](https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33/blob/main/docs/assets/deep_sleep_no_peripherals.png) | [75.51 μA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/H7_lite_deep_sleep_peripherals_off.png) | [379 μA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/H7_deep_sleep_peripherals_off.png) | 43 | | Standby consumption with peripherals on | [11.53 mA](https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33/blob/main/docs/assets/deep_sleep_peripherals_on.png) | [4.89 mA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/H7_lite_deep_sleep_peripherals_on.png) | [7.98 mA](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/docs/assets/H7_deep_sleep_peripherals_on.png) | 44 | | Sleep consumption with peripherals off | [7.02 mA](https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33/blob/main/docs/assets/sleep_no_peripherals.png) | N/A | N/A | 45 | | Sleep consumption with peripherals on | [18.26 mA](https://github.com/arduino-libraries/Arduino_LowPowerPortentaC33/blob/main/docs/assets/sleep_peripherals_on.png) | N/A | N/A | 46 | 47 | > [!NOTE] 48 | > Sleep measurements are not available on the H7 boards because the board goes to sleep automatically when idling. 49 | 50 | > [!NOTE] 51 | > These measurements have been taken using a [Nordic Power Profiler Kit II](https://www.nordicsemi.com/Products/Development-hardware/Power-Profiler-Kit-2) through the JST power connector of the Portenta boards, 52 | > the numbers might be higher when powering through the VIN or 5V pins because it involves more power regulators that are not as efficient as the PF1550's integrated regulators. 53 | 54 | ## Examples 55 | - [Battery](./examples/Battery/Battery.ino) - Demonstrates battery metrics monitoring. 56 | - [Charger](./examples/Charger/Charger.ino) - Illustrates charger status monitoring and control. 57 | - [Standby_WakeFromPin](./examples/Standby_WakeFromPin/Standby_WakeFromPin.ino) - Demonstrates how to wake up the board from standby using a wakeup pin. 58 | - [Standby_WakeFromRTC_C33](./examples/Standby_WakeFromRTC_C33/Standby_WakeFromRTC_C33.ino) - Demonstrates how to use the RTC to wake the Portenta C33 from standby. 59 | - [Standby_WakeFromRTC_H7](./examples/Standby_WakeFromRTC_H7/Standby_WakeFromRTC_H7.ino) - Demonstrates how to use the RTC to wake the Portenta H7 from standby. 60 | 61 | ## 👀 Instructions 62 | 63 | 1. Check compatibility with your platform 64 | 2. Download and install this library through the Arduino IDE, the Arduino CLI or by manually downloading it. 65 | 3. Check out the examples or read the documentation [here](./docs). 66 | 67 | 68 | ## ✨ Features 69 | - Monitor current and average battery metrics (voltage, current, percentage) 70 | - Monitor battery health metrics (temperature, reported capacity) 71 | - Monitor and control charging 72 | - Save significant amounts of power by sending the boards into Sleep (only available for Portenta C33 for now) and Standby modes 73 | - Toggle and set voltages on different power rails of the board 74 | - On Nicla Vision 75 | - Enable and disable the external power rail 76 | - Enable and disable the camera power rail 77 | - Change voltage on external power rail 78 | - On Portenta C33 79 | - Enable and disable the external power rail 80 | - Enable and disable the communication power rail (ESP32, secure element) 81 | - Change voltage on the external power rail 82 | - On Portenta H7 83 | - Enable and disable the external power rail 84 | - Change voltage on external power rail 85 | 86 | ## 📖 Documentation 87 | For more information on how to use this library please read the documentation [here](./docs). 88 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Arduino PowerManagement Documentation 2 | 3 | ## Usage 4 | 5 | ```cpp 6 | #include "PowerManagement.h" 7 | 8 | Battery battery; 9 | Charger charger; 10 | Board board; 11 | 12 | void setup(){ 13 | battery.begin(); 14 | charger.begin(); 15 | board.begin(); 16 | /* Rest of your setup() code */ 17 | } 18 | ``` 19 | 20 | 21 | ## Battery 22 | The Battery class in the PowerManagement library provides a comprehensive set of tools for monitoring and managing the health and usage of your battery. This includes real-time data on voltage, current, power, temperature, and overall battery capacity, enabling you to optimize your application for better energy efficiency and battery longevity. 23 | 24 | ### Voltage Monitoring 25 | 26 | | Method | Data Type | Description | 27 | |:-------------------------|:------------|:-------------------------------------------------| 28 | | battery.voltage() | float | Read the current voltage of the battery. | 29 | | battery.averageVoltage() | float | Get the average voltage. | 30 | | battery.minimumVoltage() | float | Access the minimum voltage since the last reset. | 31 | | battery.maximumVoltage() | float | Access the maximum voltage since the last reset. | 32 | 33 | ### Current Monitoring 34 | 35 | | Method | Data Type | Description | 36 | |:-------------------------|:------------|:-------------------------------------------------| 37 | | battery.current() | int16_t | Measure the current flow from the battery. | 38 | | battery.averageCurrent() | int16_t | Obtain the average current. | 39 | | battery.minimumCurrent() | int16_t | Access the minimum current since the last reset. | 40 | | battery.maximumCurrent() | int16_t | Access the maximum current since the last reset. | 41 | 42 | ### Power Monitoring 43 | 44 | | Method | Data Type | Description | 45 | |:-----------------------|:------------|:------------------------------------------------------| 46 | | battery.power() | int16_t | Calculate the current power usage in milliwatts (mW). | 47 | | battery.averagePower() | int16_t | Get the average power usage in milliwatts (mW). | 48 | 49 | ### Temperature Monitoring 50 | 51 | | Method | Data Type | Description | 52 | |:-------------------------------------|:------------|:---------------------------------------------------------| 53 | | battery.internalTemperature() | uint8_t | Read the internal temperature of the battery gauge chip. | 54 | | battery.averageInternalTemperature() | uint8_t | Obtain the average internal temperature. | 55 | 56 | ### Capacity and State of Charge 57 | 58 | | Method | Data Type | Description | 59 | |:----------------------------|:------------|:------------------------------------------------------------| 60 | | battery.remainingCapacity() | uint16_t | Monitor the battery's remaining capacity in mAh. | 61 | | battery.percentage() | uint8_t | Get the battery's state of charge as a percentage (0-100%). | 62 | 63 | ### Time Estimates 64 | 65 | | Method | Data Type | Description | 66 | |:----------------------|:------------|:------------------------------------------------------| 67 | | battery.timeToEmpty() | int32_t | Estimate the time until the battery is empty. | 68 | | battery.timeToFull() | int32_t | Estimate the time until the battery is fully charged. | 69 | 70 | ### Configuring Battery Characteristics 71 | 72 | To ensure accurate readings and effective battery management, you can configure the `BatteryCharacteristics` struct to match the specific attributes of your battery: 73 | 74 | ```cpp 75 | BatteryCharacteristics characteristics = BatteryCharacteristics(); 76 | 77 | characteristics.capacity = 200; // Set the battery's capacity in mAh 78 | characteristics.emptyVoltage = 3.3f; // Voltage at which the battery is considered empty 79 | characteristics.chargeVoltage = 4.2f; // Voltage at which the battery is charged 80 | characteristics.endOfChargeCurrent = 50; // End of charge current in mA 81 | characteristics.ntcResistor = NTCResistor::Resistor10K; // Set NTC resistor value (10K or 100K Ohm) 82 | characteristics.recoveryVoltage = 3.88f; // Voltage to reset empty detection 83 | ``` 84 | 85 | This configuration ensures that the Battery class operates with parameters that match your battery’s specifications, providing more accurate and reliable monitoring and management. 86 | 87 | 88 | ## Charger 89 | Charging a LiPo battery is done in three stages. This library allows you to monitor what charging stage we are in as well as control some of the chagring parameters. 90 | 91 | * **Pre-charge** - First phase of the charging process where the battery is charged at a low constant current and is slowly increased until it reaches the full *charge current* 92 | 93 | * **Constant Current** - Second phase of the charging process where the battery is charging in constant current mode until it reaches the voltage where the it's considered fully charged. (4.2V) 94 | 95 | * **Constant Voltage** - Third phase of the charging process where the battery is kept at the fully charged voltage and current is slowly decreased to the *end of charge current*. 96 | 97 | #### Get charger status 98 | You can find out what stage the charger is in by calling the `getState()` method. 99 | 100 | It will return a value of *ChargeStatus* which can be one of the above: 101 | * `preCharge` - First stage of the charging process 102 | * `fastChargeConstantCurrent` - Second stage of the charging process 103 | * `fastChargeConstantVoltage` - Last stage of the charging process 104 | * `endOfCharge` - If the battery is still connected, the charger will ensure it's kept at 4.2V by topping up the voltage to avoid self discharge. 105 | * `done` - Battery is fully charged 106 | * `timerFaultError` - The timer that is monitoring the charge status has encountered an error. 107 | * `thermistorSuspendError` - Charging was suspended due to overheating 108 | * `chargerDisabled` - Charger is disabled 109 | * `batteryOvervoltageError` - Charging was suspended due to an overvoltage fault 110 | * `chargerBypassed` - in this state, the charger is bypassed completely and the USB voltage is powering the board 111 | 112 | #### Set charging parameters 113 | This library allows you to change the following charging parameters of the charging process. Please be careful with these and make sure they are supported by the battery you are using as the wrong values might damage your board or the battery. 114 | 115 | ##### Charge Voltage 116 | Set the charging voltage in volts (V). 117 | Before changing the default values please be aware that changing the charge voltage can damage your battery. 118 | 119 | ```cpp 120 | charger.setChargeVoltage(3.80); 121 | ``` 122 | The current charging voltage is set to 4.2V by default. 123 | This method accepts float values representing voltages from 3.50V to 4.44V in steps of 0.02V (3.50, 3.52, ...., 4.42, 4.44) 124 | 125 | ##### Charge Current 126 | Set the current used in the constant charging phase. This method accepts integer values representing milli amperes (mA). 127 | 128 | ```cpp 129 | charger.setChargeCurrent(1000); 130 | ``` 131 | 132 | The default charging current is set to 100mA. 133 | Supported values: 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000mA 134 | 135 | ##### End of Charge Current 136 | This is the current used in the end-of-charge phase where the voltage is kept at 4.2V. The charger IC determines when to terminate the charge cycle based on the current going into the battery dropping below the given threshold during the constant voltage phase. At this point, the battery is considered fully charged and charging is completed. If charge termination is disabled, the charge current will naturally decay to 0mA, but this is rarely done in practice. 137 | This is because the amount of charge going into the battery exponentially decreases during CV charging, and it would take a significantly longer time to recharge the battery with a very little increase in capacity. 138 | 139 | ```cpp 140 | charger.setEndOfChargeCurrent(5); 141 | ``` 142 | 143 | The default end-of-charge current is set to 5 mA. 144 | Supported values: 5, 10, 20, 30, 50mA. 145 | 146 | 147 | ##### Input Current Limit 148 | The input current limit (ILIM) safeguards the device by preventing overcurrent, ensuring the charging current is within safe levels for the battery, and adapting to the maximum current the power source can provide, allowing you to charge and use the system at the same time. 149 | 150 | ```cpp 151 | charger.setInputCurrentLimit(1500); 152 | ``` 153 | 154 | The default input current limit is set to 1.5A. 155 | Supported values: 10, 15, 20, 25, 30, 35, 40, 45, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1500mA 156 | 157 | ## Board 158 | The PF1550 power management IC has three LDO regulators, and three DCDC converters, each of these have a configurable voltage range and can be turned on and off. 159 | The implementation of these regulators and the power rails rails differs from board to board, for example on the Nicla Vision, some of the rails are dedicated to the voltages required by the camera, while on the Portenta H7 some of these rails are dedicated to the rich USB-C functionality. 160 | 161 | Changing some voltages on some rails might break the boards functionality or even damage the board, so not all of these are available through this library. 162 | 163 | However there is one power rail they all have in common, and that is the external power rail. The external power rail is labeled as 3V3 on all of these boards and can be used to power external peripherals. 164 | 165 | Using this library we can turn it off, and thus the external peripherals to save power: 166 | 167 | ```cpp 168 | board.setExternalSwitch(false); 169 | ``` 170 | 171 | or change its voltage between the following values (1.10V, 1.20V, 1.35V, 1.50V, 1.80V, 2.50V, 3.00V and 3.30V) 172 | 173 | ```cpp 174 | board.setExternalVoltage(1.80); 175 | ``` 176 | 177 | This method takes a float parameter and automatically converts it internally to the specific internal representation, but any voltage that is not one of the enumerated walue will not work and get this method to return false. 178 | 179 | This power rail is the only rail that can be modified on the Portenta H7 board, while Portenta C33 and Nicla Vision have some extra tricks up their sleeves. 180 | 181 | #### Portenta C33 182 | The Portenta C33 board offers the most flexibility in power delivery out of the three boards. It allows you to tweak the ADC voltage as well as the reference voltage, allowing for more precise analog sensor readings, as well as toggle the power to the ESP32 chip used for WiFi and BLE to save power. 183 | 184 | 185 | To turn off the ESP32 and Secure Element this method can be used: 186 | ```cpp 187 | board.setCommunicationSwitch(false); 188 | ``` 189 | **NOTE:** This command turns off the power to the Secure Element as well, so if you would like to connect your board to Ethernet you will lose access to secure communications. 190 | 191 | 192 | 193 | 194 | To change the reference voltage (AREF) use this method: 195 | ```cpp 196 | board.setReferenceVoltage(1.80); 197 | ``` 198 | The reference regulator can be set from 1.80V to 3.30V in steps of 0.10V. Any value outside this range or with different steps will not be accepted by the library. 199 | 200 | To change the analog voltage (AVCC) use this method: 201 | ```cpp 202 | board.setAnalogVoltage(); 203 | ``` 204 | The analog voltage can be set to any of the following values: 0.75V, 0.80V, 0.85V, 0.90V, 0.95V, 1.00V, 1.05V, 1.10V, 1.15V, 1.20V, 1.25V, 1.30V, 1.35V, 1.40V, 1.45V, 1.50V, 1.80V, 1.90V, 2.00V, 2.10V, 2.20V, 2.30V, 2.40V, 2.50V, 2.60V, 2.70V, 2.80V, 2.90V, 3.00V, 3.10V, 3.20V, 3.30V. Any value outside this range or with different steps will not be accepted by the library. 205 | 206 | 207 | #### Nicla Vision 208 | On the Nicla Vision board you can turn the rails that power the cameras and ToF sensor on and off: 209 | ```cpp 210 | board.setCameraSwitch(false); 211 | ``` 212 | 213 | **NOTE:** Any change to the power rails persists even if the board is disconnected from power. Make sure you design your solution accordingly. 214 | 215 | 216 | ## Low Power 217 | 218 | ### Sleep Modes 219 | The Renesas and ST chips that are supported by this library have a slightly different way of handling sleep, and very different ways of calling those modes. For example ST calls the deepest sleep mode *Standby* while Renesas calls the most light sleep mode *Standby*. To reduce the confusion, and to have a universal API for both architectures we have selected two sleep modes and simply called them: **Sleep** and **Standby**: 220 | 221 | #### Sleep 222 | * **Function**: Reduces the microcontroller's power usage to about half of its normal consumption. 223 | * **Effect**: Upon waking up from this mode, the execution of your program resumes exactly where it stopped. This is particularly useful for applications that require a quick resume with minimal power savings. 224 | * **Wake-Up Triggers**: Differ from board to board. 225 | 226 | #### Standby 227 | * **Function**: Significantly reduces power usage to approximately 100uA-300uA (when all peripherals are off), making it ideal for long-term, battery-dependent operations. 228 | * **Effect**: Unlike Sleep Mode, waking up from standby Mode restarts the board, triggering the void setup() function. This behavior is suitable for scenarios where a full reset is acceptable or desired upon waking up. 229 | * **Wake-Up Triggers**: Both board can be configured to wake up either from an RTC alarm or an external interrupt pin. 230 | 231 | 232 | ### Portenta C33 233 | #### Selecting a wakeup source 234 | The wakeup source can be one of the deep-sleep enabled wakeup pins, and an RTC Alarm. You can select multiple pins or the RTC alarm to wake up the board. These sources are the same for both **Sleep** and **Standby** 235 | 236 | ##### Wakeup Pins 237 | This feature can be used when you want to wake up the board from external stimuli, such as sensors or user input. Some sensors have an interrupt pin that you can connect to one of the wakeup pins (eg: most motion sensors), while some output voltage on a pin, (eg: Passive Infrared Sensors or user buttons). 238 | To select a wakeup pin just call `board.setWakeupPin(, )`. The direction can be either **RISING** if you want to wake up when voltage is applied to a pin, or **FALLING** if you want to wake when no voltage is applied anymore. 239 | Here is a list of the usable interrupts: 240 | 241 | | Arduino Pin | MCU PIN | IRQ | 242 | |-------------|---------|---------| 243 | | A0 | P006 | IRQ11| 244 | | A1 | P005 | IRQ10| 245 | | A2 | P004 | IRQ9 | 246 | | A3 | P002 | IRQ8 | 247 | | A4 | P001 | IRQ7 | 248 | | A5 | P015 | IRQ13| 249 | | D4 | P401 | IRQ5 | 250 | | D7 | P402 | IRQ4 | 251 | 252 | > [!IMPORTANT] 253 | > Not all IRQs are created equal, the number of the IRQ represents it's priority. (IRQ0 being the highest priority and IRQ15 the lowest). Be careful when selecting your IRQ pin to make sure the board behaves as expected. 254 | 255 | ##### RTC Alarm 256 | This feature is particularly useful when you want to set the board to wake up at specific times. You can use this in conjunction with the [RTC library](). 257 | To make your board wake up on an RTC alarm you simply need to call `board.setWakeupRTC()` and it will enable that functionality. Check out [this example]() for more details about setting up the RTC. 258 | 259 | To simplify things, we have added a convenience function in `Board` called `sleepFor`. This method takes a number of hours, minutes and seconds as a parameters. For more information, check out the [Standby_WakeFromRTC_C33](https://github.com/arduino-libraries/Arduino_PowerManagement/blob/main/examples/Standby_WakeFromRTC_C33/Standby_WakeFromRTC_C33.ino) example. 260 | 261 | ##### Send the board to sleep 262 | * `board.sleepUntilwakeupEvent();` - Sends the board into the sleep state, where it consumes about ~6mA without peripherals and ~18mA with peripherals. 263 | * `board.standByUntilWakeupEvent();` - Sends the board into the standby state, where it consumes around ~100uA without peripherals and ~12mA with peripherals. 264 | 265 | ##### Toggle peripherals 266 | * `board.turnPeripheralsOff();` - Turn the peripherals on Portenta C33 (ADC, RGB LED, Secure Element, Wifi and Bluetooth) off. 267 | * `board.turnPeripheralsOn();` - Turns them back on. 268 | 269 | #### Low Power Measurements 270 | Here's an overview of the reduction in power usage that you can expect from this library on the Portenta C33. The screenshots below are taken from the nRF Power Profiler application using a Nordic PPK2 while running the blink sketch on the same board. 271 | 272 | #### Without power optimisations 273 | ![](https://raw.githubusercontent.com/arduino-libraries/Arduino_LowPowerPortentaC33/main/docs/assets/normal_usage_blink.png) 274 | 275 | #### Sleep (ADC, RGB LED, Secure Element, Wifi and Bluetooth off) 276 | ![](https://raw.githubusercontent.com/arduino-libraries/Arduino_LowPowerPortentaC33/main/docs/assets/sleep_no_peripherals.png) 277 | 278 | #### Standby (ADC, RGB LED, Secure Element, Wifi and Bluetooth off) 279 | ![](https://raw.githubusercontent.com/arduino-libraries/Arduino_LowPowerPortentaC33/main/docs/assets/deep_sleep_no_peripherals.png) 280 | 281 | #### Sleep (ADC, RGB LED, Secure Element, Wifi and Bluetooth on) 282 | ![](https://raw.githubusercontent.com/arduino-libraries/Arduino_LowPowerPortentaC33/main/docs/assets/sleep_peripherals_on.png) 283 | 284 | #### Standby (ADC, RGB LED, Secure Element, Wifi and Bluetooth on) 285 | ![](https://raw.githubusercontent.com/arduino-libraries/Arduino_LowPowerPortentaC33/main/docs/assets/deep_sleep_peripherals_on.png) 286 | 287 | 288 | ### Portenta H7 Low Power 289 | When utilizing Mbed with STM32-based microcontrollers such as the Portenta H7, the approach to managing sleep modes exhibits some unique characteristics. Mbed is designed to transition the board to a sleep-like state—akin to the Sleep Mode found on the Portenta C33—whenever the system is not actively processing tasks. This energy-saving feature can be activated by invoking the `board.enableSleepWhenIdle()` method within your code. 290 | 291 | However, initiating this command doesn't guarantee automatic entry into sleep mode due to the presence of Sleep Locks. These locks act as safeguards, preventing the system from sleeping under certain conditions to ensure ongoing operations remain uninterrupted. Common peripherals, such as the USB Stack, may engage a sleep lock, effectively keeping the board awake even during periods of apparent inactivity. This behavior underscores how the effectiveness of sleep mode is closely linked to the specific operations and configurations defined in your sketch. 292 | 293 | For those looking to fine-tune their board's energy efficiency by leveraging automatic sleep functionality, a particularly useful resource is [the Sleep Lock Example Sketch](https://github.com/alrvid/Arduino_LowPowerPortentaH7/blob/main/examples/DeepSleepLockDebug_Example/DeepSleepLockDebug_Example.ino). This sketch provides a comprehensive overview of the active sleep locks, offering insights into what may be preventing the board from entering sleep mode and how to address these obstacles. 294 | 295 | #### Send the board to sleep 296 | `board.standByUntilWakeupEvent()` - Sends the board into the standby state, where it consumes around ~100uA and ~300uA without peripherals. 297 | 298 | #### Waking up from GPIO 299 | > [!NOTE] 300 | > There is only one Wake-Up pin on the portenta H7 ``GPIO0`` on the High Density connector. You can access it on the [Arduino Portenta Breakout Board](https://store.arduino.cc/products/arduino-portenta-breakout). 301 | 302 | #### Waking up from RTC 303 | 304 | This feature is particularly useful when you want to set the board to wake up at specific times. To make your board wake up on an RTC alarm you simply need to call `board.setWakeupRTC()` and it will enable that functionality. 305 | 306 | To simplify things, we have added a convenience function in `Board` called `sleepFor`. This method takes a number of hours, minutes and seconds as a parameters. For more information, check out the [Standby_WakeFromRTC_H7](../examples/Standby_WakeFromRTC_H7/Standby_WakeFromRTC_H7.ino) example. 307 | 308 | ```cpp 309 | Board board; 310 | 311 | void setup() { 312 | board.begin(); 313 | board.enableWakeupFromRTC(0, 0, 1); 314 | board.setAllPeripheralsPower(false); 315 | board.standByUntilWakeupEvent(); 316 | } 317 | ``` 318 | 319 | ### Toggle peripherals 320 | * `board.setAllPeripheralsPower(false);` - Turn the peripherals on Portenta C33 (ADC, RGB LED, Secure Element, Wifi and Bluetooth) off. 321 | * `board.setAllPeripheralsPower(true);` - Turns them back on. (should be called as close to the beginning of the `void setup()` method as possible. 322 | 323 | > [!WARNING] 324 | > This method toggles power to important system peripherals like the DRAM, Oscilllators, USB and Ethernet PHY chips. Do set this to `false` unless it's before sending the board to sleep, as it might cause undefined behaviours. 325 | 326 | 327 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | Members | Descriptions 4 | --------------------------------|--------------------------------------------- 5 | `class ` [`Battery`](#class_battery) | This class provides a detailed insight into the battery's health and usage. 6 | `class ` [`Board`](#class_board) | Represents a board with power management capabilities. 7 | `class ` [`Charger`](#class_charger) | Class for controlling charging parameters and monitoring charging status. 8 | `class ` [`MAX1726Driver`](#class_m_a_x1726_driver) | Driver class for the MAX1726 Fuel Gauge IC. 9 | `struct ` [`BatteryCharacteristics`](#struct_battery_characteristics) | This struct contains the characteristics of the battery. 10 | 11 | # class `Battery` 12 | 13 | This class provides a detailed insight into the battery's health and usage. 14 | 15 | ## Summary 16 | 17 | Members | Descriptions 18 | --------------------------------|--------------------------------------------- 19 | | [`Battery`](#class_battery_1a36a6234c583e3b3506f4a77e3eb49989) | Initializes the battery object with default values for capacity (0mAh) and empty voltage (3.3V). | 20 | | [`Battery`](#class_battery_1a64cc106078933ec547418bb649936d0e) | Initializes the battery object with the given battery characteristics. | 21 | | [`begin`](#class_battery_1ad1ef0c48a213f0ad607f08fca6b30d96) | Initializes the battery communication and configuration. | 22 | | [`isConnected`](#class_battery_1af280ded5baecec455934785c317df299) | Checks if a battery is connected to the system. | 23 | | [`voltage`](#class_battery_1add29e37e363157d436d60d2679e4893c) | Reads the current voltage of the battery. Voltage is usually between 3.0V and 4.2V. | 24 | | [`averageVoltage`](#class_battery_1a90aedc34714a42b3ba38e34db6736cf4) | Reads an average of voltage readings of the battery. | 25 | | [`minimumVoltage`](#class_battery_1abde4f669d71076bfc09e4004e3369664) | Returns the minimum voltage value measured since the last device reset. At power-up the minimum voltage value is set to FFh (the maximum). | 26 | | [`maximumVoltage`](#class_battery_1a7baebfaf75aff02842a5eab7ce1629e7) | Returns the maximum voltage value measured since the last device reset. At power-up the maximum voltage value is set to 00h (the minimum). | 27 | | [`resetMaximumMinimumVoltage`](#class_battery_1a2d4bde0fc0207f0a103f67be970cc412) | Resets the minimum and maximum voltage values. | 28 | | [`current`](#class_battery_1addc7658b5425f6bfabb8e55c7f73fd33) | Reads the current flowing from the battery at the moment. Negative values indicate that the battery is charging, positive values indicate that the battery is discharging. When no battery is connected, the value is -1. | 29 | | [`averageCurrent`](#class_battery_1ab74143421658d9aafb05535558a315dc) | Reads an average of current readings of the battery. | 30 | | [`minimumCurrent`](#class_battery_1a04fb9f935964dc4cad7673d581a4e701) | Reads the minimum current values measured since the last device reset. Note: The resolution of the minimum current value is 160mA so the value is rounded to the nearest 160mA. | 31 | | [`maximumCurrent`](#class_battery_1a06bbac275505bd55f54904dde086ea72) | Reads the maximum current values measured since the last device reset. Note: The resolution of the minimum current value is 160mA so the value is rounded to the nearest 160mA. | 32 | | [`resetMaximumMinimumCurrent`](#class_battery_1a86b64cfdb23050cc77a66c1e03750b42) | Resets the minimum and maximum current values. | 33 | | [`power`](#class_battery_1acc75ec11734cdc2f877e11a122ebf164) | Reads the current power of the battery in milliwatts (mW). This value is calculated based on the current and voltage of the battery. | 34 | | [`averagePower`](#class_battery_1a41ff59184770622e77ba133818996793) | Reads an average of power readings of the battery in milliwatts (mW). This value is calculated based on the current and voltage of the battery. | 35 | | [`internalTemperature`](#class_battery_1a3967e87e3ff5bdfbfb99681902d31464) | Reads the current temperature of the internal die of the battery gauge chip. | 36 | | [`averageInternalTemperature`](#class_battery_1a658633cd49a8b594f6a8e75a126e6b5d) | Reads an average of internal temperature readings of the battery. Note: If the battery temperature was read before, this function will change the configuration to read the internal temperature. You will have to await a couple of temperature readings before getting a meaningful average temperature. | 37 | | [`percentage`](#class_battery_1a2f7cd7878c7e3b8227ff38e18c8f65e2) | Reads the battery's state of charge (SOC). This value is based on both the voltage and the current of the battery as well as compensation for the battery's age and temperature and discharge rate. | 38 | | [`remainingCapacity`](#class_battery_1a769e8dddd81779757b8399660b084734) | Reads the remaining capacity of the battery. In combination with [current()](#class_battery_1addc7658b5425f6bfabb8e55c7f73fd33), this value can be used to estimate the remaining time until the battery is empty. | 39 | | [`fullCapacity`](#class_battery_1afb54a909c0fa3e5d7aed34026a441c49) | Returns the full capacity of the battery. For this to work, the capacity of the battery must be set when initializing the battery object. | 40 | | [`isEmpty`](#class_battery_1a91fab7f1d3534f6f657e5868ee9af95a) | Checks if the battery is empty. Returns false once the cell voltage rises above the recovery threshold. | 41 | | [`timeToEmpty`](#class_battery_1af7775216c6a4278af417f8ebdfd94dbe) | Calculates the estimated time until the battery is empty. | 42 | | [`timeToFull`](#class_battery_1a6eaae4e71787908063ad64ee58ba09d2) | Calculates the estimated time until the battery is fully charged. The value is determined by learning from the experience of prior charge cycles. | 43 | 44 | ## Members 45 | 46 | ### `Battery` 47 | 48 | ```cpp 49 | Battery() 50 | ``` 51 | 52 | Initializes the battery object with default values for capacity (0mAh) and empty voltage (3.3V). 53 | 54 |
55 | 56 | ### `Battery` 57 | 58 | ```cpp 59 | Battery( BatteryCharacteristics batteryCharacteristics) 60 | ``` 61 | 62 | Initializes the battery object with the given battery characteristics. 63 | 64 | #### Parameters 65 | * `batteryCharacteristics` The characteristics of the battery. 66 |
67 | 68 | ### `begin` 69 | 70 | ```cpp 71 | bool begin(bool enforceReload) 72 | ``` 73 | 74 | Initializes the battery communication and configuration. 75 | 76 | #### Parameters 77 | * `enforceReload` If set to true, the battery gauge config will be reloaded. 78 | 79 | #### Returns 80 | True if the initialization was successful, false otherwise. 81 |
82 | 83 | ### `isConnected` 84 | 85 | ```cpp 86 | boolean isConnected() 87 | ``` 88 | 89 | Checks if a battery is connected to the system. 90 | 91 | #### Returns 92 | True if a battery is connected, false otherwise 93 |
94 | 95 | ### `voltage` 96 | 97 | ```cpp 98 | float voltage() 99 | ``` 100 | 101 | Reads the current voltage of the battery. Voltage is usually between 3.0V and 4.2V. 102 | 103 | #### Returns 104 | The current voltage in volts (V). 105 |
106 | 107 | ### `averageVoltage` 108 | 109 | ```cpp 110 | float averageVoltage() 111 | ``` 112 | 113 | Reads an average of voltage readings of the battery. 114 | 115 | #### Returns 116 | The average voltage in volts (V). 117 |
118 | 119 | ### `minimumVoltage` 120 | 121 | ```cpp 122 | float minimumVoltage() 123 | ``` 124 | 125 | Returns the minimum voltage value measured since the last device reset. At power-up the minimum voltage value is set to FFh (the maximum). 126 | 127 | #### Returns 128 | The minimum voltage value in volts (V). 129 |
130 | 131 | ### `maximumVoltage` 132 | 133 | ```cpp 134 | float maximumVoltage() 135 | ``` 136 | 137 | Returns the maximum voltage value measured since the last device reset. At power-up the maximum voltage value is set to 00h (the minimum). 138 | 139 | #### Returns 140 | The maximum voltage value in volts (V). 141 |
142 | 143 | ### `resetMaximumMinimumVoltage` 144 | 145 | ```cpp 146 | bool resetMaximumMinimumVoltage() 147 | ``` 148 | 149 | Resets the minimum and maximum voltage values. 150 | 151 | #### Returns 152 | True if the minimum and maximum voltage values were successfully reset, false otherwise. 153 |
154 | 155 | ### `current` 156 | 157 | ```cpp 158 | int16_t current() 159 | ``` 160 | 161 | Reads the current flowing from the battery at the moment. Negative values indicate that the battery is charging, positive values indicate that the battery is discharging. When no battery is connected, the value is -1. 162 | 163 | #### Returns 164 | The current flowing from the battery in milli amperes (mA). 165 |
166 | 167 | ### `averageCurrent` 168 | 169 | ```cpp 170 | int16_t averageCurrent() 171 | ``` 172 | 173 | Reads an average of current readings of the battery. 174 | 175 | #### Returns 176 | The average current in milli amperes (mA). 177 |
178 | 179 | ### `minimumCurrent` 180 | 181 | ```cpp 182 | int16_t minimumCurrent() 183 | ``` 184 | 185 | Reads the minimum current values measured since the last device reset. Note: The resolution of the minimum current value is 160mA so the value is rounded to the nearest 160mA. 186 | 187 | #### Returns 188 | The minimum current values in milli amperes (mA). 189 |
190 | 191 | ### `maximumCurrent` 192 | 193 | ```cpp 194 | int16_t maximumCurrent() 195 | ``` 196 | 197 | Reads the maximum current values measured since the last device reset. Note: The resolution of the minimum current value is 160mA so the value is rounded to the nearest 160mA. 198 | 199 | #### Returns 200 | The maximum current values in milli amperes (mA). 201 |
202 | 203 | ### `resetMaximumMinimumCurrent` 204 | 205 | ```cpp 206 | bool resetMaximumMinimumCurrent() 207 | ``` 208 | 209 | Resets the minimum and maximum current values. 210 | 211 | #### Returns 212 | True if the minimum and maximum current values were successfully reset, false otherwise. 213 |
214 | 215 | ### `power` 216 | 217 | ```cpp 218 | int16_t power() 219 | ``` 220 | 221 | Reads the current power of the battery in milliwatts (mW). This value is calculated based on the current and voltage of the battery. 222 | 223 | #### Returns 224 | The current power in milliwatts (mW). 225 |
226 | 227 | ### `averagePower` 228 | 229 | ```cpp 230 | int16_t averagePower() 231 | ``` 232 | 233 | Reads an average of power readings of the battery in milliwatts (mW). This value is calculated based on the current and voltage of the battery. 234 | 235 | #### Returns 236 | The average power in milliwatts (mW). 237 |
238 | 239 | ### `internalTemperature` 240 | 241 | ```cpp 242 | uint8_t internalTemperature() 243 | ``` 244 | 245 | Reads the current temperature of the internal die of the battery gauge chip. 246 | 247 | #### Returns 248 | The current temperature in degrees Celsius. 249 |
250 | 251 | ### `averageInternalTemperature` 252 | 253 | ```cpp 254 | uint8_t averageInternalTemperature() 255 | ``` 256 | 257 | Reads an average of internal temperature readings of the battery. Note: If the battery temperature was read before, this function will change the configuration to read the internal temperature. You will have to await a couple of temperature readings before getting a meaningful average temperature. 258 | 259 | #### Returns 260 | The average temperature in degrees Celsius. 261 |
262 | 263 | ### `percentage` 264 | 265 | ```cpp 266 | uint8_t percentage() 267 | ``` 268 | 269 | Reads the battery's state of charge (SOC). This value is based on both the voltage and the current of the battery as well as compensation for the battery's age and temperature and discharge rate. 270 | 271 | #### Returns 272 | The state of charge as a percentage (Range: 0% - 100%). 273 |
274 | 275 | ### `remainingCapacity` 276 | 277 | ```cpp 278 | uint16_t remainingCapacity() 279 | ``` 280 | 281 | Reads the remaining capacity of the battery. In combination with [current()](#class_battery_1addc7658b5425f6bfabb8e55c7f73fd33), this value can be used to estimate the remaining time until the battery is empty. 282 | 283 | #### Returns 284 | The remaining capacity in milliampere-hours (mAh). 285 |
286 | 287 | ### `fullCapacity` 288 | 289 | ```cpp 290 | uint16_t fullCapacity() 291 | ``` 292 | 293 | Returns the full capacity of the battery. For this to work, the capacity of the battery must be set when initializing the battery object. 294 | #### Returns 295 | The full capacity of the battery. 296 |
297 | 298 | ### `isEmpty` 299 | 300 | ```cpp 301 | bool isEmpty() 302 | ``` 303 | 304 | Checks if the battery is empty. Returns false once the cell voltage rises above the recovery threshold. 305 | 306 | #### Returns 307 | true if the battery is empty, false otherwise. 308 |
309 | 310 | ### `timeToEmpty` 311 | 312 | ```cpp 313 | int32_t timeToEmpty() 314 | ``` 315 | 316 | Calculates the estimated time until the battery is empty. 317 | #### Returns 318 | The estimated time until the battery is empty, in seconds. If the battery is charging, the function returns -1. 319 |
320 | 321 | ### `timeToFull` 322 | 323 | ```cpp 324 | int32_t timeToFull() 325 | ``` 326 | 327 | Calculates the estimated time until the battery is fully charged. The value is determined by learning from the experience of prior charge cycles. 328 | #### Returns 329 | The estimated time until the battery is fully charged in seconds. If the battery is discharging, the function returns -1. 330 |
331 | 332 | # class `Board` 333 | 334 | Represents a board with power management capabilities. 335 | 336 | The [Board](#class_board) class provides methods to check the power source, enable/disable power rails, set voltage levels, enable/disable wakeup from pins or RTC, put the device into sleep mode for a specified duration, and control peripherals' power. 337 | 338 | Supported boards: Arduino Portenta H7, Arduino Portenta C33, Arduino Nicla Vision. 339 | 340 | ## Summary 341 | 342 | Members | Descriptions 343 | --------------------------------|--------------------------------------------- 344 | | [`Board`](#class_board_1a9ee491d4fea680cf69b033374a9fdfcb) | Construct a new [Board](#class_board) object. | 345 | | [`~Board`](#class_board_1af73f45730119a1fd8f6670f53f959e68) | Destroy the [Board](#class_board) object. | 346 | | [`begin`](#class_board_1a7cd62c2a663226bb02c87675e947ade0) | Initializes the board by initiating the PMIC. | 347 | | [`isUSBPowered`](#class_board_1aa53bea8ac0404de8a7389484a61937ae) | Check if the board is powered through USB. | 348 | | [`isBatteryPowered`](#class_board_1a80a62c172bea4a16b6983f204380f92b) | Check if the board is powered by the battery. | 349 | | [`setExternalPowerEnabled`](#class_board_1a336acc6fbe5c9d3544a14bc47afc0fc8) | Enables/disables the voltage on the external power rail. This lane powers the pin labeled 3V3 on the board. | 350 | | [`setExternalVoltage`](#class_board_1ad92e1939e565023016b210ce6aae68b6) | Set the voltage for the external power rail. This lane powers the pin labeled 3V3 on the board. | 351 | | [`setCameraPowerEnabled`](#class_board_1ad02a62c1b376df7523751fa4b3207204) | Enables/disables the camera's power rail on boards with a built-in camera. | 352 | | [`enableWakeupFromPin`](#class_board_1ae04b853e945b07852a65fb4f0e7e7d2b) | Enables wakeup from pin GPIO0 on Portenta H7. The pin is only accessible via high-density connectors. | 353 | | [`enableSleepWhenIdle`](#class_board_1ae4c9772a307f85306102cf4333ecf249) | Enables sleep mode when the board is idle. | 354 | | [`enableWakeupFromPin`](#class_board_1a94ac54f76a7e8fff5b7c6523af64169a) | Enables wake-up of the device from a specified pin (A0, A1, A2, A3, A4, A5, D4, D7 ) on Arduino Portenta C33. | 355 | | [`enableWakeupFromRTC`](#class_board_1a5fc9e67d4e42ce7943b9675c17fd77f6) | Enables wake-up of the device from the RTC. This function allows to use a custom RTC instance to put the device in sleep mode. | 356 | | [`enableWakeupFromRTC`](#class_board_1a49925de689c2f73f49b09d7b190e5036) | Enables wake-up of the device from the RTC. | 357 | | [`enableWakeupFromRTC`](#class_board_1a27a7db805596399daf0e89de755eca0c) | Enables wake-up of the device from the RTC. | 358 | | [`sleepUntilWakeupEvent`](#class_board_1a1d23524e0bfeb7282fa465f3834027e1) | Put the device into sleep mode until a wakeup event occurs This sleep mode is ideal for applications requiring periodic wake-ups or brief intervals of inactivity and reduces consumption to a range between 6mA and 18mA depending on the state of the peripherals. This sleep mode resumes the operation from the last operation without resetting the board. A wakeup event can be an interrupt on a pin or the RTC, depending on what you set with [enableWakeupFromPin()](#class_board_1ae04b853e945b07852a65fb4f0e7e7d2b) and [enableWakeupFromRTC()](#class_board_1a5fc9e67d4e42ce7943b9675c17fd77f6). | 359 | | [`standByUntilWakeupEvent`](#class_board_1a7dda3836bbe5a11628d4d8a122529ded) | Put the device into standby mode until a wakeup event occurs. For scenarios demanding drastic power conservation, the standby Mode significantly reduces the board's power usage to micro amperes range depending on the state of the peripherals. This mode restarts the board on wake-up, effectively running the setup() function again. A wakeup event can be an interrupt on a pin or the RTC, depending on what you set with [enableWakeupFromPin()](#class_board_1ae04b853e945b07852a65fb4f0e7e7d2b) and [enableWakeupFromRTC()](#class_board_1a5fc9e67d4e42ce7943b9675c17fd77f6). | 360 | | [`setAllPeripheralsPower`](#class_board_1a27dcc0b9d69a8cce256494192f2efb3a) | Toggle the peripherals' power on Portenta C33 (ADC, RGB LED, Secure Element, Wifi and Bluetooth). | 361 | | [`setCommunicationPeripheralsPower`](#class_board_1ac91aa31be1ea833d7266dda45dd1c5f2) | Toggles the communication peripherials' power on Portenta C33 (Wifi, Bluetooth and Secure Element) | 362 | | [`setAnalogDigitalConverterPower`](#class_board_1a3d9fbb30b03c3a7f733f0c89dd9dc43d) | Toggles the power of the analog digital converter on Portenta C33. This is not available on the Portenta H7. | 363 | | [`setReferenceVoltage`](#class_board_1a8feb9efc5439c8fec5139f66484200b5) | Set the reference voltage. This value is used by the ADC to convert analog values to digital values. This can be particularly useful to increase the accuracy of the ADC when working with low voltages. | 364 | | [`shutDownFuelGauge`](#class_board_1ae4af8f64cc1389bc0b640124dbb0d9c8) | Shuts down the fuel gauge to reduce power consumption. The IC returns to active mode on any edge of any communication line. If the IC is power-cycled or the software RESET command is sent the IC returns to active mode of operation. | 365 | 366 | ## Members 367 | 368 | ### `Board` 369 | 370 | ```cpp 371 | Board() 372 | ``` 373 | 374 | Construct a new [Board](#class_board) object. 375 | 376 |
377 | 378 | ### `~Board` 379 | 380 | ```cpp 381 | ~Board() 382 | ``` 383 | 384 | Destroy the [Board](#class_board) object. 385 | 386 |
387 | 388 | ### `begin` 389 | 390 | ```cpp 391 | bool begin() 392 | ``` 393 | 394 | Initializes the board by initiating the PMIC. 395 | 396 | #### Returns 397 | true if the board initialization is successful, false otherwise. 398 |
399 | 400 | ### `isUSBPowered` 401 | 402 | ```cpp 403 | bool isUSBPowered() 404 | ``` 405 | 406 | Check if the board is powered through USB. 407 | 408 | #### Returns 409 | True if powered through USB, false otherwise. 410 |
411 | 412 | ### `isBatteryPowered` 413 | 414 | ```cpp 415 | bool isBatteryPowered() 416 | ``` 417 | 418 | Check if the board is powered by the battery. 419 | 420 | #### Returns 421 | True if powered by the battery, false otherwise. 422 |
423 | 424 | ### `setExternalPowerEnabled` 425 | 426 | ```cpp 427 | void setExternalPowerEnabled(bool on) 428 | ``` 429 | 430 | Enables/disables the voltage on the external power rail. This lane powers the pin labeled 3V3 on the board. 431 | #### Parameters 432 | * `on` True to enable this power rail, false to disable it. 433 |
434 | 435 | ### `setExternalVoltage` 436 | 437 | ```cpp 438 | bool setExternalVoltage(float voltage) 439 | ``` 440 | 441 | Set the voltage for the external power rail. This lane powers the pin labeled 3V3 on the board. 442 | #### Parameters 443 | * `voltage` float value of the voltage value to set. Value has to be one of the following (1.10, 1.20, 1.35, 1.50, 1.80, 2.50, 3.00, 3.30) 444 | 445 | #### Returns 446 | True the voltage was set successfully, false otherwise. 447 |
448 | 449 | ### `setCameraPowerEnabled` 450 | 451 | ```cpp 452 | void setCameraPowerEnabled(bool enabled) 453 | ``` 454 | 455 | Enables/disables the camera's power rail on boards with a built-in camera. 456 | 457 | #### Parameters 458 | * `enabled` True to turn on the camera, false to turn it off. 459 |
460 | 461 | ### `enableWakeupFromPin` 462 | 463 | ```cpp 464 | void enableWakeupFromPin() 465 | ``` 466 | 467 | Enables wakeup from pin GPIO0 on Portenta H7. The pin is only accessible via high-density connectors. 468 |
469 | 470 | ### `enableSleepWhenIdle` 471 | 472 | ```cpp 473 | void enableSleepWhenIdle() 474 | ``` 475 | 476 | Enables sleep mode when the board is idle. 477 |
478 | 479 | ### `enableWakeupFromPin` 480 | 481 | ```cpp 482 | void enableWakeupFromPin(uint8_t pin, PinStatus direction) 483 | ``` 484 | 485 | Enables wake-up of the device from a specified pin (A0, A1, A2, A3, A4, A5, D4, D7 ) on Arduino Portenta C33. 486 | #### Parameters 487 | * `pin` The pin number used for waking up the device. 488 | 489 | * `direction` The direction of the interrupt that will wake up the device. (RISING, FALLING) 490 |
491 | 492 | ### `enableWakeupFromRTC` 493 | 494 | ```cpp 495 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, void(*)() callbackFunction, RTClock * rtc) 496 | ``` 497 | 498 | Enables wake-up of the device from the RTC. This function allows to use a custom RTC instance to put the device in sleep mode. 499 | 500 | #### Parameters 501 | * `hours` The number of hours to sleep. 502 | 503 | * `minutes` The number of minutes to sleep. 504 | 505 | * `seconds` The number of seconds to sleep. 506 | 507 | * `callbackFunction` The function to call when the device wakes up. If no callback function is provided, the device will wake up without calling any function. 508 | 509 | * `rtc` The RTC instance to use for the sleep function. If no RTC instance is provided, the default RTC instance is used. 510 | 511 | #### Returns 512 | True if successful, false otherwise. 513 |
514 | 515 | ### `enableWakeupFromRTC` 516 | 517 | ```cpp 518 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, RTClock * rtc) 519 | ``` 520 | 521 | Enables wake-up of the device from the RTC. 522 | 523 | #### Parameters 524 | * `hours` The number of hours to sleep. 525 | 526 | * `minutes` The number of minutes to sleep. 527 | 528 | * `seconds` The number of seconds to sleep. 529 | 530 | * `rtc` The RTC instance to use for the sleep function. Default is the shared RTC instance. 531 | 532 | #### Returns 533 | True if successful, false otherwise. 534 |
535 | 536 | ### `enableWakeupFromRTC` 537 | 538 | ```cpp 539 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds) 540 | ``` 541 | 542 | Enables wake-up of the device from the RTC. 543 | #### Parameters 544 | * `hours` The number of hours to sleep. 545 | 546 | * `minutes` The number of minutes to sleep. 547 | 548 | * `seconds` The number of seconds to sleep. 549 | 550 | #### Returns 551 | True if successful, false otherwise. 552 |
553 | 554 | ### `sleepUntilWakeupEvent` 555 | 556 | ```cpp 557 | void sleepUntilWakeupEvent() 558 | ``` 559 | 560 | Put the device into sleep mode until a wakeup event occurs This sleep mode is ideal for applications requiring periodic wake-ups or brief intervals of inactivity and reduces consumption to a range between 6mA and 18mA depending on the state of the peripherals. This sleep mode resumes the operation from the last operation without resetting the board. A wakeup event can be an interrupt on a pin or the RTC, depending on what you set with [enableWakeupFromPin()](#class_board_1ae04b853e945b07852a65fb4f0e7e7d2b) and [enableWakeupFromRTC()](#class_board_1a5fc9e67d4e42ce7943b9675c17fd77f6). 561 |
562 | 563 | ### `standByUntilWakeupEvent` 564 | 565 | ```cpp 566 | void standByUntilWakeupEvent() 567 | ``` 568 | 569 | Put the device into standby mode until a wakeup event occurs. For scenarios demanding drastic power conservation, the standby Mode significantly reduces the board's power usage to micro amperes range depending on the state of the peripherals. This mode restarts the board on wake-up, effectively running the setup() function again. A wakeup event can be an interrupt on a pin or the RTC, depending on what you set with [enableWakeupFromPin()](#class_board_1ae04b853e945b07852a65fb4f0e7e7d2b) and [enableWakeupFromRTC()](#class_board_1a5fc9e67d4e42ce7943b9675c17fd77f6). 570 |
571 | 572 | ### `setAllPeripheralsPower` 573 | 574 | ```cpp 575 | void setAllPeripheralsPower(bool on) 576 | ``` 577 | 578 | Toggle the peripherals' power on Portenta C33 (ADC, RGB LED, Secure Element, Wifi and Bluetooth). 579 | 580 | #### Parameters 581 | * `on` True to turn on the power, false to turn it off. 582 |
583 | 584 | ### `setCommunicationPeripheralsPower` 585 | 586 | ```cpp 587 | void setCommunicationPeripheralsPower(bool on) 588 | ``` 589 | 590 | Toggles the communication peripherials' power on Portenta C33 (Wifi, Bluetooth and Secure Element) 591 | 592 | #### Parameters 593 | * `on` True to turn on the power, false to turn it off. 594 |
595 | 596 | ### `setAnalogDigitalConverterPower` 597 | 598 | ```cpp 599 | void setAnalogDigitalConverterPower(bool on) 600 | ``` 601 | 602 | Toggles the power of the analog digital converter on Portenta C33. This is not available on the Portenta H7. 603 | 604 | #### Parameters 605 | * `on` True to turn on the power, false to turn it off. 606 |
607 | 608 | ### `setReferenceVoltage` 609 | 610 | ```cpp 611 | bool setReferenceVoltage(float voltage) 612 | ``` 613 | 614 | Set the reference voltage. This value is used by the ADC to convert analog values to digital values. This can be particularly useful to increase the accuracy of the ADC when working with low voltages. 615 | 616 | #### Parameters 617 | * `voltage` Reference voltage value in volts. It can be anything between 1.80V and 3.30V in steps of 0.10V. Any value outside this range or with different steps will not be accepted by the library. 618 | 619 | #### Returns 620 | True if the voltage was set successfully, false otherwise. 621 |
622 | 623 | ### `shutDownFuelGauge` 624 | 625 | ```cpp 626 | void shutDownFuelGauge() 627 | ``` 628 | 629 | Shuts down the fuel gauge to reduce power consumption. The IC returns to active mode on any edge of any communication line. If the IC is power-cycled or the software RESET command is sent the IC returns to active mode of operation. 630 | 631 |
632 | 633 | # class `Charger` 634 | 635 | Class for controlling charging parameters and monitoring charging status. 636 | 637 | ## Summary 638 | 639 | Members | Descriptions 640 | --------------------------------|--------------------------------------------- 641 | | [`Charger`](#class_charger_1a386eef9a5c151f3e5eb3ee67a0aeb0cb) | Constructs a new [Charger](#class_charger) object. | 642 | | [`begin`](#class_charger_1af3ebf5b1bbd6c4909f47217b83fe57ab) | Initializes the charger by initiating the PMIC. | 643 | | [`setChargeCurrent`](#class_charger_1a7092cf5385bf469d3b27204478fb1671) | Set the charging current. The default charging current is set to 100mA. | 644 | | [`getChargeCurrent`](#class_charger_1a3ff22dda98f766f42a3a7fdfc46d7a41) | Get the charge current in milli amperes (mA). | 645 | | [`setChargeVoltage`](#class_charger_1a46cee977438a93ef66ed3882d741efbc) | Set the charging voltage in volts (V). The current charging voltage is set to 4.2V by default. | 646 | | [`getChargeVoltage`](#class_charger_1af478d3be4756b758238823696c1a00c8) | Get the charge voltage in volts (V). | 647 | | [`setEndOfChargeCurrent`](#class_charger_1a9b54c1077cc45ed826b8269ff57a3ee2) | Set the end-of-charge current. The charger IC determines when to terminate the charge cycle based on the current going into the battery dropping below the given threshold during the constant voltage phase. At this point, the battery is considered fully charged and charging is completed. If charge termination is disabled, the charge current will naturally decay to 0mA, but this is rarely done in practice. This is because the amount of charge going into the battery exponentially decreases during CV charging, and it would take a significantly longer time to recharge the battery with a very little increase in capacity. | 648 | | [`getEndOfChargeCurrent`](#class_charger_1ae74b5ab591bd666c47cd133d2855152c) | Get the end of charge current. | 649 | | [`setInputCurrentLimit`](#class_charger_1a1c4b5f61bd84e067c2643a0da1ccd1a7) | The input current limit (ILIM) safeguards the device by preventing overcurrent, ensuring the charging current is within safe levels for the battery, and adapting to the maximum current the power source can provide, allowing you to charge and use the system at the same time. The default input current limit is set to 1.5A. | 650 | | [`getInputCurrentLimit`](#class_charger_1a768963364778f838e4b44463c020b173) | Get the input current limit. It is a safeguard to prevent overcurrent when charging respectively to the maximum current the power source can provide. | 651 | | [`getState`](#class_charger_1a4670cf2d22eb36216e7ef2b65077021a) | Get the current charging status. | 652 | | [`isEnabled`](#class_charger_1a908179315a74d96f27fbb86858ae4892) | Checks if the charger and thus charging is enabled. By default, the charger is enabled. | 653 | | [`setEnabled`](#class_charger_1a4db5462b2061801340f022f2cd35e6c0) | Sets the enabled state of the charger. When enabling it uses the default settings or the last saved parameters, depending on what was set previously. | 654 | 655 | ## Members 656 | 657 | ### `Charger` 658 | 659 | ```cpp 660 | Charger() 661 | ``` 662 | 663 | Constructs a new [Charger](#class_charger) object. 664 | 665 |
666 | 667 | ### `begin` 668 | 669 | ```cpp 670 | bool begin() 671 | ``` 672 | 673 | Initializes the charger by initiating the PMIC. 674 | 675 | #### Returns 676 | true if the charger initialization is successful, false otherwise. 677 |
678 | 679 | ### `setChargeCurrent` 680 | 681 | ```cpp 682 | bool setChargeCurrent(uint16_t current) 683 | ``` 684 | 685 | Set the charging current. The default charging current is set to 100mA. 686 | 687 | #### Parameters 688 | * `current` Charging current in milli amperes (mA). Supported values: 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000 689 | 690 | #### Returns 691 | True if successful, false if an invalid value was provided or if the PMIC communication failed. 692 |
693 | 694 | ### `getChargeCurrent` 695 | 696 | ```cpp 697 | uint16_t getChargeCurrent() 698 | ``` 699 | 700 | Get the charge current in milli amperes (mA). 701 | 702 | #### Returns 703 | The charge current in float. 704 |
705 | 706 | ### `setChargeVoltage` 707 | 708 | ```cpp 709 | bool setChargeVoltage(float voltage) 710 | ``` 711 | 712 | Set the charging voltage in volts (V). The current charging voltage is set to 4.2V by default. 713 | 714 | #### Parameters 715 | * `voltage` Charging voltage in volts (V). Supported values: 3.50, 3.52, 3.54, 3.56, 3.58, 3.60, 3.62, 3.64, 3.66, 3.68, 3.70, 3.72, 3.74, 3.76, 3.78, 3.80, 3.82, 3.84, 3.86, 3.88, 3.90, 3.92, 3.94, 3.96, 3.98, 4.00, 4.02, 4.04, 4.06, 4.08, 4.10, 4.12, 4.14, 4.16, 4.18, 4.20, 4.22, 4.24, 4.26, 4.28, 4.30, 4.32, 4.34, 4.36, 4.38, 4.40, 4.42, 4.44 716 | 717 | #### Returns 718 | True if successful, false if an invalid value was provided or if the PMIC communication failed. 719 |
720 | 721 | ### `getChargeVoltage` 722 | 723 | ```cpp 724 | float getChargeVoltage() 725 | ``` 726 | 727 | Get the charge voltage in volts (V). 728 | 729 | #### Returns 730 | The charge voltage as a float value. 731 |
732 | 733 | ### `setEndOfChargeCurrent` 734 | 735 | ```cpp 736 | bool setEndOfChargeCurrent(uint16_t current) 737 | ``` 738 | 739 | Set the end-of-charge current. The charger IC determines when to terminate the charge cycle based on the current going into the battery dropping below the given threshold during the constant voltage phase. At this point, the battery is considered fully charged and charging is completed. If charge termination is disabled, the charge current will naturally decay to 0mA, but this is rarely done in practice. This is because the amount of charge going into the battery exponentially decreases during CV charging, and it would take a significantly longer time to recharge the battery with a very little increase in capacity. 740 | 741 | #### Parameters 742 | * `current` End-of-charge current in milli amperes (mA). The default end-of-charge current is set to 50 mA. Supported values: 5, 10, 20, 30, 50 743 | 744 | #### Returns 745 | True if successful, false if an invalid value was provided or if the PMIC communication failed. 746 |
747 | 748 | ### `getEndOfChargeCurrent` 749 | 750 | ```cpp 751 | uint16_t getEndOfChargeCurrent() 752 | ``` 753 | 754 | Get the end of charge current. 755 | 756 | This function returns the current value at which the charging process is considered complete. Charging is terminated when the supplied current drops below the pre-programmed end of charge level. 757 | 758 | #### Returns 759 | The end of charge current. 760 |
761 | 762 | ### `setInputCurrentLimit` 763 | 764 | ```cpp 765 | bool setInputCurrentLimit(uint16_t current) 766 | ``` 767 | 768 | The input current limit (ILIM) safeguards the device by preventing overcurrent, ensuring the charging current is within safe levels for the battery, and adapting to the maximum current the power source can provide, allowing you to charge and use the system at the same time. The default input current limit is set to 1.5A. 769 | 770 | #### Parameters 771 | * `current` Maximum input current in milli amperes (mA). Supported values: 10, 15, 20, 25, 30, 35, 40, 45, 50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1500 772 | 773 | #### Returns 774 | True if successful, false if an invalid value was provided or if the PMIC communication failed. 775 |
776 | 777 | ### `getInputCurrentLimit` 778 | 779 | ```cpp 780 | uint16_t getInputCurrentLimit() 781 | ``` 782 | 783 | Get the input current limit. It is a safeguard to prevent overcurrent when charging respectively to the maximum current the power source can provide. 784 | 785 | #### Returns 786 | The input current limit in milli amperes (mA). 787 |
788 | 789 | ### `getState` 790 | 791 | ```cpp 792 | ChargingState getState() 793 | ``` 794 | 795 | Get the current charging status. 796 | 797 | #### Returns 798 | Charging status enum value (ChargingState). The possible states are: 799 | 800 | * none: Provided by the registers, not used in this API. 801 | 802 | * preCharge: First stage of the charging process, prepares battery for the charging process. 803 | 804 | * fastChargeConstantCurrent: Second phase of the charging process where the battery is charging in constant current mode until it reaches the voltage where the it's considered fully charged. (4.2V) 805 | 806 | * fastChargeConstantVoltage: Third phase of the charging process where the battery is kept at the fully charged voltage and current is slowly decreased to the end of charge current. 807 | 808 | * endOfCharge: If the battery is still connected, the charger will ensure it's kept at 4.2V by topping up the voltage to avoid self discharge. 809 | 810 | * done: [Battery](#class_battery) is fully charged 811 | 812 | * timerFaultError: The timer that is monitoring the charge status has encountered an error. 813 | 814 | * thermistorSuspendError: Charging was suspended due to overheating 815 | 816 | * chargerDisabled: [Charger](#class_charger) is disabled 817 | 818 | * batteryOvervoltageError: Charging was suspended due to an overvoltage fault 819 | 820 | * chargerBypassed: The charger is bypassed completely and the USB voltage is powering the board 821 |
822 | 823 | ### `isEnabled` 824 | 825 | ```cpp 826 | bool isEnabled() 827 | ``` 828 | 829 | Checks if the charger and thus charging is enabled. By default, the charger is enabled. 830 | 831 | #### Returns 832 | true if the charger is enabled, false otherwise. 833 |
834 | 835 | ### `setEnabled` 836 | 837 | ```cpp 838 | bool setEnabled(bool enabled) 839 | ``` 840 | 841 | Sets the enabled state of the charger. When enabling it uses the default settings or the last saved parameters, depending on what was set previously. 842 | 843 | #### Parameters 844 | * `enabled` The desired enabled state of the charger. 845 | 846 | #### Returns 847 | true if the enabled state was successfully set, false otherwise. 848 |
849 | 850 | # class `MAX1726Driver` 851 | 852 | Driver class for the MAX1726 Fuel Gauge IC. 853 | 854 | ## Summary 855 | 856 | Members | Descriptions 857 | --------------------------------|--------------------------------------------- 858 | | [`chargingComplete`](#class_m_a_x1726_driver_1a9c7c3d11d9dddc7667eafba19c52e65f) | Checks if the battery charging is complete. | 859 | | [`setOperationMode`](#class_m_a_x1726_driver_1a13aeedba15e50b2fd2dada0a8ab0e5cd) | Sets the operation mode of the Fuel Gauge. | 860 | | [`MAX1726Driver`](#class_m_a_x1726_driver_1af68eee6cc8a3d2a9fbbb0097b55e7ec6) | Constructs a new [MAX1726Driver](#class_m_a_x1726_driver) object. | 861 | | [`~MAX1726Driver`](#class_m_a_x1726_driver_1a29bb1df9c115d16da9e4ce3afc99a992) | | 862 | 863 | ## Members 864 | 865 | ### `chargingComplete` 866 | 867 | ```cpp 868 | bool chargingComplete() 869 | ``` 870 | 871 | Checks if the battery charging is complete. 872 | 873 | Checks if the charging process is complete. 874 | 875 | #### Returns 876 | true if the charging process is complete, false otherwise. 877 | 878 | #### Returns 879 | true if the charging is complete, false otherwise. 880 |
881 | 882 | ### `setOperationMode` 883 | 884 | ```cpp 885 | bool setOperationMode(FuelGaugeOperationMode mode) 886 | ``` 887 | 888 | Sets the operation mode of the Fuel Gauge. 889 | 890 | #### Parameters 891 | * `mode` The operation mode to set. Possible values are: hibernate, shutdown, active. 892 | 893 | #### Returns 894 | True if the operation mode was set successfully, false otherwise. 895 |
896 | 897 | ### `MAX1726Driver` 898 | 899 | ```cpp 900 | MAX1726Driver(TwoWire * wire, uint8_t i2cAddress) 901 | ``` 902 | 903 | Constructs a new [MAX1726Driver](#class_m_a_x1726_driver) object. 904 | 905 | #### Parameters 906 | * `wire` Pointer to the TwoWire object for I2C communication. 907 | 908 | * `i2cAddress` The I2C address of the MAX1726 device. The default value is 0x36. 909 |
910 | 911 | ### `~MAX1726Driver` 912 | 913 | ```cpp 914 | ~MAX1726Driver() 915 | ``` 916 | 917 |
918 | 919 | # struct `BatteryCharacteristics` 920 | 921 | This struct contains the characteristics of the battery. 922 | 923 | ## Summary 924 | 925 | Members | Descriptions 926 | --------------------------------|--------------------------------------------- 927 | | [`capacity`](#struct_battery_characteristics_1a6bd20a3ca22801553596234c7d80014c) | The battery's capacity in milliampere-hours (mAh). | 928 | | [`emptyVoltage`](#struct_battery_characteristics_1af8847c64e47bd1ee07a2234572658baf) | The voltage in volts (V) at which the battery is considered empty. If you don't know this value you can use the minimumVoltage() function to find out while you let the battery completely discharge. | 929 | | [`chargeVoltage`](#struct_battery_characteristics_1a1278657cc6c246d6545a3770d213cf91) | The voltage in volts (V) at which the battery is being charged. | 930 | | [`endOfChargeCurrent`](#struct_battery_characteristics_1a13c651b4da45ab04ec9935023257aec2) | The current in milli amperes (mA) that is used to keep the battery charged at the end of the charging process. | 931 | | [`ntcResistor`](#struct_battery_characteristics_1ab5b19a09ad5e5d61c1ad72bdf4de4fb3) | The NTC resistor value used in the battery pack (10K or 100K Ohm). | 932 | | [`recoveryVoltage`](#struct_battery_characteristics_1aff1e9cdf23f2a9ff9e23da138f1791b4) | Sets the voltage level for clearing empty detection. Once the cell voltage rises above this point, empty voltage detection is re-enabled. | 933 | 934 | ## Members 935 | 936 | ### `capacity` 937 | 938 | ```cpp 939 | int capacity 940 | ``` 941 | 942 | The battery's capacity in milliampere-hours (mAh). 943 | 944 |
945 | 946 | ### `emptyVoltage` 947 | 948 | ```cpp 949 | float emptyVoltage 950 | ``` 951 | 952 | The voltage in volts (V) at which the battery is considered empty. If you don't know this value you can use the minimumVoltage() function to find out while you let the battery completely discharge. 953 | 954 |
955 | 956 | ### `chargeVoltage` 957 | 958 | ```cpp 959 | float chargeVoltage 960 | ``` 961 | 962 | The voltage in volts (V) at which the battery is being charged. 963 | 964 |
965 | 966 | ### `endOfChargeCurrent` 967 | 968 | ```cpp 969 | int endOfChargeCurrent 970 | ``` 971 | 972 | The current in milli amperes (mA) that is used to keep the battery charged at the end of the charging process. 973 | 974 |
975 | 976 | ### `ntcResistor` 977 | 978 | ```cpp 979 | NTCResistor ntcResistor 980 | ``` 981 | 982 | The NTC resistor value used in the battery pack (10K or 100K Ohm). 983 | 984 |
985 | 986 | ### `recoveryVoltage` 987 | 988 | ```cpp 989 | float recoveryVoltage 990 | ``` 991 | 992 | Sets the voltage level for clearing empty detection. Once the cell voltage rises above this point, empty voltage detection is re-enabled. 993 | 994 |
995 | 996 | -------------------------------------------------------------------------------- /docs/assets/H7_deep_sleep_peripherals_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_PowerManagement/867ee03c07652ed7b95302d7338ce41f03f2c7ff/docs/assets/H7_deep_sleep_peripherals_off.png -------------------------------------------------------------------------------- /docs/assets/H7_deep_sleep_peripherals_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_PowerManagement/867ee03c07652ed7b95302d7338ce41f03f2c7ff/docs/assets/H7_deep_sleep_peripherals_on.png -------------------------------------------------------------------------------- /docs/assets/H7_lite_deep_sleep_peripherals_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_PowerManagement/867ee03c07652ed7b95302d7338ce41f03f2c7ff/docs/assets/H7_lite_deep_sleep_peripherals_off.png -------------------------------------------------------------------------------- /docs/assets/H7_lite_deep_sleep_peripherals_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_PowerManagement/867ee03c07652ed7b95302d7338ce41f03f2c7ff/docs/assets/H7_lite_deep_sleep_peripherals_on.png -------------------------------------------------------------------------------- /docs/assets/normal_usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arduino-libraries/Arduino_PowerManagement/867ee03c07652ed7b95302d7338ce41f03f2c7ff/docs/assets/normal_usage.png -------------------------------------------------------------------------------- /examples/Battery/Battery.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Battery Information Display Demo 3 | 4 | This sketch demonstrates how to read battery information using the PowerManagement library. 5 | It prints the battery's voltage, current, percentage, remaining capacity and temperature in a loop. 6 | The sketch is designed to work with the PowerManagement library and is intended for educational purposes. 7 | 8 | Requirements: 9 | - Arduino Portenta C33, Arduino Portenta H7, Arduino Nicla Vision 10 | - Arduino IDE / Arduino CLI 11 | - PowerManagement library (installable from the Arduino Library Manager) 12 | 13 | Usage: 14 | 15 | 1. Connect the Battery: 16 | - Please note that batteries without NTC thermistors will 17 | not provide temperature information. 18 | 19 | 2. Upload the Sketch: 20 | - Open the provided sketch in the Arduino IDE. 21 | - Select your board type and port from the "Tools" menu. 22 | - Click the "Upload" button to upload the sketch to your board. 23 | 24 | 3. Monitor Serial Output: 25 | - Open the Serial Monitor in the Arduino IDE. 26 | - Set the baud rate to 115200. 27 | - You will see the sketch continuously printing battery information. 28 | 29 | Initial author: Sebastian Romero (s.romero@arduino.cc) 30 | */ 31 | 32 | #include "Arduino_PowerManagement.h" 33 | 34 | Battery battery; 35 | Charger charger; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | while (!Serial); 40 | delay(1000); // Delay to give time to load the Serial Monitor 41 | 42 | charger.begin(); 43 | Serial.println("* 🔌 Charger initialized."); 44 | auto chargeVoltage = charger.getChargeVoltage(); 45 | auto endOfChargeCurrent = charger.getEndOfChargeCurrent(); 46 | Serial.println("* ⚡️ Charge voltage: " + String(chargeVoltage) + " V"); 47 | Serial.println("* ⚡️ End of charge current: " + String(endOfChargeCurrent) + " mA"); 48 | 49 | BatteryCharacteristics characteristics = BatteryCharacteristics(); 50 | characteristics.capacity = 200; // Battery capacity in mAh. Change this value to match your battery's capacity. 51 | characteristics.ntcResistor = NTCResistor::Resistor10K; // NTC resistor value 10 or 100 kOhm 52 | characteristics.endOfChargeCurrent = endOfChargeCurrent; // End of charge current in mA 53 | characteristics.chargeVoltage = chargeVoltage; // Charge voltage in V 54 | 55 | battery = Battery(characteristics); 56 | bool batteryInitialized = battery.begin(true); 57 | 58 | if (!batteryInitialized) { 59 | Serial.println("Battery initialization failed."); 60 | Serial.println("Please make sure the battery is connected and try again."); 61 | while (true); 62 | } 63 | 64 | battery.resetMaximumMinimumVoltage(); 65 | battery.resetMaximumMinimumCurrent(); 66 | } 67 | 68 | void printTimeToEmpty(){ 69 | auto timeToEmptySeconds = battery.timeToEmpty(); 70 | if(timeToEmptySeconds == -1){ 71 | Serial.println("* ⏱️ Time to empty: N/A"); 72 | return; 73 | } 74 | auto timeToEmptyHours = timeToEmptySeconds / 3600; 75 | timeToEmptySeconds = timeToEmptySeconds % 3600; 76 | auto timeToEmptyMinutes = timeToEmptySeconds / 60; 77 | timeToEmptySeconds = timeToEmptySeconds % 60; 78 | Serial.println("* ⏱️ Time to empty: " + String(timeToEmptyHours) + "h " + String(timeToEmptyMinutes) + "m " + String(timeToEmptySeconds) + "s"); 79 | } 80 | 81 | void printTimeToFull(){ 82 | auto timeToFullSeconds = battery.timeToFull(); 83 | if(timeToFullSeconds == -1){ 84 | Serial.println("* ⏱️ Time to full: N/A"); 85 | return; 86 | } 87 | auto timeToFullHours = timeToFullSeconds / 3600; 88 | timeToFullSeconds = timeToFullSeconds % 3600; 89 | auto timeToFullMinutes = timeToFullSeconds / 60; 90 | timeToFullSeconds = timeToFullSeconds % 60; 91 | Serial.println("* ⏱️ Time to full: " + String(timeToFullHours) + "h " + String(timeToFullMinutes) + "m " + String(timeToFullSeconds) + "s"); 92 | } 93 | 94 | void loop() { 95 | bool batteryConnected = battery.isConnected(); 96 | Serial.println("* 🔌 Battery is connected: " + ( batteryConnected ? String("Yes") : String("No"))); 97 | 98 | if(batteryConnected){ 99 | Serial.println("* 🪫 Battery is empty: " + ( battery.isEmpty() ? String("Yes") : String("No"))); 100 | 101 | Serial.println("* ⚡️ Voltage: " + String(battery.voltage()) + " V"); 102 | Serial.println("* ⚡️ Average Voltage: " + String(battery.averageVoltage()) + " V"); 103 | Serial.println("* ⚡️ Minimum Voltage since reset: " + String(battery.minimumVoltage()) + " V"); 104 | Serial.println("* ⚡️ Maximum Voltage since reset: " + String(battery.maximumVoltage()) + " V"); 105 | Serial.println("* ⚡️ Current: " + String(battery.current()) + " mA"); 106 | Serial.println("* ⚡️ Average Current: " + String(battery.averageCurrent()) + " mA"); 107 | Serial.println("* ⚡️ Minimum Current since reset (160mA resolution): " + String(battery.minimumCurrent()) + " mA"); 108 | Serial.println("* ⚡️ Maximum Current since reset (160mA resolution): " + String(battery.maximumCurrent()) + " mA"); 109 | Serial.println("* ⚡️ Power: " + String(battery.power()) + " mW"); 110 | Serial.println("* ⚡️ Average Power: " + String(battery.averagePower()) + " mW"); 111 | 112 | Serial.println("* 🔋 Percentage: " + String(battery.percentage()) + "%"); 113 | Serial.println("* 🔋 Remaining Capacity: " + String(battery.remainingCapacity()) + " mAh"); 114 | Serial.println("* 🔋 Full Capacity: " + String(battery.fullCapacity()) + " mAh"); 115 | Serial.println("* 🌡️ Internal Temperature: " + String(battery.internalTemperature()) + "°C"); 116 | Serial.println("* 🌡️ Average internal Temperature: " + String(battery.averageInternalTemperature()) + "°C"); 117 | printTimeToEmpty(); 118 | printTimeToFull(); 119 | Serial.println(); 120 | } 121 | delay(2000); 122 | } 123 | -------------------------------------------------------------------------------- /examples/Charger/Charger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Charger Demo 3 | 4 | This sketch demonstrates how to write charging parameters,read charger state and monitor charging using the PowerManagement library: 5 | * In the setup() function, it sets the charging parameters 6 | * In the loop() functionit prints the battery's voltage, current, percentage and the charger's state allowing you to monitor charging. 7 | 8 | Requirements: 9 | - Arduino Portenta C33, Arduino Portenta H7, Arduino Nicla Vision 10 | - Arduino IDE 11 | - PowerManagement library (installable from the Arduino Library Manager) 12 | 13 | Usage: 14 | 15 | 1. Connect a Battery to the board 16 | 17 | 2. Upload the Sketch: 18 | - Open the provided sketch in the Arduino IDE. 19 | - Select your board type and port from the "Tools" menu. 20 | - Click the "Upload" button to upload the sketch to your board. 21 | 22 | 3. Monitor Serial Output: 23 | - Open the Serial Monitor in the Arduino IDE. 24 | - Set the baud rate to 115200. 25 | - You will see the sketch continuously printing charger state information. 26 | 27 | Please note that the Portenta C33 will not charge batteries that do not have an NTC. 28 | 29 | Initial authors: 30 | Cristian Dragomir (c.dragomir@arduino.cc) 31 | Sebastian Romero (s.romero@arduino.cc) 32 | */ 33 | 34 | #include "Arduino_PowerManagement.h" 35 | 36 | Charger charger; 37 | 38 | // Charge current in mA, a safe value for most batteries is half the battery capacity 39 | constexpr int CHARGE_CURRENT_MA = 100; // mA 40 | 41 | // End of charge current in mA, a safe value for most batteries is 5% of the battery capacity 42 | constexpr int END_OF_CHARGE_CURRENT_MA = 5; // mA 43 | 44 | void setup() { 45 | Serial.begin(115200); 46 | // Wait for Serial to be ready with a timeout of 5 seconds 47 | for (auto start = millis(); !Serial && millis() - start < 5000;); 48 | delay(1000); // Delay to give time to load the Serial Monitor 49 | 50 | if(!charger.begin()){ 51 | Serial.println("Charger initialization failed."); 52 | while (true); 53 | } 54 | 55 | Serial.print("* ✅ Charging is enabled: "); 56 | Serial.println(charger.isEnabled() ? "true" : "false"); 57 | 58 | auto chargeVoltage = charger.getChargeVoltage(); 59 | auto inputCurrentLimit = charger.getInputCurrentLimit(); 60 | 61 | Serial.println("* ⚡️ Charge voltage: " + String(chargeVoltage) + " V"); 62 | Serial.println("* ⚡️ Input current limit: " + String(inputCurrentLimit) + " mA"); 63 | 64 | if (!charger.setChargeCurrent(CHARGE_CURRENT_MA)){ 65 | Serial.println("Failed to set charge current"); 66 | Serial.println("Please double check the supported values in the documentation"); 67 | } 68 | 69 | if (!charger.setEndOfChargeCurrent(END_OF_CHARGE_CURRENT_MA)){ 70 | Serial.println("Failed to set end of charge current"); 71 | Serial.println("Please double check the supported values in the documentation"); 72 | } 73 | 74 | auto chargeCurrent = charger.getChargeCurrent(); 75 | auto endOfChargeCurrent = charger.getEndOfChargeCurrent(); 76 | 77 | Serial.println("* ⚡️ Charge current set to: " + String(CHARGE_CURRENT_MA) + " mA"); 78 | Serial.println("* ⚡️ End of charge current set to: " + String(END_OF_CHARGE_CURRENT_MA) + " mA"); 79 | } 80 | 81 | String getChargerState(){ 82 | ChargingState status = charger.getState(); 83 | 84 | switch (status) { 85 | case ChargingState::preCharge: 86 | return "precharge"; 87 | break; 88 | case ChargingState::fastChargeConstantCurrent: 89 | return "fast-charge constant current"; 90 | break; 91 | case ChargingState::fastChargeConstantVoltage: 92 | return "fast-charge constant voltage"; 93 | break; 94 | case ChargingState::endOfCharge: 95 | return "end-of-charge"; 96 | break; 97 | case ChargingState::done: 98 | return "done"; 99 | break; 100 | case ChargingState::timerFaultError: 101 | return "timer fault"; 102 | break; 103 | case ChargingState::thermistorSuspendError: 104 | return "thermistor suspend"; 105 | break; 106 | case ChargingState::chargerDisabled: 107 | return "off"; 108 | break; 109 | case ChargingState::batteryOvervoltageError: 110 | return "overvoltage condition"; 111 | break; 112 | case ChargingState::chargerBypassed: 113 | return "disabled"; 114 | break; 115 | default: 116 | return "unknown"; 117 | break; 118 | } 119 | } 120 | 121 | void loop(){ 122 | static ChargingState status = ChargingState::none; 123 | 124 | if (status != charger.getState()) { 125 | status = charger.getState(); 126 | Serial.print("* 👀 Charger state: "); 127 | Serial.println(getChargerState()); 128 | } 129 | 130 | delay(1000); 131 | } 132 | -------------------------------------------------------------------------------- /examples/Standby_WakeFromPin/Standby_WakeFromPin.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Standby Wake From Pin Demo 3 | 4 | This sketch demonstrates how you can use the Arduino_PowermManagement library to send a board to standby mode by using a GPIO pin and wake it up from another. 5 | This sketch is universal and worksn on both Portenta C33 and H7. 6 | 7 | On the the Portenta C33 you can select any of the supported pins (A0, A1, A2, A3, A4, A5, D4, D7) to wake up the board from standby mode. 8 | However setAllPeripheralsPower(false) in this sketch turns off the power lane of the analog pins. 9 | This means they cannot sink current and therefore cannot be used to wake up the device. 10 | Hency only D4 and D7 can be used to wake up the device in this configuration. 11 | 12 | On the Portenta H7 only GPIO0 can be used to wake up the board from standby mode. GPIO0 is available through the High Density Connectors and you need a breakout board to access it. 13 | 14 | Requirements: 15 | - Arduino Portenta C33, Arduino Portenta H7 16 | - Arduino IDE / Arduino CLI 17 | - PowerManagement library (installable from the Arduino Library Manager) 18 | 19 | Usage: 20 | - Connect a button to GOTO_SLEEP_PIN and with a pull-up resistor to 3.3V 21 | - Connect a button to pin PORTENTA_C33_WAKEUP_PIN if you are using the Portenta C33 or GPIO0 if you are using a Portenta H7 and with a pull-up resistor to 3.3V 22 | For maximum power saving use external pull-up resistors. 23 | You will need to power them separately as the 3.3V pin on the board 24 | is turned off when the device goes to sleep and peripherals are turned off. 25 | Alternatively, use pinMode(, INPUT_PULLUP) for the pins and connect the buttons to ground. 26 | (If you need information about how to wire the buttons check this link: https://docs.arduino.cc/built-in-examples/digital/Button/) 27 | 28 | - Upload the provided sketch to the board 29 | Note: On Portenta H7, you need to upload this sketch to both cores, the M7 and the M4 for it to work. 30 | You can do so by selecting the M7 core and then the M4 core from the Tools menu in the "Target core" section. 31 | - Press the button connected to GOTO_SLEEP_PIN to put the board into standby mode 32 | - Press the button connected to PORTENTA_C33_WAKEUP_PIN or GPIO0 on Portenta H7 to wake up the board from standby mode 33 | - The LED will blink every second to show that the board is awake when not in standby mode 34 | 35 | Original author: C. Dragomir (http://arduino.cc) 36 | */ 37 | 38 | #include "Arduino.h" 39 | #include "Arduino_PowerManagement.h" 40 | 41 | 42 | #define PORTENTA_C33_WAKEUP_PIN D4 43 | #define GOTO_SLEEP_PIN D0 44 | 45 | volatile bool shouldGoToSleep = false; 46 | 47 | Board board; 48 | 49 | void setup() { 50 | // When uploading this sketch to the M4 core, it just goes to standby mode. 51 | #if defined(ARDUINO_GENERIC_STM32H747_M4) 52 | board.standByUntilWakeupEvent(); 53 | return; 54 | #endif 55 | 56 | pinMode(LED_BUILTIN, OUTPUT); 57 | 58 | // Register the sleep and wake-up pins as inputs 59 | pinMode(GOTO_SLEEP_PIN, INPUT); 60 | pinMode(PORTENTA_C33_WAKEUP_PIN, INPUT); 61 | 62 | board.begin(); 63 | board.setAllPeripheralsPower(true); // turn on peripherals after waking up from deep sleep 64 | 65 | // Allows to use a button to put the device into sleep mode 66 | attachInterrupt(digitalPinToInterrupt(GOTO_SLEEP_PIN), goToSleep, FALLING); 67 | 68 | #if defined(ARDUINO_PORTENTA_C33) 69 | // On Portenta C33, you can specify which pin to use to wake up the device from sleep mode 70 | // Please read the documentation to understand which pins can be used to wake up the device. 71 | board.enableWakeupFromPin(PORTENTA_C33_WAKEUP_PIN, FALLING); 72 | #elif defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_GENERIC_STM32H747_M4) || defined(ARDUINO_NICLA_VISION) 73 | // On Portenta only pin GPIO0 can be used to wake up the device from sleep mode 74 | board.enableWakeupFromPin(); 75 | #endif 76 | } 77 | 78 | void goToSleep(){ 79 | shouldGoToSleep = true; 80 | } 81 | 82 | void loop() { 83 | if(shouldGoToSleep){ 84 | digitalWrite(LED_BUILTIN, HIGH); // turn off the LED to show that the board is going to sleep 85 | board.shutDownFuelGauge(); 86 | board.setAllPeripheralsPower(false); // turn off peripherals before going to sleep 87 | board.standByUntilWakeupEvent(); 88 | shouldGoToSleep = false; 89 | } else { 90 | // Show that the board is awake by blinking the LED 91 | digitalWrite(LED_BUILTIN, HIGH); 92 | delay(500); 93 | digitalWrite(LED_BUILTIN, LOW); 94 | delay(500); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/Standby_WakeFromRTC_C33/Standby_WakeFromRTC_C33.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Standby Wake from RTC Demo for Portenta C33 3 | 4 | This example demonstrates how to wake up the Portenta C33 from standby mode using the included RTC (Real Time Clock). 5 | The device will go to sleep for 5 seconds and then wake up. When the device is awake you will see the board's built-in LED turned on. 6 | Effectively, you will get the same effect as with blink. 7 | 8 | On the Portenta C33 with the peripherals turned off you can expect around 60uA of current consumption in standby mode. 9 | The example also turns off the peripherals before going to sleep and turns them back on after waking up. 10 | 11 | Usage: 12 | - Make sure you are running the latest version of the Renesas Core 13 | - Select the Portenta C33 board from the Tools menu 14 | - Select the Portenta C33 USB port from the Tools menu 15 | - Upload the code to your Portenta C33 16 | 17 | Initial author: Cristian Dragomir (c.dragomir@arduino.cc) 18 | */ 19 | 20 | 21 | #include "Arduino_PowerManagement.h" 22 | #include "RTC.h" 23 | 24 | RTCTime initialTime(1, Month::JANUARY, 2000, 12, 10, 00, DayOfWeek::SATURDAY, SaveLight::SAVING_TIME_ACTIVE); 25 | 26 | Board board; 27 | 28 | void blinkLed(int ledPin, int delayTime = 1000){ 29 | digitalWrite(ledPin, LOW); 30 | delay(delayTime); 31 | digitalWrite(ledPin, HIGH); 32 | delay(delayTime); 33 | } 34 | 35 | void setup() { 36 | pinMode(LEDR, OUTPUT); // Used to indicate errors 37 | digitalWrite(LEDR, HIGH); // Turn off the red LED 38 | pinMode(LED_BUILTIN, OUTPUT); // Used to indicate that the board is awake 39 | 40 | if(!board.begin()){ 41 | while (true){ 42 | blinkLed(LEDR); 43 | } 44 | } 45 | 46 | board.setAllPeripheralsPower(true); 47 | digitalWrite(LED_BUILTIN, LOW); // Turn on the LED to show that the board is awake 48 | 49 | RTC.begin(); 50 | if (!RTC.isRunning()) { 51 | // The initial time is a dummy time 52 | // You could also get the actual time from an NTP server or from a user input 53 | if(!RTC.setTime(initialTime)){ 54 | while (true){ 55 | blinkLed(LEDR); 56 | } 57 | } 58 | } 59 | 60 | delay(5000); // Keep the board awake for 5 seconds, so we can se it working 61 | board.enableWakeupFromRTC(0, 0, 5); // Sleep for 5 seconds 62 | 63 | board.shutDownFuelGauge(); 64 | board.setAllPeripheralsPower(false); 65 | board.standByUntilWakeupEvent(); 66 | } 67 | 68 | void loop(){} -------------------------------------------------------------------------------- /examples/Standby_WakeFromRTC_H7/Standby_WakeFromRTC_H7.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Standby Wake from RTC Demo for Portenta H7 3 | This example demonstrates how to wake up the Portenta H7 from standby mode using the included RTC (Real Time Clock). 4 | The device will stay awake for ~5 seconds, then go to sleep for 10 second. When the device is awake you will see the board's blue LED turned on. 5 | Effectively, you will get the same effect as with blink. 6 | 7 | On the Portenta H7 with the peripherals turned off you can expect around 300uA of current consumption in standby mode. 8 | The example also turns off the peripherals before going to sleep and turns them back on after waking up. 9 | Usage: 10 | - Make sure you are running the latest version of the Portenta H7 core. 11 | - Select the Portenta H7 board from the Tools menu 12 | - Select the Portenta H7 USB port from the Tools menu 13 | - Upload the code to your Portenta H7 14 | 15 | Note: You need to upload this sketch to both cores, the M7 and the M4 for it to work. 16 | You can do so by selecting the M7 core and then the M4 core from the Tools menu in the "Target core" section. 17 | 18 | Initial authors: 19 | Cristian Dragomir (c.dragomir@arduino.cc) 20 | Sebastian Romero (s.romero@arduino.cc) 21 | */ 22 | 23 | #include "Arduino_PowerManagement.h" 24 | 25 | Board board; 26 | 27 | void blinkLed(int ledPin, int delayTime = 1000){ 28 | digitalWrite(ledPin, LOW); 29 | delay(delayTime); 30 | digitalWrite(ledPin, HIGH); 31 | delay(delayTime); 32 | } 33 | 34 | void setup() { 35 | // When uploading this sketch to the M4 core, it just goes to standby mode. 36 | #if defined(ARDUINO_GENERIC_STM32H747_M4) 37 | board.standByUntilWakeupEvent(); 38 | return; 39 | #endif 40 | 41 | pinMode(LEDR, OUTPUT); // Used to indicate errors 42 | digitalWrite(LEDR, HIGH); // Turn off the red LED 43 | pinMode(LED_BUILTIN, OUTPUT); 44 | digitalWrite(LED_BUILTIN, LOW); // Turn on the built-in LED to show that the board is awake 45 | 46 | if(!board.begin()){ 47 | // If the board fails to initialize, it will blink the red LED 48 | while (true){ 49 | blinkLed(LEDR); 50 | } 51 | } 52 | 53 | delay(10000); // keep the board awake for 10 seconds, so we can se it working 54 | board.shutDownFuelGauge(); 55 | 56 | // The LED should go off when the board goes to sleep 57 | board.setAllPeripheralsPower(false); 58 | 59 | board.enableWakeupFromRTC(0, 0, 10); // Go to standby for 10 seconds 60 | board.standByUntilWakeupEvent(); 61 | } 62 | 63 | void loop() {} -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Arduino_PowerManagement 2 | version=1.0.0 3 | author=Arduino 4 | maintainer=Arduino 5 | sentence=A library to charge and monitor the battery and use low power modes on the Portenta C33, Portenta H7 and Nicla Vision boards. 6 | paragraph=Abstracts the functionality of the PF1550 Power Management IC and MAX17162 LiPo Fuel Gauge found in the Portenta C33, Portenta H7 and Nicla Vision boards. 7 | category=Device Control 8 | url=https://github.com/arduino-libraries/Arduino_PowerManagement 9 | architectures=renesas_portenta, mbed_portenta, mbed_nicla 10 | includes=Arduino_PowerManagement.h 11 | depends=Arduino_PF1550,Arduino_LowPowerPortentaC33,Arduino_LowPowerPortentaH7 -------------------------------------------------------------------------------- /src/Arduino_PowerManagement.h: -------------------------------------------------------------------------------- 1 | #ifndef ARDUINO_POWER_MANAGEMENT_H 2 | #define ARDUINO_POWER_MANAGEMENT_H 3 | 4 | #include "Battery.h" 5 | #include "Board.h" 6 | #include "Charger.h" 7 | 8 | #endif -------------------------------------------------------------------------------- /src/Battery.cpp: -------------------------------------------------------------------------------- 1 | #include "Battery.h" 2 | #include "PF1550.h" 3 | #include "WireUtils.h" 4 | #include "BatteryConstants.h" 5 | 6 | Battery::Battery() { 7 | Battery(BatteryCharacteristics()); 8 | } 9 | 10 | Battery::Battery(BatteryCharacteristics batteryCharacteristics) : characteristics(batteryCharacteristics) { 11 | } 12 | 13 | bool Battery::begin(bool enforceReload) { 14 | // PMIC already initializes the I2C bus, so no need to call Wire.begin() for the fuel gauge 15 | if(PMIC.begin() != 0){ 16 | return false; 17 | } 18 | 19 | // If hardware / software power-on-reset (POR) event has occurred, reconfigure the battery gauge, otherwise, skip the configuration 20 | if (!enforceReload && getBit(this->wire, FUEL_GAUGE_ADDRESS, STATUS_REG, POR_BIT) != 1) { 21 | return true; 22 | } 23 | 24 | if(!awaitDataReady()){ 25 | return false; // Timeout while waiting for the battery gauge to be ready 26 | } 27 | 28 | uint16_t tempHibernateConfigRegister = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, HIB_CFG_REG); 29 | releaseFromHibernation(); 30 | configureBatteryCharacteristics(); 31 | 32 | if(!refreshBatteryGaugeModel()){ 33 | return false; 34 | } 35 | 36 | // Restore the original Hibernate Config Register value 37 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, HIB_CFG_REG, tempHibernateConfigRegister); // Restore Original HibCFG value 38 | replaceRegisterBit(this->wire, FUEL_GAUGE_ADDRESS, STATUS_REG, POR_BIT, 0x0); // Clear POR bit after reset 39 | return true; 40 | } 41 | 42 | bool Battery::awaitDataReady(uint16_t timeout){ 43 | unsigned long startTime = millis(); 44 | 45 | while (true){ 46 | bool dataIsReady = getBit(this->wire, FUEL_GAUGE_ADDRESS, F_STAT_REG, DNR_BIT) == 0; 47 | if (dataIsReady) { 48 | return true; 49 | } 50 | 51 | if (millis() - startTime > timeout) { 52 | return false; 53 | } 54 | 55 | delay(100); // Wait for the data to be ready. This takes 710ms from power-up 56 | } 57 | } 58 | 59 | void Battery::configureBatteryCharacteristics(){ 60 | uint16_t designCapacity = static_cast(characteristics.capacity / CAPACITY_MULTIPLIER_MAH); 61 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, DESIGN_CAP_REG, designCapacity); 62 | 63 | uint16_t terminationCurrent = static_cast(characteristics.endOfChargeCurrent / CURRENT_MULTIPLIER_MA); 64 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, I_CHG_TERM_REG, terminationCurrent); 65 | 66 | uint16_t emptyVoltageValue = static_cast((characteristics.emptyVoltage * 1000) / EMPTY_VOLTAGE_MULTIPLIER_MV); 67 | uint8_t recoveryVoltageValue = static_cast((characteristics.recoveryVoltage * 1000) / RECOVERY_VOLTAGE_MULTIPLIER_MV); 68 | uint16_t emptyVoltageRegisterValue = emptyVoltageValue << 7 | recoveryVoltageValue; 69 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, V_EMPTY_REG, emptyVoltageRegisterValue); 70 | } 71 | 72 | void Battery::releaseFromHibernation(){ 73 | // See section "Soft-Wakeup" in user manual https://www.analog.com/media/en/technical-documentation/user-guides/max1726x-modelgauge-m5-ez-user-guide.pdf 74 | replaceRegisterBit(this->wire, FUEL_GAUGE_ADDRESS, HIB_CFG_REG, EN_HIBERNATION_BIT, 0); // Exit Hibernate Mode 75 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, SOFT_WAKEUP_REG, 0x90); // Wakes up the fuel gauge from hibernate mode to reduce the response time of the IC to configuration changes 76 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, SOFT_WAKEUP_REG, 0x0); // Soft wake-up must be manually cleared (0x0000) afterward to keep proper fuel gauge timing 77 | } 78 | 79 | bool Battery::refreshBatteryGaugeModel(){ 80 | uint16_t registerValue = 1 << MODEL_CFG_REFRESH_BIT; 81 | 82 | // Set NTC resistor option for 100k resistor 83 | if (characteristics.ntcResistor == NTCResistor::Resistor100K) { 84 | registerValue |= 1 << R100_BIT; 85 | } 86 | 87 | // Set the charge voltage option for voltages above 4.25V according to the datasheet 88 | if(characteristics.chargeVoltage > 4.25f) { 89 | // Set bit 10 to 1 90 | registerValue |= 1 << VCHG_BIT; 91 | } 92 | 93 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MODEL_CFG_REG, registerValue); 94 | unsigned long startTime = millis(); 95 | 96 | while (true) { 97 | // Read back the model configuration register to ensure the refresh bit is cleared 98 | bool refreshComplete = getBit(this->wire, FUEL_GAUGE_ADDRESS, MODEL_CFG_REG, MODEL_CFG_REFRESH_BIT) == 0; 99 | if (refreshComplete) { 100 | return true; // Exit the loop if the refresh bit is cleared 101 | } 102 | 103 | if (millis() - startTime > 1000) { 104 | return false; // Exit the loop if the refresh bit is not cleared after 1 second 105 | } 106 | delay(10); 107 | } 108 | } 109 | 110 | bool Battery::isConnected(){ 111 | uint16_t statusRegister = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, STATUS_REG); 112 | return bitRead(statusRegister, BATTERY_STATUS_BIT) == 0; 113 | } 114 | 115 | float Battery::voltage(){ 116 | if(!isConnected()){ 117 | return -1; 118 | } 119 | 120 | auto voltageInMV = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, VCELL_REG) * VOLTAGE_MULTIPLIER_MV; 121 | return voltageInMV / 1000.0f; 122 | } 123 | 124 | float Battery::averageVoltage(){ 125 | if(!isConnected()){ 126 | return -1; 127 | } 128 | 129 | auto voltageInMV = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, AVG_VCELL_REG) * VOLTAGE_MULTIPLIER_MV; 130 | return voltageInMV / 1000.0f; 131 | } 132 | 133 | float Battery::minimumVoltage(){ 134 | if(!isConnected()){ 135 | return -1; 136 | } 137 | uint16_t maxMinVoltageRegisterValue = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_VOLT_REG); 138 | uint8_t minimumVoltageValue = maxMinVoltageRegisterValue & 0xFF; // The minimum voltage is stored in the lower byte 139 | return (minimumVoltageValue * MAXMIN_VOLT_MULTIPLIER_MV) / 1000.0f; 140 | } 141 | 142 | float Battery::maximumVoltage(){ 143 | if(!isConnected()){ 144 | return -1; 145 | } 146 | uint16_t maxMinVoltageRegisterValue = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_VOLT_REG); 147 | uint8_t maximumVoltageValue = maxMinVoltageRegisterValue >> 8; // The maximum voltage is stored in the upper byte 148 | return (maximumVoltageValue * MAXMIN_VOLT_MULTIPLIER_MV) / 1000.0f; 149 | } 150 | 151 | bool Battery::resetMaximumMinimumVoltage(){ 152 | return writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_VOLT_REG, MAXMIN_VOLT_INITIAL_VALUE) == 0; 153 | } 154 | 155 | void Battery::setTemperatureMeasurementMode(bool externalTemperature){ 156 | uint16_t configRegister = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, CONFIG_REG); 157 | uint8_t tselValue = bitRead(configRegister, TSEL_BIT); 158 | uint8_t ethrmValue = bitRead(configRegister, ETHRM_BIT); 159 | uint8_t tenValue = bitRead(configRegister, TEN_BIT); 160 | 161 | // TSEL value 0: internal die temperature, 1: thermistor temperature 162 | if(!externalTemperature && tselValue == 0 && tenValue == 1){ 163 | // Serial.println("Temperature measurement mode is already set to internal die temperature."); 164 | return; // No need to change the configuration 165 | } 166 | 167 | // ETHRM bit must be set to 1 when TSel is 1. 168 | if(externalTemperature && tselValue == 1 && ethrmValue == 1 && tenValue == 1){ 169 | // Serial.println("Temperature measurement mode is already set to external thermistor temperature."); 170 | return; // No need to change the configuration 171 | } 172 | 173 | if(externalTemperature){ 174 | configRegister = bitSet(configRegister, TSEL_BIT); 175 | configRegister = bitSet(configRegister, ETHRM_BIT); 176 | // FIXME: The external thermistor temperature measurement is not working as expected 177 | // In order to support this, probably more configuration is needed 178 | // Currently after taking the first reading, the battery gets reported as disconnected 179 | // plus the register value is reset to the default value. 180 | } else { 181 | configRegister = bitClear(configRegister, TSEL_BIT); 182 | configRegister = bitClear(configRegister, ETHRM_BIT); 183 | } 184 | 185 | // Enable temperature channel for both modes. 186 | // It's not clear if this is necessary for the internal die temperature, but it's enabled by default. 187 | configRegister = bitSet(configRegister, TEN_BIT); 188 | 189 | uint16_t status2RegisterValue = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, STATUS2_REG); 190 | bool isInHibernateMode = bitRead(status2RegisterValue, HIB_BIT) == 1; 191 | writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, CONFIG_REG, configRegister); 192 | 193 | // Wait for the configuration to be updated 194 | // This takes 175ms in active mode, and 5.6 seconds in hibernate mode by default 195 | if(isInHibernateMode){ 196 | delay(5700); // Wait a bit longer to ensure the configuration is updated 197 | } else { 198 | delay(250); // Wait a bit longer to ensure the configuration is updated 199 | } 200 | 201 | } 202 | 203 | uint8_t Battery::internalTemperature(){ 204 | if(!isConnected()){ 205 | return -1; 206 | } 207 | 208 | setTemperatureMeasurementMode(false); 209 | // Also see DieTemp Register (034h) for the internal die temperature p. 24 in DS 210 | // If Config.TSel = 0, DieTemp and Temp registers have the value of the die temperature. 211 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, TEMP_REG) * TEMPERATURE_MULTIPLIER_C; 212 | } 213 | 214 | uint8_t Battery::averageInternalTemperature(){ 215 | if(!isConnected()){ 216 | return -1; 217 | } 218 | 219 | setTemperatureMeasurementMode(false); 220 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, AVG_TA_REG) * TEMPERATURE_MULTIPLIER_C; 221 | } 222 | 223 | uint8_t Battery::batteryTemperature(){ 224 | if(!isConnected()){ 225 | return -1; 226 | } 227 | 228 | setTemperatureMeasurementMode(true); 229 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, TEMP_REG) * TEMPERATURE_MULTIPLIER_C; 230 | } 231 | 232 | uint8_t Battery::averageBatteryTemperature(){ 233 | if(!isConnected()){ 234 | return -1; 235 | } 236 | 237 | setTemperatureMeasurementMode(true); 238 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, AVG_TA_REG) * TEMPERATURE_MULTIPLIER_C; 239 | } 240 | 241 | int16_t Battery::current(){ 242 | if(!isConnected()){ 243 | return -1; 244 | } 245 | 246 | return (int16_t)readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, CURRENT_REG) * CURRENT_MULTIPLIER_MA; 247 | } 248 | 249 | int16_t Battery::averageCurrent(){ 250 | if(!isConnected()){ 251 | return -1; 252 | } 253 | 254 | return (int16_t)readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, AVG_CURRENT_REG) * CURRENT_MULTIPLIER_MA; 255 | } 256 | 257 | int16_t Battery::minimumCurrent(){ 258 | if(!isConnected()){ 259 | return -1; 260 | } 261 | 262 | uint16_t registerValue = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_CURRENT_REG); 263 | if(registerValue == MAXMIN_CURRENT_INITIAL_VALUE){ 264 | return 0; // The minimum current is not valid 265 | } 266 | 267 | int8_t lowerByte = static_cast( registerValue & 0xFF); 268 | return static_cast(lowerByte * MAXMIN_CURRENT_MULTIPLIER_MA); 269 | } 270 | 271 | int16_t Battery::maximumCurrent(){ 272 | if(!isConnected()){ 273 | return -1; 274 | } 275 | 276 | uint16_t registerValue = readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_CURRENT_REG); 277 | if(registerValue == MAXMIN_CURRENT_INITIAL_VALUE){ 278 | return 0; // The minimum current is not valid 279 | } 280 | 281 | // The maximum current is stored in the upper byte 282 | int8_t upperByte = static_cast( registerValue >> 8); 283 | return static_cast(upperByte * MAXMIN_CURRENT_MULTIPLIER_MA); 284 | } 285 | 286 | bool Battery::resetMaximumMinimumCurrent(){ 287 | return writeRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, MAXMIN_CURRENT_REG, MAXMIN_CURRENT_INITIAL_VALUE) == 0; 288 | } 289 | 290 | int16_t Battery::power(){ 291 | if(!isConnected()){ 292 | return -1; 293 | } 294 | 295 | return (int16_t)readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, POWER_REG) * POWER_MULTIPLIER_MW; 296 | } 297 | 298 | int16_t Battery::averagePower(){ 299 | if(!isConnected()){ 300 | return -1; 301 | } 302 | 303 | return (int16_t)readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, AVG_POWER_REG) * POWER_MULTIPLIER_MW; 304 | } 305 | 306 | uint8_t Battery::percentage(){ 307 | if(!isConnected()){ 308 | return -1; 309 | } 310 | 311 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, REP_SOC_REG) * PERCENTAGE_MULTIPLIER; 312 | } 313 | 314 | uint16_t Battery::remainingCapacity(){ 315 | if(!isConnected()){ 316 | return -1; 317 | } 318 | 319 | if(characteristics.capacity == 0){ 320 | return -1; 321 | } 322 | 323 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, REP_CAP_REG) * CAPACITY_MULTIPLIER_MAH; 324 | } 325 | 326 | uint16_t Battery::fullCapacity(){ 327 | if(!isConnected()){ 328 | return -1; 329 | } 330 | 331 | if(characteristics.capacity == 0){ 332 | return -1; 333 | } 334 | 335 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, FULL_CAP_REP_REG) * CAPACITY_MULTIPLIER_MAH; 336 | } 337 | 338 | bool Battery::isEmpty(){ 339 | return getBit(this->wire, FUEL_GAUGE_ADDRESS, F_STAT_REG, E_DET_BIT) == 1; 340 | } 341 | 342 | int32_t Battery::timeToEmpty(){ 343 | if(!isConnected()){ 344 | return -1; 345 | } 346 | 347 | int16_t current = this->averageCurrent(); 348 | if(current >= 0){ 349 | return -1; // The battery is charging, so the time to empty is not valid 350 | } 351 | 352 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, TTE_REG) * TIME_MULTIPLIER_S; 353 | } 354 | 355 | int32_t Battery::timeToFull(){ 356 | if(!isConnected()){ 357 | return -1; 358 | } 359 | 360 | int16_t current = this->averageCurrent(); 361 | if(current <= 0){ 362 | return -1; // The battery is discharging, so the time to full is not valid 363 | } 364 | 365 | return readRegister16Bits(this->wire, FUEL_GAUGE_ADDRESS, TTF_REG) * TIME_MULTIPLIER_S; 366 | } 367 | -------------------------------------------------------------------------------- /src/Battery.h: -------------------------------------------------------------------------------- 1 | #ifndef Battery_H 2 | #define Battery_H 3 | 4 | #include "Arduino.h" 5 | #include "Wire.h" 6 | 7 | constexpr int FUEL_GAUGE_ADDRESS = 0x36; // I2C address of the fuel gauge 8 | constexpr float DEFAULT_BATTERY_EMPTY_VOLTAGE = 3.3f; // V 9 | constexpr float DEFAULT_CHARGE_VOLTAGE = 4.2f; // V 10 | constexpr int DEFAULT_END_OF_CHARGE_CURRENT = 50; // mA 11 | constexpr float DEFAULT_RECOVERY_VOLTAGE = 3.88f; // V 12 | 13 | enum class NTCResistor { 14 | Resistor10K, 15 | Resistor100K 16 | }; 17 | 18 | /** 19 | * @brief This struct contains the characteristics of the battery. 20 | */ 21 | struct BatteryCharacteristics { 22 | /// @brief The battery's capacity in milliampere-hours (mAh). 23 | int capacity = 0; 24 | 25 | /// @brief The voltage in volts (V) at which the battery is considered empty. 26 | /// If you don't know this value you can use the minimumVoltage() function to find out 27 | /// while you let the battery completely discharge. 28 | float emptyVoltage = DEFAULT_BATTERY_EMPTY_VOLTAGE; 29 | 30 | /// @brief The voltage in volts (V) at which the battery is being charged. 31 | float chargeVoltage = DEFAULT_CHARGE_VOLTAGE; 32 | 33 | /// @brief The current in milli amperes (mA) that is used to keep the battery charged at the end of the charging process. 34 | int endOfChargeCurrent = DEFAULT_END_OF_CHARGE_CURRENT; 35 | 36 | /// @brief The NTC resistor value used in the battery pack (10K or 100K Ohm). 37 | NTCResistor ntcResistor = NTCResistor::Resistor10K; 38 | 39 | /// @brief Sets the voltage level for clearing empty detection. Once the cell voltage rises above this point, empty voltage detection is re-enabled. 40 | float recoveryVoltage = DEFAULT_RECOVERY_VOLTAGE; 41 | }; 42 | 43 | /** 44 | * @brief This class provides a detailed insight into the battery's health and usage. 45 | */ 46 | class Battery { 47 | public: 48 | /** 49 | * @brief Initializes the battery object with default values for capacity (0mAh) and empty voltage (3.3V). 50 | */ 51 | Battery(); 52 | 53 | /** 54 | * @brief Initializes the battery object with the given battery characteristics. 55 | * @param batteryCharacteristics The characteristics of the battery. 56 | */ 57 | Battery(BatteryCharacteristics batteryCharacteristics); 58 | 59 | /** 60 | * @brief Initializes the battery communication and configuration. 61 | * @param enforceReload If set to true, the battery gauge config will be reloaded. 62 | * @return True if the initialization was successful, false otherwise. 63 | */ 64 | bool begin(bool enforceReload = false); 65 | 66 | /** 67 | * @brief Checks if a battery is connected to the system. 68 | * @return True if a battery is connected, false otherwise 69 | */ 70 | boolean isConnected(); 71 | 72 | /** 73 | * @brief Reads the current voltage of the battery. 74 | * Voltage is usually between 3.0V and 4.2V. 75 | * @return The current voltage in volts (V). 76 | */ 77 | float voltage(); 78 | 79 | /** 80 | * @brief Reads an average of voltage readings of the battery. 81 | * @return The average voltage in volts (V). 82 | */ 83 | float averageVoltage(); 84 | 85 | /** 86 | * @brief Returns the minimum voltage value measured since the last device reset. 87 | * At power-up the minimum voltage value is set to FFh (the maximum). 88 | * @return The minimum voltage value in volts (V). 89 | */ 90 | float minimumVoltage(); 91 | 92 | /** 93 | * @brief Returns the maximum voltage value measured since the last device reset. 94 | * At power-up the maximum voltage value is set to 00h (the minimum). 95 | * @return The maximum voltage value in volts (V). 96 | */ 97 | float maximumVoltage(); 98 | 99 | /** 100 | * @brief Resets the minimum and maximum voltage values. 101 | * @return True if the minimum and maximum voltage values were successfully reset, false otherwise. 102 | */ 103 | bool resetMaximumMinimumVoltage(); 104 | 105 | /** 106 | * @brief Reads the current flowing from the battery at the moment. 107 | * Negative values indicate that the battery is charging, 108 | * positive values indicate that the battery is discharging. 109 | * When no battery is connected, the value is -1. 110 | * @return The current flowing from the battery in milli amperes (mA). 111 | */ 112 | int16_t current(); 113 | 114 | /** 115 | * @brief Reads an average of current readings of the battery. 116 | * @return The average current in milli amperes (mA). 117 | */ 118 | int16_t averageCurrent(); 119 | 120 | /** 121 | * @brief Reads the minimum current values measured since the last device reset. 122 | * Note: The resolution of the minimum current value is 160mA so the value 123 | * is rounded to the nearest 160mA. 124 | * @return The minimum current values in milli amperes (mA). 125 | */ 126 | int16_t minimumCurrent(); 127 | 128 | /** 129 | * @brief Reads the maximum current values measured since the last device reset. 130 | * Note: The resolution of the minimum current value is 160mA so the value 131 | * is rounded to the nearest 160mA. 132 | * @return The maximum current values in milli amperes (mA). 133 | */ 134 | int16_t maximumCurrent(); 135 | 136 | /** 137 | * @brief Resets the minimum and maximum current values. 138 | * @return True if the minimum and maximum current values were successfully reset, false otherwise. 139 | */ 140 | bool resetMaximumMinimumCurrent(); 141 | 142 | /** 143 | * @brief Reads the current power of the battery in milliwatts (mW). 144 | * This value is calculated based on the current and voltage of the battery. 145 | * @return The current power in milliwatts (mW). 146 | */ 147 | int16_t power(); 148 | 149 | /** 150 | * @brief Reads an average of power readings of the battery in milliwatts (mW). 151 | * This value is calculated based on the current and voltage of the battery. 152 | * @return The average power in milliwatts (mW). 153 | */ 154 | int16_t averagePower(); 155 | 156 | /** 157 | * @brief Reads the current temperature of the internal die of the battery gauge chip. 158 | * @return The current temperature in degrees Celsius. 159 | */ 160 | uint8_t internalTemperature(); 161 | 162 | /** 163 | * @brief Reads an average of internal temperature readings of the battery. 164 | * Note: If the battery temperature was read before, 165 | * this function will change the configuration to read the internal temperature. 166 | * You will have to await a couple of temperature readings before 167 | * getting a meaningful average temperature. 168 | * @return The average temperature in degrees Celsius. 169 | */ 170 | uint8_t averageInternalTemperature(); 171 | 172 | /** 173 | * @brief Reads the battery's state of charge (SOC). 174 | * This value is based on both the voltage and the current of the battery as well as 175 | * compensation for the battery's age and temperature and discharge rate. 176 | * @return The state of charge as a percentage (Range: 0% - 100%). 177 | */ 178 | uint8_t percentage(); 179 | 180 | /** 181 | * @brief Reads the remaining capacity of the battery. 182 | * In combination with current(), this value can be used to estimate 183 | * the remaining time until the battery is empty. 184 | * @return The remaining capacity in milliampere-hours (mAh). 185 | */ 186 | uint16_t remainingCapacity(); 187 | 188 | /** 189 | * Returns the full capacity of the battery. 190 | * For this to work, the capacity of the battery must be set 191 | * when initializing the battery object. 192 | * @return The full capacity of the battery. 193 | */ 194 | uint16_t fullCapacity(); 195 | 196 | /** 197 | * @brief Checks if the battery is empty. 198 | * Returns false once the cell voltage rises above the recovery threshold. 199 | * @return true if the battery is empty, false otherwise. 200 | */ 201 | bool isEmpty(); 202 | 203 | /** 204 | * Calculates the estimated time until the battery is empty. 205 | * @return The estimated time until the battery is empty, in seconds. 206 | * If the battery is charging, the function returns -1. 207 | */ 208 | int32_t timeToEmpty(); 209 | 210 | /** 211 | * Calculates the estimated time until the battery is fully charged. 212 | * The value is determined by learning from the experience of prior charge cycles. 213 | * @return The estimated time until the battery is fully charged in seconds. 214 | * If the battery is discharging, the function returns -1. 215 | */ 216 | int32_t timeToFull(); 217 | 218 | private: 219 | /** 220 | * @brief Refreshes the battery gauge model. This is required when 221 | * changing the battery's characteristics and is used by the EZ algorithm (battery characterization). 222 | * EZ stands for "Easy" and highlights how the algorithm makes it easy to 223 | * use the battery gauge without needing to provide precise battery characteristics. 224 | * 225 | * @return true if the battery gauge model was successfully refreshed, false otherwise. 226 | */ 227 | bool refreshBatteryGaugeModel(); 228 | 229 | /** 230 | * @brief Reads the battery's temperature. 231 | * Note: This only works if the battery is equipped with a thermistor. 232 | * @return The battery temperature in degrees Celsius. 233 | */ 234 | uint8_t batteryTemperature(); 235 | 236 | /** 237 | * @brief Reads the average battery temperature. 238 | * Note: This only works if the battery is equipped with a thermistor. 239 | * @return The average battery temperature in degrees Celsius. 240 | */ 241 | uint8_t averageBatteryTemperature(); 242 | 243 | /** 244 | * @brief Releases the device from hibernation mode. 245 | * This is used during the initialization process to wake up the device. 246 | * 247 | * This function is used to release the device from hibernation mode and resume normal operation. 248 | */ 249 | void releaseFromHibernation(); 250 | 251 | /** 252 | * Waits for the data of the EZ algorithm's output registers to be ready. 253 | * @return True when the data has become ready, false when the timeout is reached. 254 | */ 255 | bool awaitDataReady(uint16_t timeout = 1000); 256 | 257 | /** 258 | * Configures the characteristics of the battery as part of the initialization process. 259 | */ 260 | void configureBatteryCharacteristics(); 261 | 262 | /** 263 | * Sets the temperature measurement mode for the battery. 264 | * 265 | * @param externalTemperature Flag indicating whether to measeure the internal die temperature or the battery temperature. 266 | */ 267 | void setTemperatureMeasurementMode(bool externalTemperature); 268 | 269 | BatteryCharacteristics characteristics; 270 | 271 | #if defined(ARDUINO_PORTENTA_C33) 272 | TwoWire *wire = &Wire3; 273 | #elif defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_GENERIC_STM32H747_M4) 274 | TwoWire *wire = &Wire1; 275 | #elif defined(ARDUINO_NICLA_VISION) 276 | TwoWire *wire = &Wire1; 277 | #else 278 | #error "The selected board is not supported by the Battery class." 279 | #endif 280 | }; 281 | 282 | #endif -------------------------------------------------------------------------------- /src/BatteryConstants.h: -------------------------------------------------------------------------------- 1 | #ifndef BATTERY_CONSTANTS_H 2 | 3 | // SEE: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX17262.pdf 4 | 5 | // Initial Values (for resetting registers) 6 | #define MAXMIN_VOLT_INITIAL_VALUE 0x00FF 7 | #define MAXMIN_CURRENT_INITIAL_VALUE 0x807F 8 | 9 | // Conversion factors (See section "ModelGauge m5 Register Standard Resolutions" in the datasheet) 10 | #define VOLTAGE_MULTIPLIER_MV (1.25 / 16) // Resolution: 78.125 μV per LSB 11 | #define CURRENT_MULTIPLIER_MA 0.15625 // Resolution: 0.15625 mA per LSB for MAX17262R 12 | #define MAXMIN_CURRENT_MULTIPLIER_MA 160 // Resolution: 160mA per LSB 13 | #define CAPACITY_MULTIPLIER_MAH 0.5 // Resolution: 0.5mAh per LSB for MAX17262R 14 | #define PERCENTAGE_MULTIPLIER (1.0 / 256) // Resolution: 1/256% per LSB 15 | #define TIME_MULTIPLIER_S 5.625 // Resolution: 5.625 seconds per LSB 16 | #define TEMPERATURE_MULTIPLIER_C (1.0 / 256) // Resolution: 1/256°C per LSB 17 | #define EMPTY_VOLTAGE_MULTIPLIER_MV 10 // Resolution: 10mV per LSB 18 | #define RECOVERY_VOLTAGE_MULTIPLIER_MV 40 // Resolution: 40mV per LSB 19 | #define MAXMIN_VOLT_MULTIPLIER_MV 20 // Resolution: 20mV per LSB 20 | #define POWER_MULTIPLIER_MW 1.6 // Resolution: 1.6mW per LSB 21 | 22 | // Voltage Registers 23 | #define VCELL_REG 0x09 // VCell reports the voltage measured between BATT and GND. 24 | #define AVG_VCELL_REG 0x19 // The AvgVCell register reports an average of the VCell register readings. 25 | #define VRIPPLE_REG 0xBC 26 | #define MAXMIN_VOLT_REG 0x1B // The MaxMinVolt register maintains the maximum and minimum of VCell register values since device reset. 27 | 28 | // Temperature Registers 29 | #define TEMP_REG 0x08 // The Temp register provides the temperature measured by the thermistor or die temperature based on the Config register setting. 30 | #define AVG_TA_REG 0x16 // The AvgTA register reports an average of the readings from the Temp register. 31 | #define AIN_REG 0x027 32 | #define T_GAIN_REG 0x2C 33 | #define T_OFF_REG 0x02D 34 | #define DIE_TEMP_REG 0x34 35 | 36 | // Capacity Registers 37 | #define REP_CAP_REG 0x05 // RepCap or reported remaining capacity in mAh. 38 | #define Q_RESIDUAL_REG 0x0C 39 | #define MIX_CAP_REG 0x0Fs 40 | #define FULL_CAP_REP_REG 0x10 // This register reports the full capacity that goes with RepCap, generally used for reporting to the GUI. 41 | #define DESIGN_CAP_REG 0x18 // The DesignCap register holds the expected capacity of the cell. 42 | #define AV_CAP_REG 0x1F 43 | #define FULL_CAP_NOM_REG 0x23 44 | #define DQ_ACC_REG 0x45 45 | #define VFR_REM_CAP_REG 0x4A 46 | #define QH_REG 0x4D // The QH register displays the raw coulomb count generated by the device. This register is used internally as an input to the mixing algorithm 47 | #define AT_QRESIDUAL_REG 0xDC 48 | #define AT_AV_CAP_REG 0xDF 49 | 50 | // current/timers 51 | #define AT_RATE_REG 0x04 52 | #define CURRENT_REG 0x0A // The MAX17262 uses internal current sensing to monitor the current through the SYS pin. The measurement value is stored in two's-complement format. 53 | #define AVG_CURRENT_REG 0x0B // The AvgCurrent register reports an average of Current register readings. 54 | #define MAXMIN_CURRENT_REG 0x1C // The MaxMinCurr register maintains the maximum and minimum Current register values since the last IC reset or until cleared by host software. 55 | #define TTE_REG 0x11 // The TTE register holds the estimated time to empty for the application under present temperature and load conditions. TTE register is only valid when current register is negative. 56 | #define TTF_REG 0x20 // The TTF register holds the estimated time to full for the application under present conditions. The TTF register is only valid when the current register is positive. 57 | #define TIMER_REG 0x3E 58 | #define TIMER_H_REG 0xBE 59 | #define AT_TTE_REG 0xDD 60 | 61 | // percentages 62 | #define REP_SOC_REG 0x06 // the reported state-of-charge percentage output for use by the application GUI. 63 | #define AGE_REG 0x07 64 | #define MIX_SOC_REG 0x0D 65 | #define AV_SOC_REG 0x0E 66 | #define DP_ACC_REG 0x46 67 | #define AT_AV_SOC_REG 0xDE 68 | 69 | // Model Registers 70 | #define QR_TABLE_00_REG 0x12 71 | #define FULL_SOC_THR_REG 0x13 72 | #define CONFIG_REG 0x1D // The Config registers hold all shutdown enable, alert enable, and temperature enable control bits. Writing a bit location enables the corresponding function within one task period. (One task period is 175ms in active mode, and 5.6 seconds in hibernate mode by default.) 73 | #define I_CHG_TERM_REG 0x1E // The IChgTerm register allows the device to detect when a charge cycle of the cell has completed. 74 | #define QR_TABLE_10_REG 0x22 75 | #define LEARN_CFG_REG 0x28 76 | #define FILTER_CFG_REG 0x29 77 | #define RELAX_CFG_REG 0x2A 78 | #define MISC_CFG_REG 0x2B 79 | #define QR_TABLE_20_REG 0x32 80 | #define FULL_CAP_REG 0x35 81 | #define R_COMP_0_REG 0x38 82 | #define TEMP_CO_REG 0x39 83 | #define V_EMPTY_REG 0x3A 84 | #define QR_TABLE_30_REG 0x42 85 | 86 | // Status Registers 87 | #define STATUS_REG 0x00 88 | #define STATUS2_REG 0xB0 // The Status2 register maintains status of various firmware functions. 89 | #define F_STAT_REG 0x3D // The FStat register is a read-only register that monitors the status of the ModelGauge m5 algorithm. 90 | #define HIB_CFG_REG 0xBA // The HibCfg register controls hibernate mode functionality. The MAX1726x enters and exits hibernate when the battery current is less than approximately C/100. 91 | #define SHDN_TIMER 0x3F 92 | #define SOFT_WAKEUP_REG 0x60 // The Command register accepts commands to perform functions to wake up the IC for configuration changes. 93 | #define MODEL_CFG_REG 0xDB 94 | #define CYCLES_REG 0x17 95 | #define DEV_NAME_REG 0x21 96 | #define POWER_REG 0xB1 // Instant power calculation from immediate current and voltage 97 | #define AVG_POWER_REG 0xB3 98 | 99 | // POR (Power-On Reset): This bit is set to 1 when the device detects that 100 | // a software or hardware POR event has occurred. This bit must be cleared 101 | // by system software to detect the next POR event. POR is set to 1 at power-up. 102 | #define POR_BIT 1 103 | #define DNR_BIT 0 // Data Not Ready. This bit is set to 1 at cell insertion and remains set until the output registers have been updated. Afterward, the IC clears this bit, indicating the fuel gauge calculations are up to date. This takes 710ms from power-up 104 | #define E_DET_BIT 8 // FStat Register: Empty Detection. This bit is set to 1 when the IC detects that the cell empty point has been reached. This bit is reset to 0 when the cell voltage rises above the recovery threshold. 105 | #define FULL_DET_BIT 5 // Status2 Register: Full Detection. 106 | #define FQ_BIT 7 // FStat Register: Full Qualified. This bit is set when all charge termination conditions have been met. See the End-of-Charge Detection section for details. 107 | #define EN_HIBERNATION_BIT 15 // HibCfg Register: Enable Hibernate Mode. When set to 1, the IC will enter hibernate mode if conditions are met. When set to 0, the IC always remains in the active mode of operation. 108 | #define SHDN_BIT 7 // Config Register: Write this bit to logic 1 to force a shutdown of the device after timeout of the ShdnTimer register 109 | #define HIB_BIT 1 // Hibernate Status. This bit is set to a 1 when the device is in hibernate mode or 0 when the device is in active mode. Hib is set to 0 at power-up. 110 | #define R100_BIT 13 // The R100 bit needs to be set when using a 100k NTC resistor 111 | #define VCHG_BIT 10 // Set to 1 for charge voltage higher than 4.25V (4.3V–4.4V). Set VChg to 0 for 4.2V charge voltage. 112 | #define MODEL_CFG_REFRESH_BIT 15 // Model Configuration Refresh. This bit is set to 1 to refresh the ModelGauge m5 algorithm configuration. This bit is automatically cleared to 0 after the refresh is complete. 113 | #define BATTERY_STATUS_BIT 3 // Useful when the IC is used in a host-side application. This bit is set to 0 when a battery is present in the system, and set to 1 when the battery is absent. Bst is set to 0 at power-up. 114 | #define TSEL_BIT 15 // Temperature sensor select. Set to 0 to use internal die temperature. Set to 1 to use temperature information from thermistor. ETHRM bit must be set to 1 when TSel is 1. 115 | #define ETHRM_BIT 4 // Enable Thermistor. Set to logic 1 to enable the TH pin measurement. 116 | #define TEN_BIT 9 // Enable Temperature Channel. Set to 1 and set ETHRM or FTHRM to 1 to enable temperature measurement. 117 | 118 | #endif -------------------------------------------------------------------------------- /src/Board.cpp: -------------------------------------------------------------------------------- 1 | #include "Board.h" 2 | #include "MAX1726Driver.h" 3 | #include 4 | 5 | constexpr int UNKNOWN_VALUE = 0xFF; 6 | 7 | std::map ldo2VoltageMap = { 8 | {1.80f, Ldo2Voltage::V_1_80}, 9 | {1.90f, Ldo2Voltage::V_1_90}, 10 | {2.00f, Ldo2Voltage::V_2_00}, 11 | {2.10f, Ldo2Voltage::V_2_10}, 12 | {2.20f, Ldo2Voltage::V_2_20}, 13 | {2.30f, Ldo2Voltage::V_2_30}, 14 | {2.40f, Ldo2Voltage::V_2_40}, 15 | {2.50f, Ldo2Voltage::V_2_50}, 16 | {2.60f, Ldo2Voltage::V_2_60}, 17 | {2.70f, Ldo2Voltage::V_2_70}, 18 | {2.80f, Ldo2Voltage::V_2_80}, 19 | {2.90f, Ldo2Voltage::V_2_90}, 20 | {3.00f, Ldo2Voltage::V_3_00}, 21 | {3.10f, Ldo2Voltage::V_3_10}, 22 | {3.20f, Ldo2Voltage::V_3_20}, 23 | {3.30f, Ldo2Voltage::V_3_30} 24 | }; 25 | 26 | std::map sw1VoltageMap = { 27 | {1.10f, Sw1Voltage::V_1_10}, 28 | {1.20f, Sw1Voltage::V_1_20}, 29 | {1.35f, Sw1Voltage::V_1_35}, 30 | {1.50f, Sw1Voltage::V_1_50}, 31 | {1.80f, Sw1Voltage::V_1_80}, 32 | {2.50f, Sw1Voltage::V_2_50}, 33 | {3.00f, Sw1Voltage::V_3_00}, 34 | {3.30f, Sw1Voltage::V_3_30} 35 | }; 36 | 37 | std::map sw2VoltageMap = { 38 | {1.10f, Sw2Voltage::V_1_10}, 39 | {1.20f, Sw2Voltage::V_1_20}, 40 | {1.35f, Sw2Voltage::V_1_35}, 41 | {1.50f, Sw2Voltage::V_1_50}, 42 | {1.80f, Sw2Voltage::V_1_80}, 43 | {2.50f, Sw2Voltage::V_2_50}, 44 | {3.00f, Sw2Voltage::V_3_00}, 45 | {3.30f, Sw2Voltage::V_3_30} 46 | }; 47 | 48 | Board::Board() { 49 | #if defined(ARDUINO_PORTENTA_C33) 50 | this->lowPower = new LowPower(); 51 | #endif 52 | } 53 | 54 | Board::~Board() { 55 | #if defined(ARDUINO_PORTENTA_C33) 56 | delete lowPower; 57 | #endif 58 | } 59 | 60 | bool Board::begin() { 61 | #if defined(ARDUINO_PORTENTA_H7_M7) 62 | if (CM7_CPUID == HAL_GetCurrentCPUID()){ 63 | if (LowPowerReturnCode::success != LowPower.checkOptionBytes()){ 64 | LowPower.prepareOptionBytes(); 65 | } 66 | bootM4(); 67 | } 68 | #endif 69 | return PMIC.begin() == 0; 70 | } 71 | 72 | bool Board::isUSBPowered() { 73 | uint16_t registerValue = PMIC.readPMICreg(Register::CHARGER_VBUS_SNS); 74 | return bitRead(registerValue, 5) == 1; // — VBUS is valid -> USB powered 75 | } 76 | 77 | bool Board::isBatteryPowered() { 78 | uint8_t registerValue = PMIC.readPMICreg(Register::CHARGER_BATT_SNS); 79 | uint8_t batteryPower = extractBits(registerValue, 0, 2); 80 | return batteryPower == 0; 81 | } 82 | 83 | void Board::setExternalPowerEnabled(bool on) { 84 | if(on) 85 | PMIC.getControl()->turnSw2On(Sw2Mode::Normal); 86 | else 87 | PMIC.getControl()->turnSw2Off(Sw2Mode::Normal); 88 | } 89 | 90 | bool Board::setExternalVoltage(float voltage) { 91 | this -> setExternalPowerEnabled(false); 92 | uint8_t targetVoltage = getRailVoltageEnum(voltage, CONTEXT_SW2); 93 | 94 | if (targetVoltage == UNKNOWN_VALUE){ 95 | return false; 96 | } 97 | 98 | PMIC.writePMICreg(Register::PMIC_SW2_VOLT, targetVoltage); 99 | if(PMIC.readPMICreg(Register::PMIC_SW2_VOLT) == targetVoltage){ 100 | this -> setExternalPowerEnabled(true); 101 | return true; 102 | } 103 | 104 | return false; 105 | } 106 | 107 | void Board::setCameraPowerEnabled(bool on) { 108 | #if defined(ARDUINO_NICLA_VISION) 109 | if(on){ 110 | PMIC.getControl()->turnLDO1On(Ldo1Mode::Normal); 111 | PMIC.getControl()->turnLDO2On(Ldo2Mode::Normal); 112 | PMIC.getControl()->turnLDO3On(Ldo3Mode::Normal); 113 | } else { 114 | PMIC.getControl()->turnLDO1Off(Ldo1Mode::Normal); 115 | PMIC.getControl()->turnLDO2Off(Ldo2Mode::Normal); 116 | PMIC.getControl()->turnLDO3Off(Ldo3Mode::Normal); 117 | } 118 | #endif 119 | } 120 | 121 | #if defined(ARDUINO_PORTENTA_C33) 122 | void Board::enableWakeupFromPin(uint8_t pin, PinStatus direction){ 123 | lowPower->enableWakeupFromPin(pin, direction); 124 | } 125 | #endif 126 | 127 | #if defined(ARDUINO_PORTENTA_H7) 128 | void Board::enableWakeupFromPin(){ 129 | standbyType |= StandbyType::untilPinActivity; 130 | } 131 | 132 | void Board::enableSleepWhenIdle(){ 133 | LowPower.allowDeepSleep(); 134 | } 135 | #endif 136 | 137 | 138 | #if defined(ARDUINO_PORTENTA_C33) 139 | bool Board::enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, void (* const callbackFunction)(), RTClock * rtc){ 140 | return lowPower->setWakeUpAlarm(hours, minutes, seconds, callbackFunction, rtc); 141 | } 142 | 143 | bool Board::enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, RTClock * rtc){ 144 | return enableWakeupFromRTC(hours, minutes, seconds, nullptr, rtc); 145 | } 146 | 147 | #endif 148 | 149 | #if defined(ARDUINO_PORTENTA_H7) 150 | bool Board::enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds){ 151 | standbyType |= StandbyType::untilTimeElapsed; 152 | wakeupDelayHours = hours; 153 | wakeupDelayMinutes = minutes; 154 | wakeupDelaySeconds = seconds; 155 | return true; 156 | } 157 | #endif 158 | 159 | #if defined(ARDUINO_PORTENTA_C33) 160 | void Board::sleepUntilWakeupEvent(){ 161 | lowPower -> sleep(); 162 | } 163 | #endif 164 | 165 | void Board::standByUntilWakeupEvent(){ 166 | #if defined(ARDUINO_PORTENTA_C33) 167 | lowPower -> deepSleep(); 168 | #elif defined(ARDUINO_GENERIC_STM32H747_M4) 169 | LowPower.standbyM4(); 170 | #elif defined(ARDUINO_PORTENTA_H7_M7) 171 | RTCWakeupDelay rtcWakeupDelay = RTCWakeupDelay(wakeupDelayHours, wakeupDelayMinutes, wakeupDelaySeconds); 172 | if(standbyType == StandbyType::untilEither) 173 | LowPower.standbyM7(LowPowerStandbyType::untilPinActivity | LowPowerStandbyType::untilTimeElapsed, rtcWakeupDelay); 174 | else if (standbyType == StandbyType::untilPinActivity) 175 | LowPower.standbyM7(LowPowerStandbyType::untilPinActivity); 176 | else if (standbyType == StandbyType::untilTimeElapsed) 177 | LowPower.standbyM7(LowPowerStandbyType::untilTimeElapsed, rtcWakeupDelay); 178 | #endif 179 | } 180 | 181 | void Board::setAllPeripheralsPower(bool on){ 182 | #if defined(ARDUINO_PORTENTA_C33) 183 | // The power architecture on the C33 puts peripherals on sepparate power lanes which are independent, so we can turn off the peripherals separately. 184 | // Turning these rails will not interfere with your sketch. 185 | this -> setCommunicationPeripheralsPower(on); 186 | this -> setExternalPowerEnabled(on); 187 | this -> setAnalogDigitalConverterPower(on); 188 | #else if defined(ARDUINO_PORTENTA_H7) 189 | // On the H7 several chips need different voltages, so we cannot turn the lanes that are dependent on each other separately. 190 | // This should only be used when going into standby mode, as turning off the USB-C PHY, Ethernet or Video bridge might cause undefined behaviour. 191 | if(on){ 192 | PMIC.getControl() -> turnLDO2On(Ldo2Mode::Normal); 193 | PMIC.getControl() -> turnLDO1On(Ldo1Mode::Normal); 194 | PMIC.getControl() -> turnLDO3On(Ldo3Mode::Normal); 195 | PMIC.getControl() -> turnSw1On(Sw1Mode::Normal); 196 | } else { 197 | PMIC.getControl() -> turnLDO2Off(Ldo2Mode::Normal); 198 | PMIC.getControl() -> turnLDO1Off(Ldo1Mode::Normal); 199 | PMIC.getControl() -> turnLDO3Off(Ldo3Mode::Normal); 200 | PMIC.getControl() -> turnSw1Off(Sw1Mode::Normal); 201 | } 202 | #endif 203 | } 204 | // 205 | void Board::setAnalogDigitalConverterPower(bool on){ 206 | #if defined(ARDUINO_PORTENTA_C33) 207 | if(on){ 208 | PMIC.getControl()->turnLDO1On(Ldo1Mode::Normal); 209 | } else { 210 | PMIC.getControl()->turnLDO1Off(Ldo1Mode::Normal); 211 | } 212 | #endif 213 | // On the H7 the ADC is powered by the main MCU power lane, so we cannot turn it off independently. 214 | } 215 | 216 | void Board::setCommunicationPeripheralsPower(bool on){ 217 | #if defined(ARDUINO_PORTENTA_C33) 218 | if(on){ 219 | PMIC.getControl()->turnSw1On(Sw1Mode::Normal); 220 | } else { 221 | PMIC.getControl()->turnSw1Off(Sw1Mode::Normal); 222 | } 223 | #endif 224 | // On the H7 the communication peripherals are powered by the main MCU power lane, 225 | // so we cannot turn them off independently. 226 | } 227 | 228 | 229 | bool Board::setReferenceVoltage(float voltage) { 230 | uint8_t voltageRegisterValue = getRailVoltageEnum(voltage, CONTEXT_LDO2); 231 | 232 | // If voltageRegisterValue is not empty, write it to the PMIC register 233 | // and return the result of the comparison directly. 234 | if (voltageRegisterValue != UNKNOWN_VALUE) { 235 | PMIC.writePMICreg(Register::PMIC_LDO2_VOLT, voltageRegisterValue); 236 | return (PMIC.readPMICreg(Register::PMIC_LDO2_VOLT) == voltageRegisterValue); 237 | } 238 | 239 | return false; 240 | } 241 | 242 | uint8_t Board::getRailVoltageEnum(float voltage, int context) { 243 | switch (context) { 244 | case CONTEXT_LDO2: 245 | if (ldo2VoltageMap.find(voltage) != ldo2VoltageMap.end()) { 246 | return static_cast(ldo2VoltageMap[voltage]); 247 | } 248 | break; 249 | 250 | case CONTEXT_SW1: 251 | if (sw1VoltageMap.find(voltage) != sw1VoltageMap.end()) { 252 | return static_cast(sw1VoltageMap[voltage]); 253 | } 254 | break; 255 | 256 | case CONTEXT_SW2: 257 | if (sw2VoltageMap.find(voltage) != sw2VoltageMap.end()) { 258 | return static_cast(sw2VoltageMap[voltage]); 259 | } 260 | break; 261 | 262 | default: 263 | return UNKNOWN_VALUE; 264 | } 265 | 266 | 267 | return UNKNOWN_VALUE; 268 | } 269 | 270 | void Board::shutDownFuelGauge() { 271 | #if defined(ARDUINO_PORTENTA_C33) 272 | MAX1726Driver fuelGauge(&Wire3); 273 | #elif defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_GENERIC_STM32H747_M4) 274 | MAX1726Driver fuelGauge(&Wire1); 275 | #elif defined(ARDUINO_NICLA_VISION) 276 | MAX1726Driver fuelGauge(&Wire1); 277 | #else 278 | #error "The selected board is not supported by the Battery class." 279 | #endif 280 | fuelGauge.setOperationMode(FuelGaugeOperationMode::shutdown); 281 | } 282 | -------------------------------------------------------------------------------- /src/Board.h: -------------------------------------------------------------------------------- 1 | #ifndef BOARD_H 2 | #define BOARD_H 3 | 4 | #include 5 | #include 6 | #include "WireUtils.h" 7 | 8 | #if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_GENERIC_STM32H747_M4) 9 | #define ARDUINO_PORTENTA_H7 10 | #endif 11 | 12 | #if defined(ARDUINO_PORTENTA_C33) 13 | #include "Arduino_LowPowerPortentaC33.h" 14 | #include "RTC.h" 15 | #elif defined(ARDUINO_PORTENTA_H7) 16 | // When used standalone the LowPowerPortentaH7 library will turn off the Ethernet interface to avoid issues with the line termination resistors, 17 | // but in this library we can turn of the Ethernet interface using the PMIC, so we set the NO_ETHERNET_TURN_OFF flag to avoid turning off the Ethernet interface from both sides. 18 | #define NO_ETHERNET_TURN_OFF 19 | #include "Arduino_LowPowerPortentaH7.h" 20 | #endif 21 | 22 | #define CONTEXT_LDO2 2 // LDO regulator: 1.8 V to 3.3 V, 400 mA 23 | #define CONTEXT_SW1 3 // Buck converter: 1.0 A; 0.6 V to 1.3875 V 24 | #define CONTEXT_SW2 4 // Buck converter: 1.0 A; 0.6 V to 1.3875 V 25 | 26 | enum class StandbyType : uint8_t { 27 | none = 0, 28 | untilPinActivity = 1, 29 | untilTimeElapsed = 2, 30 | untilEither = 3 31 | }; 32 | 33 | inline constexpr StandbyType operator|(StandbyType x, StandbyType y){ 34 | return static_cast(static_cast(x) | static_cast(y)); 35 | } 36 | 37 | inline constexpr StandbyType operator|=(StandbyType& x, StandbyType y){ 38 | return x = x | y; 39 | } 40 | 41 | /** 42 | * @brief Represents a board with power management capabilities. 43 | * 44 | * The Board class provides methods to check the power source, enable/disable power rails, 45 | * set voltage levels, enable/disable wakeup from pins or RTC, 46 | * put the device into sleep mode for a specified duration, and control peripherals' power. 47 | * 48 | * Supported boards: Arduino Portenta H7, Arduino Portenta C33, Arduino Nicla Vision. 49 | */ 50 | class Board { 51 | public: 52 | /** 53 | * @brief Construct a new Board object. 54 | */ 55 | Board(); 56 | 57 | /** 58 | * @brief Destroy the Board object. 59 | */ 60 | ~Board(); 61 | 62 | /** 63 | * @brief Initializes the board by initiating the PMIC. 64 | * 65 | * @return true if the board initialization is successful, false otherwise. 66 | */ 67 | bool begin(); 68 | 69 | /** 70 | * @brief Check if the board is powered through USB. 71 | * @return True if powered through USB, false otherwise. 72 | */ 73 | bool isUSBPowered(); 74 | 75 | /** 76 | * @brief Check if the board is powered by the battery. 77 | * @return True if powered by the battery, false otherwise. 78 | */ 79 | bool isBatteryPowered(); 80 | 81 | /** 82 | * Enables/disables the voltage on the external power rail. 83 | * This lane powers the pin labeled 3V3 on the board. 84 | * @param on True to enable this power rail, false to disable it. 85 | */ 86 | void setExternalPowerEnabled(bool on); 87 | 88 | 89 | /** 90 | * Set the voltage for the external power rail. 91 | * This lane powers the pin labeled 3V3 on the board. 92 | * @param voltage float value of the voltage value to set. 93 | * Value has to be one of the following (1.10, 1.20, 1.35, 1.50, 1.80, 2.50, 3.00, 3.30) 94 | * @return True the voltage was set successfully, false otherwise. 95 | */ 96 | bool setExternalVoltage(float voltage); 97 | 98 | 99 | /** 100 | * @brief Enables/disables the camera's power rail on boards with a built-in camera. 101 | * @param enabled True to turn on the camera, false to turn it off. 102 | */ 103 | void setCameraPowerEnabled(bool enabled); 104 | 105 | 106 | #if defined(ARDUINO_PORTENTA_H7) 107 | 108 | /** 109 | * Enables wakeup from pin GPIO0 on Portenta H7. 110 | * The pin is only accessible via high-density connectors. 111 | */ 112 | void enableWakeupFromPin(); 113 | 114 | /** 115 | * Enables sleep mode when the board is idle. 116 | */ 117 | void enableSleepWhenIdle(); 118 | #endif 119 | 120 | #if defined(ARDUINO_PORTENTA_C33) 121 | /** 122 | * Enables wake-up of the device from a specified pin (A0, A1, A2, A3, A4, A5, D4, D7 ) on Arduino Portenta C33. 123 | * @param pin The pin number used for waking up the device. 124 | * @param direction The direction of the interrupt that will wake up the device. (RISING, FALLING) 125 | */ 126 | void enableWakeupFromPin(uint8_t pin, PinStatus direction); 127 | #endif 128 | 129 | 130 | #if defined(ARDUINO_PORTENTA_C33) 131 | /** 132 | * @brief Enables wake-up of the device from the RTC. 133 | * This function allows to use a custom RTC instance to put the device in sleep mode. 134 | * @param hours The number of hours to sleep. 135 | * @param minutes The number of minutes to sleep. 136 | * @param seconds The number of seconds to sleep. 137 | * @param callbackFunction The function to call when the device wakes up. 138 | * If no callback function is provided, the device will wake up without calling any function. 139 | * @param rtc The RTC instance to use for the sleep function. 140 | * If no RTC instance is provided, the default RTC instance is used. 141 | * @return True if successful, false otherwise. 142 | */ 143 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, void (* const callbackFunction)(), RTClock * rtc = &RTC); 144 | 145 | /** 146 | * @brief Enables wake-up of the device from the RTC. 147 | * @param hours The number of hours to sleep. 148 | * @param minutes The number of minutes to sleep. 149 | * @param seconds The number of seconds to sleep. 150 | * @param rtc The RTC instance to use for the sleep function. Default is the shared RTC instance. 151 | * @return True if successful, false otherwise. 152 | */ 153 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds, RTClock * rtc = &RTC); 154 | #endif 155 | 156 | #if defined(ARDUINO_PORTENTA_H7) 157 | /** 158 | * Enables wake-up of the device from the RTC. 159 | * @param hours The number of hours to sleep. 160 | * @param minutes The number of minutes to sleep. 161 | * @param seconds The number of seconds to sleep. 162 | * @return True if successful, false otherwise. 163 | */ 164 | bool enableWakeupFromRTC(uint32_t hours, uint32_t minutes, uint32_t seconds); 165 | #endif 166 | 167 | 168 | #if defined(ARDUINO_PORTENTA_C33) 169 | /** 170 | * Put the device into sleep mode until a wakeup event occurs 171 | * This sleep mode is ideal for applications requiring periodic wake-ups or 172 | * brief intervals of inactivity and reduces consumption to a range between 173 | * 6mA and 18mA depending on the state of the peripherals. 174 | * This sleep mode resumes the operation from the last operation without resetting the board. 175 | * A wakeup event can be an interrupt on a pin or the RTC, 176 | * depending on what you set with enableWakeupFromPin() and enableWakeupFromRTC(). 177 | */ 178 | void sleepUntilWakeupEvent(); 179 | #endif 180 | 181 | 182 | /** 183 | * Put the device into standby mode until a wakeup event occurs. 184 | * For scenarios demanding drastic power conservation, the standby Mode significantly reduces 185 | * the board's power usage to micro amperes range depending on the state of the peripherals. 186 | * This mode restarts the board on wake-up, effectively running the setup() function again. 187 | * A wakeup event can be an interrupt on a pin or the RTC, depending on what 188 | * you set with enableWakeupFromPin() and enableWakeupFromRTC(). 189 | */ 190 | void standByUntilWakeupEvent(); 191 | 192 | /** 193 | * @brief Toggle the peripherals' power on Portenta C33 (ADC, RGB LED, Secure Element, Wifi and Bluetooth). 194 | * @param on True to turn on the power, false to turn it off. 195 | */ 196 | void setAllPeripheralsPower(bool on); 197 | 198 | /** 199 | * @brief Toggles the communication peripherials' power on Portenta C33 (Wifi, Bluetooth and Secure Element) 200 | * @param on True to turn on the power, false to turn it off. 201 | */ 202 | void setCommunicationPeripheralsPower(bool on); 203 | 204 | /** 205 | * @brief Toggles the power of the analog digital converter on Portenta C33. 206 | * This is not available on the Portenta H7. 207 | * @param on True to turn on the power, false to turn it off. 208 | */ 209 | void setAnalogDigitalConverterPower(bool on); 210 | 211 | /** 212 | * @brief Set the reference voltage. This value is used by the ADC to convert analog values to digital values. 213 | * This can be particularly useful to increase the accuracy of the ADC when working with low voltages 214 | * @param voltage Reference voltage value in volts. It can be anything between 1.80V and 3.30V in steps of 0.10V. 215 | * Any value outside this range or with different steps will not be accepted by the library. 216 | * @return True if the voltage was set successfully, false otherwise. 217 | */ 218 | bool setReferenceVoltage(float voltage); 219 | 220 | /** 221 | * @brief Shuts down the fuel gauge to reduce power consumption. 222 | * The IC returns to active mode on any edge of any communication line. 223 | * If the IC is power-cycled or the software RESET command is sent the IC 224 | * returns to active mode of operation. 225 | */ 226 | void shutDownFuelGauge(); 227 | 228 | private: 229 | /** 230 | * Convert a numeric voltage value to the corresponding enum value for the PMIC library. 231 | */ 232 | static uint8_t getRailVoltageEnum(float voltage, int context); 233 | 234 | #if defined(ARDUINO_PORTENTA_C33) 235 | LowPower * lowPower; 236 | #endif 237 | 238 | StandbyType standbyType = StandbyType::none; 239 | uint32_t wakeupDelayHours; 240 | uint32_t wakeupDelayMinutes; 241 | uint32_t wakeupDelaySeconds; 242 | }; 243 | 244 | #endif -------------------------------------------------------------------------------- /src/Charger.cpp: -------------------------------------------------------------------------------- 1 | #include "Charger.h" 2 | #include 3 | 4 | std::map chargeCurrentMap = { 5 | {100, ChargeCurrent::I_100_mA}, 6 | {150, ChargeCurrent::I_150_mA}, 7 | {200, ChargeCurrent::I_200_mA}, 8 | {250, ChargeCurrent::I_250_mA}, 9 | {300, ChargeCurrent::I_300_mA}, 10 | {350, ChargeCurrent::I_350_mA}, 11 | {400, ChargeCurrent::I_400_mA}, 12 | {450, ChargeCurrent::I_450_mA}, 13 | {500, ChargeCurrent::I_500_mA}, 14 | {550, ChargeCurrent::I_550_mA}, 15 | {600, ChargeCurrent::I_600_mA}, 16 | {650, ChargeCurrent::I_650_mA}, 17 | {700, ChargeCurrent::I_700_mA}, 18 | {750, ChargeCurrent::I_750_mA}, 19 | {800, ChargeCurrent::I_800_mA}, 20 | {850, ChargeCurrent::I_850_mA}, 21 | {900, ChargeCurrent::I_900_mA}, 22 | {950, ChargeCurrent::I_950_mA}, 23 | {1000, ChargeCurrent::I_1000_mA} 24 | }; 25 | 26 | std::map chargeVoltageMap = { 27 | {3.50, ChargeVoltage::V_3_50}, 28 | {3.52, ChargeVoltage::V_3_52}, 29 | {3.54, ChargeVoltage::V_3_54}, 30 | {3.56, ChargeVoltage::V_3_56}, 31 | {3.58, ChargeVoltage::V_3_58}, 32 | {3.60, ChargeVoltage::V_3_60}, 33 | {3.62, ChargeVoltage::V_3_62}, 34 | {3.64, ChargeVoltage::V_3_64}, 35 | {3.66, ChargeVoltage::V_3_66}, 36 | {3.68, ChargeVoltage::V_3_68}, 37 | {3.70, ChargeVoltage::V_3_70}, 38 | {3.72, ChargeVoltage::V_3_72}, 39 | {3.74, ChargeVoltage::V_3_74}, 40 | {3.76, ChargeVoltage::V_3_76}, 41 | {3.78, ChargeVoltage::V_3_78}, 42 | {3.80, ChargeVoltage::V_3_80}, 43 | {3.82, ChargeVoltage::V_3_82}, 44 | {3.84, ChargeVoltage::V_3_84}, 45 | {3.86, ChargeVoltage::V_3_86}, 46 | {3.88, ChargeVoltage::V_3_88}, 47 | {3.90, ChargeVoltage::V_3_90}, 48 | {3.92, ChargeVoltage::V_3_92}, 49 | {3.94, ChargeVoltage::V_3_94}, 50 | {3.96, ChargeVoltage::V_3_96}, 51 | {3.98, ChargeVoltage::V_3_98}, 52 | {4.00, ChargeVoltage::V_4_00}, 53 | {4.02, ChargeVoltage::V_4_02}, 54 | {4.04, ChargeVoltage::V_4_04}, 55 | {4.06, ChargeVoltage::V_4_06}, 56 | {4.08, ChargeVoltage::V_4_08}, 57 | {4.10, ChargeVoltage::V_4_10}, 58 | {4.12, ChargeVoltage::V_4_12}, 59 | {4.14, ChargeVoltage::V_4_14}, 60 | {4.16, ChargeVoltage::V_4_16}, 61 | {4.18, ChargeVoltage::V_4_18}, 62 | {4.20, ChargeVoltage::V_4_20}, 63 | {4.22, ChargeVoltage::V_4_22}, 64 | {4.24, ChargeVoltage::V_4_24}, 65 | {4.26, ChargeVoltage::V_4_26}, 66 | {4.28, ChargeVoltage::V_4_28}, 67 | {4.30, ChargeVoltage::V_4_30}, 68 | {4.32, ChargeVoltage::V_4_32}, 69 | {4.34, ChargeVoltage::V_4_34}, 70 | {4.36, ChargeVoltage::V_4_36}, 71 | {4.38, ChargeVoltage::V_4_38}, 72 | {4.40, ChargeVoltage::V_4_40}, 73 | {4.42, ChargeVoltage::V_4_42}, 74 | {4.44, ChargeVoltage::V_4_44} 75 | }; 76 | 77 | std::map endOfChargeCurrentMap = { 78 | {5, EndOfChargeCurrent::I_5_mA}, 79 | {10, EndOfChargeCurrent::I_10_mA}, 80 | {20, EndOfChargeCurrent::I_20_mA}, 81 | {30, EndOfChargeCurrent::I_30_mA}, 82 | {50, EndOfChargeCurrent::I_50_mA} 83 | }; 84 | 85 | std::map inputCurrentLimitMap = { 86 | {10, InputCurrentLimit::I_10_mA}, 87 | {15, InputCurrentLimit::I_15_mA}, 88 | {20, InputCurrentLimit::I_20_mA}, 89 | {25, InputCurrentLimit::I_25_mA}, 90 | {30, InputCurrentLimit::I_30_mA}, 91 | {35, InputCurrentLimit::I_35_mA}, 92 | {40, InputCurrentLimit::I_40_mA}, 93 | {45, InputCurrentLimit::I_45_mA}, 94 | {50, InputCurrentLimit::I_50_mA}, 95 | {100, InputCurrentLimit::I_100_mA}, 96 | {150, InputCurrentLimit::I_150_mA}, 97 | {200, InputCurrentLimit::I_200_mA}, 98 | {300, InputCurrentLimit::I_300_mA}, 99 | {400, InputCurrentLimit::I_400_mA}, 100 | {500, InputCurrentLimit::I_500_mA}, 101 | {600, InputCurrentLimit::I_600_mA}, 102 | {700, InputCurrentLimit::I_700_mA}, 103 | {800, InputCurrentLimit::I_800_mA}, 104 | {900, InputCurrentLimit::I_900_mA}, 105 | {1000, InputCurrentLimit::I_1000_mA}, 106 | {1500, InputCurrentLimit::I_1500_mA} 107 | }; 108 | 109 | 110 | Charger::Charger(){} 111 | 112 | bool Charger::begin(){ 113 | return PMIC.begin() == 0; 114 | } 115 | 116 | bool Charger::setChargeCurrent(uint16_t current) { 117 | #if defined(ARDUINO_NICLA_VISION) 118 | return false; // Not supported on Nicla Vision 119 | #endif 120 | 121 | if (chargeCurrentMap.find(current) != chargeCurrentMap.end()) { 122 | ChargeCurrent convertedCurrent = chargeCurrentMap[current]; 123 | PMIC.getControl() -> setFastChargeCurrent(convertedCurrent); 124 | return true; 125 | } 126 | return false; 127 | } 128 | 129 | uint16_t Charger::getChargeCurrent() { 130 | auto currentValue = PMIC.readPMICreg(Register::CHARGER_CHG_CURR_CFG); 131 | currentValue = (currentValue & REG_CHG_CURR_CFG_CHG_CC_mask); 132 | ChargeCurrent current = static_cast(currentValue); 133 | for (auto const& [key, val] : chargeCurrentMap) { 134 | if (val == current) { 135 | return key; 136 | } 137 | } 138 | return -1; 139 | } 140 | 141 | float Charger::getChargeVoltage() { 142 | uint8_t currentValue = PMIC.readPMICreg(Register::CHARGER_BATT_REG); 143 | currentValue = (currentValue & REG_BATT_REG_CHCCV_mask); 144 | ChargeVoltage voltageValue = static_cast(currentValue); 145 | 146 | // Lookup the value in the map 147 | for (auto const& [key, val] : chargeVoltageMap) { 148 | if (val == voltageValue) { 149 | return key; 150 | } 151 | } 152 | return -1; 153 | } 154 | 155 | bool Charger::setChargeVoltage(float voltage) { 156 | if(chargeVoltageMap.find(voltage) != chargeVoltageMap.end()) { 157 | ChargeVoltage convertedVoltage = chargeVoltageMap[voltage]; 158 | PMIC.getControl() -> setFastChargeVoltage(convertedVoltage); 159 | return true; 160 | } 161 | return false; 162 | } 163 | 164 | bool Charger::setEndOfChargeCurrent(uint16_t current) { 165 | #if defined(ARDUINO_NICLA_VISION) 166 | return false; // Not supported on Nicla Vision 167 | #endif 168 | if(endOfChargeCurrentMap.find(current) != endOfChargeCurrentMap.end()) { 169 | EndOfChargeCurrent convertedCurrent = endOfChargeCurrentMap[current]; 170 | PMIC.getControl() -> setEndOfChargeCurrent(convertedCurrent); 171 | return true; 172 | } 173 | return false; 174 | } 175 | 176 | uint16_t Charger::getEndOfChargeCurrent() { 177 | uint8_t currentValue = PMIC.readPMICreg(Register::CHARGER_CHG_EOC_CNFG); 178 | currentValue = (currentValue & REG_CHG_EOC_CNFG_IEOC_mask); 179 | EndOfChargeCurrent current = static_cast(currentValue); 180 | for (auto const& [key, val] : endOfChargeCurrentMap) { 181 | if (val == current) { 182 | return key; 183 | } 184 | } 185 | return -1; 186 | } 187 | 188 | bool Charger::setInputCurrentLimit(uint16_t current) { 189 | if(inputCurrentLimitMap.find(current) != inputCurrentLimitMap.end()) { 190 | InputCurrentLimit convertedCurrent = inputCurrentLimitMap[current]; 191 | PMIC.getControl() -> setInputCurrentLimit(convertedCurrent); 192 | return true; 193 | } 194 | return false; 195 | } 196 | 197 | uint16_t Charger::getInputCurrentLimit() { 198 | uint8_t currentValue = PMIC.readPMICreg(Register::CHARGER_VBUS_INLIM_CNFG); 199 | currentValue = (currentValue & REG_VBUS_INLIM_CNFG_VBUS_LIN_INLIM_mask); 200 | InputCurrentLimit current = static_cast(currentValue); 201 | for (auto const& [key, val] : inputCurrentLimitMap) { 202 | if (val == current) { 203 | return key; 204 | } 205 | } 206 | return -1; 207 | } 208 | 209 | bool Charger::isEnabled(){ 210 | return PMIC.readPMICreg(Register::CHARGER_CHG_OPER) == 0x02; 211 | } 212 | 213 | bool Charger::setEnabled(bool enabled){ 214 | if(enabled){ 215 | PMIC.writePMICreg(Register::CHARGER_CHG_OPER, 0x02); 216 | return PMIC.readPMICreg(Register::CHARGER_CHG_OPER) == 0x02; 217 | } else { 218 | PMIC.writePMICreg(Register::CHARGER_CHG_OPER, 0x01); 219 | return PMIC.readPMICreg(Register::CHARGER_CHG_OPER) == 0x01; 220 | } 221 | } 222 | 223 | ChargingState Charger::getState(){ 224 | uint8_t reg_val = PMIC.readPMICreg(Register::CHARGER_CHG_SNS); 225 | switch (extractBits(reg_val, 0, 3)) { 226 | case 0: 227 | return ChargingState::preCharge; 228 | case 1: 229 | return ChargingState::fastChargeConstantCurrent; 230 | case 2: 231 | return ChargingState::fastChargeConstantVoltage; 232 | case 3: 233 | return ChargingState::endOfCharge; 234 | case 4: 235 | return ChargingState::done; 236 | case 6: 237 | return ChargingState::timerFaultError; 238 | case 7: 239 | return ChargingState::thermistorSuspendError; 240 | case 8: 241 | return ChargingState::chargerDisabled; 242 | case 9: 243 | return ChargingState::batteryOvervoltageError; 244 | case 12: 245 | return ChargingState::chargerBypassed; 246 | default: 247 | return ChargingState::none; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/Charger.h: -------------------------------------------------------------------------------- 1 | #ifndef CHARGER_H 2 | 3 | #include 4 | #include "WireUtils.h" 5 | 6 | typedef VFastCharge ChargeVoltage; 7 | typedef IFastCharge ChargeCurrent; 8 | typedef IEndOfCharge EndOfChargeCurrent; 9 | typedef IInputCurrentLimit InputCurrentLimit; 10 | 11 | /** 12 | * Enum representing different states of charging. 13 | */ 14 | 15 | enum class ChargingState { 16 | /** 17 | * Provided by the registers, not used in this API. 18 | */ 19 | none = -1, 20 | 21 | /** 22 | * First stage of the charging process, prepares battery for the charging process. 23 | */ 24 | preCharge = 0, 25 | 26 | /** 27 | * Second phase of the charging process where the battery is charging in constant current mode until it reaches the voltage where the it's considered fully charged. (4.2V) 28 | */ 29 | fastChargeConstantCurrent = 1, 30 | 31 | /** 32 | * Third phase of the charging process where the battery is kept at the fully charged voltage and current is slowly decreased to the end of charge current. 33 | */ 34 | fastChargeConstantVoltage = 2, 35 | 36 | /** 37 | * If the battery is still connected, the charger will ensure it's kept at 4.2V by topping up the voltage to avoid self discharge. 38 | */ 39 | endOfCharge = 3, 40 | 41 | /** 42 | * Battery is fully charged 43 | */ 44 | done = 4, 45 | 46 | /** 47 | * The timer that is monitoring the charge status has encountered an error. 48 | */ 49 | timerFaultError = 6, 50 | 51 | /** 52 | * Charging was suspended due to overheating 53 | */ 54 | thermistorSuspendError = 7, 55 | 56 | /** 57 | * Charger is disabled 58 | */ 59 | chargerDisabled = 8, 60 | 61 | /** 62 | * Charging was suspended due to an overvoltage fault 63 | */ 64 | batteryOvervoltageError = 9, 65 | 66 | /** 67 | * The charger is bypassed completely and the USB voltage is powering the board 68 | */ 69 | chargerBypassed = 12 70 | }; 71 | 72 | /** 73 | * @brief Class for controlling charging parameters and monitoring charging status. 74 | */ 75 | class Charger { 76 | public: 77 | /** 78 | * @brief Constructs a new Charger object. 79 | */ 80 | Charger(); 81 | 82 | /** 83 | * @brief Initializes the charger by initiating the PMIC. 84 | * 85 | * @return true if the charger initialization is successful, false otherwise. 86 | */ 87 | bool begin(); 88 | 89 | /** 90 | * @brief Set the charging current. 91 | * The default charging current is set to 100mA. 92 | * @param current Charging current in milli amperes (mA). 93 | * Supported values: 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000 94 | * @return True if successful, false if an invalid value was provided or if the PMIC communication failed. 95 | */ 96 | bool setChargeCurrent(uint16_t current); 97 | 98 | /** 99 | * @brief Get the charge current in milli amperes (mA). 100 | * @return The charge current in float. 101 | */ 102 | uint16_t getChargeCurrent(); 103 | 104 | /** 105 | * @brief Set the charging voltage in volts (V). 106 | * The current charging voltage is set to 4.2V by default. 107 | * @param voltage Charging voltage in volts (V). 108 | * Supported values: 3.50, 3.52, 3.54, 3.56, 3.58, 3.60, 3.62, 3.64, 3.66, 3.68, 3.70, 3.72, 3.74, 3.76, 109 | * 3.78, 3.80, 3.82, 3.84, 3.86, 3.88, 3.90, 3.92, 3.94, 3.96, 3.98, 4.00, 4.02, 4.04, 4.06, 4.08, 4.10, 110 | * 4.12, 4.14, 4.16, 4.18, 4.20, 4.22, 4.24, 4.26, 4.28, 4.30, 4.32, 4.34, 4.36, 4.38, 4.40, 4.42, 4.44 111 | * @return True if successful, false if an invalid value was provided or if the PMIC communication failed. 112 | */ 113 | bool setChargeVoltage(float voltage); 114 | 115 | /** 116 | * @brief Get the charge voltage in volts (V). 117 | * @return The charge voltage as a float value. 118 | */ 119 | float getChargeVoltage(); 120 | 121 | /** 122 | * @brief Set the end-of-charge current. 123 | * The charger IC determines when to terminate the charge cycle based on the current going into the battery 124 | * dropping below the given threshold during the constant voltage phase. At this point, the battery 125 | * is considered fully charged and charging is completed. If charge termination is disabled, 126 | * the charge current will naturally decay to 0mA, but this is rarely done in practice. 127 | * This is because the amount of charge going into the battery exponentially decreases during CV charging, 128 | * and it would take a significantly longer time to recharge the battery with a very little increase in capacity. 129 | * @param current End-of-charge current in milli amperes (mA). 130 | * The default end-of-charge current is set to 50 mA. 131 | * Supported values: 5, 10, 20, 30, 50 132 | * @return True if successful, false if an invalid value was provided or if the PMIC communication failed. 133 | */ 134 | bool setEndOfChargeCurrent(uint16_t current); 135 | 136 | /** 137 | * @brief Get the end of charge current. 138 | * 139 | * This function returns the current value at which the charging process is considered complete. 140 | * Charging is terminated when the supplied current drops below the pre-programmed end of charge level. 141 | * 142 | * @return The end of charge current. 143 | */ 144 | uint16_t getEndOfChargeCurrent(); 145 | 146 | /** 147 | * @brief The input current limit (ILIM) safeguards the device by preventing overcurrent, 148 | * ensuring the charging current is within safe levels for the battery, and adapting to the maximum 149 | * current the power source can provide, allowing you to charge and use the system at the same time. 150 | * The default input current limit is set to 1.5A. 151 | * @param current Maximum input current in milli amperes (mA). 152 | * Supported values: 10, 15, 20, 25, 30, 35, 40, 45, 50, 100, 150, 200, 300, 153 | * 400, 500, 600, 700, 800, 900, 1000, 1500 154 | * @return True if successful, false if an invalid value was provided or if the PMIC communication failed. 155 | */ 156 | bool setInputCurrentLimit(uint16_t current); 157 | 158 | /** 159 | * @brief Get the input current limit. It is a safeguard to prevent overcurrent when charging 160 | * respectively to the maximum current the power source can provide. 161 | * 162 | * @return The input current limit in milli amperes (mA). 163 | */ 164 | uint16_t getInputCurrentLimit(); 165 | 166 | /** 167 | * @brief Get the current charging status. 168 | * @return Charging status enum value (ChargingState). 169 | * The possible states are: 170 | * - none: Provided by the registers, not used in this API. 171 | * - preCharge: First stage of the charging process, prepares battery for the charging process. 172 | * - fastChargeConstantCurrent: Second phase of the charging process where the battery is charging in constant current mode until it reaches the voltage where the it's considered fully charged. (4.2V) 173 | * - fastChargeConstantVoltage: Third phase of the charging process where the battery is kept at the fully charged voltage and current is slowly decreased to the end of charge current. 174 | * - endOfCharge: If the battery is still connected, the charger will ensure it's kept at 4.2V by topping up the voltage to avoid self discharge. 175 | * - done: Battery is fully charged 176 | * - timerFaultError: The timer that is monitoring the charge status has encountered an error. 177 | * - thermistorSuspendError: Charging was suspended due to overheating 178 | * - chargerDisabled: Charger is disabled 179 | * - batteryOvervoltageError: Charging was suspended due to an overvoltage fault 180 | * - chargerBypassed: The charger is bypassed completely and the USB voltage is powering the board 181 | */ 182 | ChargingState getState(); 183 | 184 | /** 185 | * @brief Checks if the charger and thus charging is enabled. 186 | * By default, the charger is enabled. 187 | * @return true if the charger is enabled, false otherwise. 188 | */ 189 | bool isEnabled(); 190 | 191 | /** 192 | * @brief Sets the enabled state of the charger. 193 | * When enabling it uses the default settings or the last saved parameters, depending on what was set previously. 194 | * @param enabled The desired enabled state of the charger. 195 | * @return true if the enabled state was successfully set, false otherwise. 196 | */ 197 | bool setEnabled(bool enabled); 198 | }; 199 | 200 | #endif // CHARGER_H -------------------------------------------------------------------------------- /src/MAX1726Driver.h: -------------------------------------------------------------------------------- 1 | #include "WireUtils.h" 2 | #include "BatteryConstants.h" 3 | 4 | enum class FuelGaugeOperationMode { 5 | hibernate, 6 | shutdown, 7 | active 8 | }; 9 | 10 | constexpr uint8_t DEFAULT_I2C_ADDRESS = 0x36; 11 | 12 | /** 13 | * @brief Driver class for the MAX1726 Fuel Gauge IC. 14 | */ 15 | class MAX1726Driver { 16 | private: 17 | TwoWire *wire; 18 | uint8_t i2cAddress; 19 | /** 20 | * Enables or disables the hibernate mode. 21 | * 22 | * @param enabled - true to enable hibernate mode, false to disable it. 23 | */ 24 | void setHibernateModeEnabled(bool enabled); 25 | public: 26 | /** 27 | * Checks if the charging process is complete. 28 | * 29 | * @return true if the charging process is complete, false otherwise. 30 | */ 31 | bool chargingComplete(); 32 | /** 33 | * Sets the operation mode of the Fuel Gauge. 34 | * 35 | * @param mode The operation mode to set. Possible values are: hibernate, shutdown, active. 36 | * @return True if the operation mode was set successfully, false otherwise. 37 | */ 38 | bool setOperationMode(FuelGaugeOperationMode mode); 39 | 40 | /** 41 | * @brief Constructs a new MAX1726Driver object. 42 | * 43 | * @param wire Pointer to the TwoWire object for I2C communication. 44 | * @param i2cAddress The I2C address of the MAX1726 device. The default value is 0x36. 45 | */ 46 | MAX1726Driver(TwoWire *wire, uint8_t i2cAddress); 47 | ~MAX1726Driver(); 48 | }; 49 | 50 | MAX1726Driver::MAX1726Driver(TwoWire *wire, uint8_t i2cAddress = DEFAULT_I2C_ADDRESS) { 51 | this->wire = wire; 52 | this->i2cAddress = i2cAddress; 53 | } 54 | 55 | MAX1726Driver::~MAX1726Driver(){} 56 | 57 | void MAX1726Driver::setHibernateModeEnabled(bool enabled){ 58 | if(enabled){ 59 | // Enters hibernate mode somewhere between 2.812s and 5.625s if the threshold conditions are met 60 | replaceRegisterBit(this->wire, i2cAddress, HIB_CFG_REG, EN_HIBERNATION_BIT, 1); // Enter Hibernate Mode 61 | } else { 62 | replaceRegisterBit(this->wire, i2cAddress, HIB_CFG_REG, EN_HIBERNATION_BIT, 0); // Exit Hibernate Mode 63 | } 64 | } 65 | 66 | bool MAX1726Driver::setOperationMode(FuelGaugeOperationMode mode) { 67 | if(mode == FuelGaugeOperationMode::active){ 68 | // See section "Soft-Wakeup" in user manual https://www.analog.com/media/en/technical-documentation/user-guides/max1726x-modelgauge-m5-ez-user-guide.pdf 69 | replaceRegisterBit(this->wire, i2cAddress, HIB_CFG_REG, EN_HIBERNATION_BIT, 0); // Exit Hibernate Mode 70 | writeRegister16Bits(this->wire, i2cAddress, SOFT_WAKEUP_REG, 0x90); // Wakes up the fuel gauge from hibernate mode to reduce the response time of the IC to configuration changes 71 | writeRegister16Bits(this->wire, i2cAddress, SOFT_WAKEUP_REG, 0x0); // This command must be manually cleared (0x0000) afterward to keep proper fuel gauge timing 72 | } else if(mode == FuelGaugeOperationMode::hibernate){ 73 | this->setHibernateModeEnabled(true); 74 | } else if(mode == FuelGaugeOperationMode::shutdown){ 75 | this->setHibernateModeEnabled(false); // Enter active mode 76 | // The default (minimum) shutdown timeout is 45s 77 | replaceRegisterBit(this->wire, i2cAddress, CONFIG_REG, SHDN_BIT, 1); // Command shutdown mode 78 | } 79 | 80 | return false; 81 | } 82 | 83 | /** 84 | * @brief Checks if the battery charging is complete. 85 | * 86 | * @return true if the charging is complete, false otherwise. 87 | */ 88 | bool MAX1726Driver::chargingComplete(){ 89 | // TODO This needs to be tested, probably it's a value that only temporarily indicates the end-of-charge condition. 90 | // There is also a FULL_DET_BIT in the STATUS2 register but the datasheet does not explain it: 91 | // return getBit(this->wire, i2cAddress, STATUS2_REG, FULL_DET_BIT) == 1; 92 | return getBit(this->wire, i2cAddress, F_STAT_REG, FQ_BIT) == 1; 93 | } -------------------------------------------------------------------------------- /src/WireUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef WIREUTILS_H 2 | #define WIREUTILS_H 3 | 4 | #include "Arduino.h" 5 | #include "Wire.h" 6 | 7 | /** 8 | * @brief Extracts a range of bits from an integer value. 9 | * 10 | * This function extracts a range of bits from the given integer value, starting from the startBit 11 | * and ending at the endBit (inclusive). 12 | * 13 | * @param value The integer value from which to extract the bits. 14 | * @param startBit The starting bit position. 15 | * @param endBit The ending bit position. 16 | * @return The extracted bits as an integer value. 17 | */ 18 | static inline int extractBits(int value, int startBit, int endBit) { 19 | if (startBit < 0 || startBit > 31 || endBit < 0 || endBit > 31 || startBit > endBit) { 20 | // Handle invalid bit range 21 | return -1; 22 | } 23 | 24 | int mask = (1 << (endBit - startBit + 1)) - 1; 25 | return (value >> startBit) & mask; 26 | } 27 | 28 | /** 29 | * Writes a 16-bit data value to a register using the specified I2C wire object. 30 | * 31 | * @param wire The I2C wire object to use for communication. 32 | * @param address The I2C address of the device. 33 | * @param reg The register address to write to. 34 | * @param data The 16-bit data value to write. 35 | * @return The status of the write operation: 36 | * 0: success. 37 | * 1: data too long to fit in transmit buffer. 38 | * 2: received NACK on transmit of address. 39 | * 3: received NACK on transmit of data. 40 | * 4: other error. 41 | * 5: timeout 42 | */ 43 | static inline uint8_t writeRegister16Bits(TwoWire *wire, uint8_t address, uint8_t reg, uint16_t data) 44 | { 45 | uint8_t msb, lsb; 46 | msb = (data & 0xFF00) >> 8; 47 | lsb = (data & 0x00FF); 48 | wire->beginTransmission(address); 49 | wire->write(reg); 50 | /** 51 | * See section "Data Order" in https://www.analog.com/media/en/technical-documentation/user-guides/max1726x-modelgauge-m5-ez-user-guide.pdf 52 | * With I2C communication, a byte of data consists of 8 bits ordered most significant bit (MSb) first. 53 | * The least significant bit (LSb) of each byte is followed by the Acknowledge bit. IC registers 54 | * comprising multibyte values are ordered least significant byte (LSB) first. 55 | */ 56 | wire->write(lsb); 57 | wire->write(msb); 58 | return (wire->endTransmission()); 59 | } 60 | 61 | /** 62 | * @brief Reads a 16-bit register from a specified address using the given I2C wire object. 63 | * The data order is LSB(yte) first. 64 | * 65 | * @param wire The I2C wire object to use for communication. 66 | * @param address The address of the device to read from. 67 | * @param reg The register to read. 68 | * @return The value read from the register as a 16-bit integer. 69 | */ 70 | static inline uint16_t readRegister16Bits(TwoWire *wire, uint8_t address, uint8_t reg) 71 | { 72 | wire->beginTransmission(address); 73 | wire->write(reg); 74 | wire->endTransmission(false); 75 | wire->requestFrom(address, 2, true); 76 | uint16_t registerValue = (uint16_t)wire->read(); // Read LSB 77 | registerValue |= (uint16_t)wire->read() << 8; // Read MSB 78 | return registerValue; 79 | } 80 | 81 | /** 82 | * Gets the value of a specific bit in a register. 83 | * @param wire The I2C object used for communication. 84 | * @param address The address of the device. 85 | * @param reg The register to check. 86 | * @param index The bit index within the register value. 87 | * @return The value of the bit at the specified index. 88 | */ 89 | static inline uint8_t getBit(TwoWire *wire, uint8_t address, uint8_t reg, uint8_t index) { 90 | uint16_t regValue = readRegister16Bits(wire, address, reg); 91 | return (regValue >> index) & 0x01; 92 | } 93 | 94 | /** 95 | * Replaces specific bits in a register value of a device connected to the I2C bus. 96 | * 97 | * @param wire The I2C object representing the I2C bus. 98 | * @param address The address of the device on the I2C bus. 99 | * @param reg The register to modify. 100 | * @param indexFrom The index of the first bit to replace starting from LSB (0) 101 | * @param indexTo The index of the last bit (included) to replace starting from LSB (0) 102 | * @param data The new data (bits) to write to the register. 103 | */ 104 | static inline void replaceRegisterBits(TwoWire *wire, uint8_t address, uint8_t reg, uint16_t indexFrom, uint8_t indexTo, uint16_t data) { 105 | uint16_t registerValue = readRegister16Bits(wire, address, reg); 106 | 107 | // Create a mask to clear the bits to be replaced 108 | uint16_t mask = 0; 109 | for (int i = indexFrom; i <= indexTo; i++) { 110 | mask |= (1 << i); 111 | } 112 | registerValue &= ~mask; // Clear the bits to be replaced 113 | registerValue |= (data << indexFrom); // Set the new bits 114 | writeRegister16Bits(wire, address, reg, registerValue); 115 | } 116 | 117 | 118 | /** 119 | * @brief Replaces a specific bit in a register of a given I2C device. 120 | * 121 | * This function replaces a specific bit in a register of a given I2C device. 122 | * 123 | * @param wire The TwoWire object representing the I2C bus. 124 | * @param address The address of the I2C device. 125 | * @param reg The register to modify. 126 | * @param index The index of the bit to replace. 127 | * @param data The new data (1 bit) to write to the register. 128 | */ 129 | static inline void replaceRegisterBit(TwoWire *wire, uint8_t address, uint8_t reg, uint16_t index, uint16_t data) { 130 | replaceRegisterBits(wire, address, reg, index, index, data); 131 | } 132 | 133 | #endif --------------------------------------------------------------------------------