Skip to content

Power: output setters return bool, add getPowerOutputCaps() - #374

Merged
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:power_output_result
Sep 20, 2026
Merged

lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:power_output_result

Conversation

@ainyan03

@ainyan03 ainyan03 commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Power_Class::setExtOutput, setUsbOutput, setExtPortBusConfig and setVibration returned void and discarded the result of the I2C / IO expander / PMIC operations behind them, so a caller could not tell whether the external 5 V, the USB output or the vibration motor were actually switched. This PR makes the four setters report whether the requested state was reached and adds getPowerOutputCaps() to tell "this model has no such path" apart from "the request failed", in the same shape as setChargeCurrent / getChargeControlCaps().

API

API Before After
setExtOutput(enable, port_mask) void bool – true when every selected port reached the requested state
setUsbOutput(enable) void bool
setExtPortBusConfig(config) void bool
setVibration(level) void bool
getPowerOutputCaps() – uint8_t bitmask of power_output_capability_t (cap_set_ext_output / cap_set_usb_output / cap_set_ext_port_bus / cap_set_vibration); 0 before M5.begin() completes and on the PC build
AXP192_Class::setEXTEN / setGPIO* / setLDO*, AXP2101_Class::set*LDO* void bool
AXP192_Class::getBatteryLevel(int8_t*), INA3221_Class::getShuntMilliVoltage(ch, int32_t*), M5PM1_Class::getGPIOOutputLatch(pin, bool*) – checked variants (I2C result separated from the value)

Behaviour

  • false covers: a model without the path (see getPowerOutputCaps()), an I2C failure, a protection check that cancelled the request (Core2 / Tough on a low battery, the CoreS3 family without a battery), the AW9523 mutex not being available or the request being superseded on CoreS3, an unconfirmed PMIC identity (same rule as the charge setters), and a port_mask that selects none of the model's ports (ext_none on every model).
  • Where port_mask selects several ports (M5Station, PowerHub, Tab5 family, CoreP4X, ChainCaptain) every selected port is still attempted; false means at least one did not take effect. Models with a single output ignore the mask.
  • Core2 / Tough: the readings used by the protection check are now checked reads. A reading that cannot be obtained refuses the enable request instead of passing the check with a folded-to-zero value.
  • Disable requests stay best effort: the AXP192 / AXP2101 rail setters and the Core2 / Tough EXTEN write attempt every step after an earlier failure, so a failed voltage write no longer skips the enable bit.
  • setVibration: a non-zero level is clamped up to the lowest voltage the rail can produce (AXP192 LDO3 1800 mV, AXP2101 DLDO1 500 mV). Levels below it used to switch the rail off while the call looked successful.
  • Core2 v1.1 / Tough with AXP2101: when the INA3221 did not initialise, the current-direction check is omitted as before; a detected INA3221 that fails to read refuses the enable request (logged).
  • ChainCaptain: the boost output follows the port latches only when a port was selected; when the latch cannot be read the boost is left untouched and the call returns false (it used to switch the boost off).
  • AXP192 / AXP2101 rail setters: a disable request reports only whether the enable bit was cleared; enable reports both the voltage and the enable write.
  • CoreS3 family: the AW9523 output helper reports each write; a request that could not take the mutex or was superseded by a later request returns false (it did not take effect).

Breaking changes

  • Return types changed (void → bool). Calls that ignore the result still compile. A return type is not part of the mangled name: rebuild everything that links against the library.
  • setExtOutput(enable, ext_none) returns false and does nothing on every model (it used to switch the single output on models that ignore the mask).
  • setVibration(1..109) on a Core2 with AXP192 (and setVibration(1) with AXP2101) now drives the motor at the rail minimum instead of switching it off.
  • The deprecated setExtPower wrapper stays void.

Not changed (separate issue)

  • CoreS3 family: setExtOutput(false) clears BOOST_EN without looking at USB_EN, which stops an enabled USB output. This condition has been in place since the first CoreS3 implementation and is out of scope here.

Verification

Build: ESP32 / ESP32-S3 (×2) / C6 / C5 / P4 Arduino, ESP32 ESP-IDF 6.0, native (PC build).

Board Path Checked
Core BASIC no PMIC caps = 0; every setter false
Tough AXP192 caps = ext; on/off with read-back, ext_none false, usb / vib / bus false
ToughC5 M5PM1 caps = ext; on/off with read-back, ext_none false
PowerHub STM32 front-end caps = ext + bus; on/off per port (USB / PA) with read-back, non-existent port false, bus config accepted
Core2 v1.3 AXP2101 + INA3221 caps = ext + vib; ext on/off with read-back, vib 1 / 0 true
Core2 v1.1 AXP2101 + INA3221 caps = ext + vib; ext on with read-back (ext off drops the USB console on this unit, same with develop)
CoreS3 (DIN base) AXP2101 + AW9523 caps = ext + usb; ext on/off with read-back (twice), usb off, ext_none false; the no-battery protection cancel reports false

setExtOutput / setUsbOutput / setExtPortBusConfig / setVibration report
whether the requested state was reached instead of discarding the I2C,
IO expander and PMIC results. getPowerOutputCaps() tells a model without
the path apart from a failed request, in the same shape as the charge
setters.

- false: model without the path, I2C failure, protection cancel (Core2 /
  Tough low battery, CoreS3 family without a battery), AW9523 lock not
  available or request superseded (CoreS3), unconfirmed PMIC identity,
  or a port_mask selecting none of the ports.
- Multi-port models attempt every selected port and AND the results.
- Core2 / Tough: the protection readings are checked reads; a reading
  that fails refuses the enable request. Units whose INA3221 did not
  respond keep the previous behaviour (current check skipped).
- AXP192 / AXP2101 rail setters: every step is attempted so that a
  disable still reaches the enable bit; disable reports only the enable
  bit, enable reports both writes.
- setVibration: a nonzero level is clamped to the rail minimum instead
  of silently switching the rail off.
- Checked getters added: AXP192 getBatteryLevel(int8_t*), INA3221
  getShuntMilliVoltage(ch, int32_t*), M5PM1 getGPIOOutputLatch(pin, bool*).
- build_test exercises the new API.

Breaking: return types void -> bool (rebuild dependants); ext_none is
now a no-op returning false on every model; setVibration(1..109) on
AXP192 drives the motor at the rail minimum instead of stopping it.
@lovyan03
lovyan03 merged commit 9c3e539 into m5stack:develop Sep 20, 2026
28 checks passed
@ainyan03
ainyan03 deleted the power_output_result branch September 20, 2026 03:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants