Implement getAPSVoltage and return NaN from the unavailable AXP2101 getters - #292
Merged
Merged
Conversation
…etters (m5stack#86 m5stack#215) Seven getters of AXP2101_Class returned 0 without reading anything, which is indistinguishable from a valid measurement of zero. That is what both issues report. The ADC of the AXP2101 covers VBAT ( 0x34 ), TS ( 0x36 ), VBUS ( 0x38 ), VSYS ( 0x3A ) and TDIE ( 0x3C ). - getAPSVoltage() now reads VSYS, which is the equivalent measurement point on the AXP2101 of the APS rail of the AXP192. Measured 3.776 V on a CoreS3 while the AXP2101 was supplying the system. - the six getters that fall outside the set above return NaN, so that a caller can tell an unavailable reading from a real zero with std::isnan(). Nothing inside the library calls them, so no other behaviour changes. - document which readings each getter provides, and note in Power_Class::getBatteryCurrent() that the value comes from an AXP192 or from a dedicated current sense IC ( INA3221 on Core2 v1.1, INA226 on M5Tab5 ), and that boards without either of them return 0.
There was a problem hiding this comment.
Pull request overview
This PR addresses ambiguous “0” readings from several AXP2101_Class getters by implementing the one voltage channel the AXP2101 ADC actually provides for the “APS” rail equivalent (VSYS), and by returning NaN for getters that the AXP2101 cannot measure so callers can distinguish “unavailable” from a real zero.
Changes:
- Implement
AXP2101_Class::getAPSVoltage()by reading VSYS (0x3A/0x3B) and returning volts. - Change six unsupported
AXP2101_Classgetters to returnNaNinstead of a hard-coded0. - Add/expand documentation clarifying which measurements are available on AXP2101 and where battery current readings come from in
Power_Class.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/utility/power/AXP2101_Class.hpp | Documents AXP2101 ADC-supported channels and clarifies which getters return NaN as “not provided”. |
| src/utility/power/AXP2101_Class.cpp | Implements getAPSVoltage() via VSYS ADC and updates unsupported getters to return NaN via a helper. |
| src/utility/Power_Class.hpp | Documents that battery current comes from board hardware (AXP192 or dedicated sense IC), otherwise returns 0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+287
to
+289
| float volt = readRegister14(0x3A); | ||
| if (volt >= 16375) { return 0.0f; } | ||
|
|
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The literal 16375 appeared at every place that checks whether a 14 bit ADC channel returned a reading, and getAPSVoltage() added a third one. Give it a name so that the intent is visible and the value lives in one place. No behaviour change.
The threshold was full scale minus 8, which is narrower than the value a channel actually settles at. Measured on a CoreS3, VBUS with nothing connected reads 16372, or full scale minus 11, so it slipped through the check and would have been reported as a 16.372 V reading. Keep the way the threshold is defined and widen the margin to 32. That is three times the distance measured above, while a channel that does have an input stays far away from it: the closest on the same board was TS at 5563, and every other channel read below 5100. This does not change what any of the getters return today. getVBUSVoltage() is already guarded by isVBUS(), and TS and VSYS always have an input on the boards in question, so no path reached the threshold with a railed value.
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Relates to #86 and #215.
Seven getters of
AXP2101_Classreturned0without reading anything:A returned
0is indistinguishable from a valid measurement of zero, which is what led to both reports.What the ADC provides
The ADC of the AXP2101 covers these measurement points:
0x340x360x380x3A0x3COf the seven getters, one falls inside this set and six do not.
Changes
getAPSVoltage()is implemented. VSYS is the equivalent measurement point on the AXP2101 of the APS rail of the AXP192, and it was the one channel not yet used. Measured 3.776 V on a CoreS3 while the AXP2101 was supplying the system, and 4.246 V with the charger connected.The other six return NaN, so a caller can tell an unavailable reading from a real zero with
std::isnan(). Nothing inside the library calls them, so no other behaviour changes.Documentation is added for what each getter provides.
Power_Class::getBatteryCurrent()also gains a note that the value comes from an AXP192 or from a dedicated current sense IC — the INA3221 on Core2 v1.1, the INA226 on M5Tab5 — and that boards carrying neither return 0.How the set of channels was checked
Rather than relying on the register map alone, the whole 256 byte register space was dumped on a CoreS3 with a battery attached, once while charging and once while running from the battery, and the two dumps were compared. Registers that also moved between two consecutive dumps in the same state were excluded as normal fluctuation.
Ten registers differed:
0x000x010x350x380x390x3A0x3B0x490xA40xA9Everything that responds to the charge state is a voltage, a status bit or the fuel gauge percentage, which matches the channel list above.
Verification
Measured on a CoreS3 with a battery attached, Arduino 2.0.17 (ESP-IDF 4.4):
Build checked for ESP32-S3 (ESP-IDF 4.4 / 5.5), ESP32 and the PC build.
Follow-up from the review
The literal
16375, which marks an ADC channel as having nothing to read, is now named. It appeared at every such check andgetAPSVoltage()added a third one, so all three sites were changed together rather than only the one that was pointed out.While naming it, the value turned out to be narrower than where a channel actually settles. It was full scale minus 8, but VBUS with nothing connected reads 16372 on a CoreS3, which is full scale minus 11 and slips through. The way the threshold is defined is kept and the margin is widened to 32:
That is three times the distance measured above, and a channel that does have an input stays far away from it — the closest on the same board was TS at 5563, and every other channel read below 5100.
Measured on a CoreS3 with a battery attached:
This does not change what any getter returns today:
getVBUSVoltage()is already guarded byisVBUS(), and TS and VSYS always have an input on the boards in question, so no path reached the threshold with a railed value.