You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Before you update
Update M5GFX to 0.3.0 together with M5Unified 0.3.0, and rebuild everything (several functions changed from void to bool).
Absolute setRotation(n) calls move by one step on many boards (see the first Breaking change).
BOARD_ID, cfg.fallback_board and boards that are not M5Stack products: check the board identification change.
Tab5 / Tab5X: setChargeCurrent(0) no longer stops charging; use setBatteryCharge(false).
Breaking changes
Display changes come with M5GFX 0.3.0 and apply to M5.Display as well. Rotation 0 is now the upright orientation on every board. On Core BASIC / Fire / Gray, Core2, Tough, ToughC5, the CoreS3 family, Station, Cardputer / Cardputer-Adv, StamPLC and CoreMatrix the start-up value used to be 1, so every absolute setRotation(n) now shows what setRotation((n + 3) % 4) showed before: M5.Display.setRotation(1); (the usual landscape call on these boards) becomes M5.Display.setRotation(0);, or simply drop the call because 0 is the start-up value. A relative call such as setRotation(getRotation() ^ 1) still toggles between landscape and portrait, but the portrait view is turned the other way (180°) compared with 0.2.x. DinMeter now starts in landscape. See the M5GFX 0.3.0 release notes for the per-board table.
PSRAM sprites are pushed asynchronously through DMA on ESP32-S3 / C5 / C61 / P4 (M5GFX 0.3.0): before reusing or freeing a sprite after pushSprite() inside startWrite(), wait on the destination with M5.Display.waitDMA() (not the sprite's waitDMA()).
Panel_ED2208::display() (PaperColor, M5GFX 0.3.0) no longer blocks: call M5.Display.waitDisplay() before sleeping or cutting the power: M5.Display.display(); M5.Display.waitDisplay();.
Rtc.setTimerIRQ(msec) returns bool instead of the programmed period; the period comes back through a new optional out-pointer: bool setTimerIRQ(uint32_t msec, uint32_t* applied_msec = nullptr). uint32_t n = M5.Rtc.setTimerIRQ(ms); still compiles but now stores 0 / 1. Rewrite it as uint32_t n; if (M5.Rtc.setTimerIRQ(ms, &n)) { ... }. (#368)
Rtc.setAlarmIRQ(date, time) returns bool (the alarm is in the requested state; a request without any alarm field clears the alarm and returns true) instead of an int that meant "alarm enabled" (0 for a clear, -1 without an RTC). Code that read that int must be revisited: bool ok = M5.Rtc.setAlarmIRQ(&date, &time);. (#368)
Rtc.setAlarmIRQ(int seconds) is removed: it programmed the periodic timer, not the alarm. Use M5.Rtc.setTimerIRQ(seconds * 1000). The overload is declared = delete, so setAlarmIRQ(0) fails to compile instead of silently binding to the tm* overload. (#368)
Rtc.setDateTime / setDate / setTime, clearIRQ and disableIRQ return bool instead of void (every register write acknowledged; an out-of-range field or a year the chip cannot store returns false instead of wrapping; weekDay must be 0-6 or -1 to compute it, so a value such as 7 that used to be recomputed is now rejected). The value-returning getTime() / getDate() / getDateTime() return every field as -1 on failure (the year used to read 2000): check with auto dt = M5.Rtc.getDateTime(); if (dt.isValid()) { ... }. Classes deriving from RTC_Base must update their overrides. (#368)
Power.timerSleep() returns bool and is fail-closed: the device powers off only when a wake-up alarm was actually armed, and the call returns false when it stays awake (invalid arguments, an RTC without the needed feature, the PC build). On PowerHub the RTC runs in the STM32 firmware, so true from the alarm setters means the request was accepted (applied asynchronously), and timerSleep() can return false after its power-off timeout and still power off later with the alarm armed. timerSleep(seconds) on PowerHub (no RTC timer) returns false; use M5.Power.timerSleep(time) or timerSleep(date, time). setTimerIRQ(1..499 ms) on the PCF8563 now arms the 1 s minimum step instead of stopping the timer. RX8130: requests of 250 ms or more may round to a coarser clock instead of 4096 Hz, so that the IRQ passes the M5PM1 relay (260 ms becomes 266 ms); read the actual period from applied_msec. (#368)
Power.isCharging() returns bool (true only while the battery is being charged) instead of is_charging_t; an unknown state now reads as false like every state that is not charging. Code that needs is_charging_t::charge_unknown should ask getChargeState() instead: auto s = M5.Power.getChargeState(); if (!m5::charge_states_known.contains(s)) { /* unknown */ } else if (m5::charge_states_any_charging.contains(s)) { /* charging */ } (comparing against a single state is model dependent), and canReport(state) to learn whether a state can ever be observed on this board. (#353)
Power.setBatteryCharge / setChargeCurrent / setChargeVoltage return bool (request applied) instead of void; setChargeCurrent(mA, &applied) / setChargeVoltage(mV, &applied) report the step actually selected (the largest step not above the request; a request below the lowest step is clamped up to it). Tab5 / Tab5X: setChargeCurrent(0) applies the lowest step (500 mA) instead of stopping the charge; use M5.Power.setBatteryCharge(false) to stop. setChargeCurrent() no longer enables charging on Tab5 / Tab5X (500 / 1000 mA used to turn CHG_EN on as well); call setBatteryCharge(true) to resume. StampS3Bat: begin() releases CHG_PROG, so the charge current after begin() is 200 mA; setChargeCurrent(650) selects 650 mA. (#353)
Power.setExtOutput / setUsbOutput / setExtPortBusConfig / setVibration return bool instead of void: true when the requested state was reached, false when this model has no such path, an I2C write failed, or a protection check cancelled the request (Core2 / Tough on a low battery, the CoreS3 family without a battery). setExtOutput(enable, ext_none) does nothing and returns false on every model; where a port_mask selects several ports (Station, PowerHub, Tab5 family, CoreP4X, ChainCaptain) false means at least one port did not take effect. setVibration(1..109) on a Core2 with AXP192 drives the motor at the rail minimum instead of switching it off. Calls that ignore the result compile unchanged: if (!M5.Power.setExtOutput(true)) { /* not on: see getPowerOutputCaps() */ }. The AXP192 setEXTEN / setGPIO* / setLDO* and AXP2101 set*LDO* helpers return bool as well. (#374)
A return type is not part of the mangled name, so the void → bool changes above are not detected by the linker: rebuild everything that links against the library. Mixed linking with binaries built against 0.2.x is unsafe. (#353, #368, #374)
The bitfield cfg.external_display.unit_poep4_hdmi is renamed unit_poep4_display_out (same bit 9 of external_display_value); code that writes the old name fails to compile. Use cfg.external_display.unit_poep4_display_out = true;. The settings structure cfg.unit_poep4_hdmi stays as a deprecated alias of cfg.unit_poep4_display_out. (#397, #403)
Board identification is now done by the M5GFX board detector (requires M5GFX 0.3.0). Check your setup if you build with BOARD_ID, set cfg.fallback_board, or use a board that is not an M5Stack product: (#396, #397, #398, #399)
BOARD_ID: a positive value now fixes the board (fixed_board in M5GFX: no probing of other boards, no NVS; a different cfg.fallback_board is ignored with a warning), so a build that used BOARD_ID as a hint or default no longer detects other boards. -DBOARD_ID without a value means 1 (M5Stack).
Order without BOARD_ID: the detector's result, cfg.fallback_board (now board_unknown by default on every target, passed to M5GFX before Display.init()), the detector's candidate, then a package default.
Boards that detection cannot confirm are no longer assumed. A generic ESP32 (D0WDQ6 dev kit) is no longer treated as TimerCam, so G33 is not driven as a power hold (a TimerCam whose RTC does not answer powers off on battery; use the TimerCam board definition, cfg.fallback_board = m5::board_t::board_M5TimerCam or BOARD_ID). A PICO-D4 board without Atom evidence is StampPico instead of AtomLite (the LED stays on G27; the internal I2C pins change from G21 / G25 to G22 / G21 and the external ones from G32 / G26 to G33 / G32); an Atom Lite / AtomS3 Lite itself is still detected. An ESP32-C6 / H2 / C5 with no evidence ends at board_unknown (NanoC6 / NanoH2 / StampC5 used to be assumed).
Package defaults: ESP32-S3 = StampS3 (QFN56) / StampS3Mini (LGA56) (was AtomS3Lite); ESP32-P4 = StampP4 / StampP4X by chip revision (was Tab5). An unknown board gets no board-specific power hold, LEDs or buttons; getDisplayCount() is 0 for StampC6 / StampC5.
New API
Power.getChargeState() returns a charge_state_t: charging / not_charging / full / disabled / discharging / idle, or the non-positive not_initialized / io_error / undetermined / unsupported. Each charger reports only what it can observe (AXP192: no full; AXP2101: full state machine, current direction and battery presence; IP5306: full from the charge-complete flag, so a full battery on USB no longer reads as discharging; M5PM1 boards: CHG_STAT; Tab5: INA226 current). An I2C failure is io_error, never a positive state. getChargeStateCaps() returns the charge_state_set_t this board can report and canReport(state) tests one state; getBatteryPresence() reports present / absent where the charger knows it (AXP2101) or where it is estimated (ToughC5, CoreMatrix); getChargeControlCaps() tells which of charge enable / current / voltage this board can set. When the PMIC does not answer its ID probe at begin(), the charge setters refuse it, getChargeStateCaps() is empty and getChargeControlCaps() is 0. (#353, #403, #422)
Power.getPowerOutputCaps(): bitmask of cap_set_ext_output / cap_set_usb_output / cap_set_ext_port_bus / cap_set_vibration for the output paths this model has; 0 before M5.begin() completes, when the PMIC does not answer its ID probe (the setters refuse it), and on the PC build. Tells "no such path" apart from "the request failed". (#374, #422)
RTC: rtc_time_t / rtc_date_t / rtc_datetime_t::isValid() (all fields set and in range); Rtc.hasTimerIRQ() / canSetAlarm(date, time) capability queries that write nothing. (#368)
cfg.unit_poep4_display_out and cfg.external_display.unit_poep4_display_out replace the unit_poep4_hdmi names for the Unit PoE-P4 display output (the settings structure keeps the old name as a deprecated alias; a changed legacy setting is used with a warning). The examples use the new names. (#397)
Checked read variants whose I2C result is separated from the value: AXP192_Class::getBatteryLevel(int8_t*), INA3221_Class::getShuntMilliVoltage(ch, int32_t*), M5PM1_Class::getGPIOOutputLatch(pin, bool*). (#374)
M5.Power.M5pm1.getWakeSource(): the reason for this power-on on boards with the M5PM1, read before M5.begin() clears the register on ToughC5 and PaperMono. The bits match m5pm1_wake_src_t of the M5PM1 library. (#432)
Audio
PC (SDL) build: M5.Mic.begin() / end() / record() were declared but not defined, so a sketch calling them failed to link. They now capture from the default input device; oversampling, gain, noise filter, stereo handling and the request queue are the same code as on the device. begin() returns false when no input device can be opened. (#406)
PC (SDL) build: the speaker initialized SDL audio inside its playback thread, so Speaker.begin() followed by Mic.begin() could initialize it twice at once and close the microphone (7 of 20 runs). Both go through one serialized initialization in begin(). (#406)
Board fixes
PaperMono: getChargeState() / isCharging() / setBatteryCharge() could stall the board (frozen screen, USB port gone, only the power key recovers it) when USB had been plugged in while the board was off: the IP2315 charger then latches its LED mode and drives the I2C lines at about 500 Hz. The lines are now watched before any I2C traffic; in LED mode getChargeState() returns undetermined and setBatteryCharge() returns false until VIN is removed. (#394)
PowerHub: an accepted power-off request cannot be withdrawn, so a repeated timerSleep() no longer clears the alarm the delayed power-off needs, and the output / measurement registers are restored when the device keeps running, so battery and charge readings keep working. (#403)
RTC relayed by the M5PM1 (PaperMono, ToughC5): a stale relay wake source / IRQ status is cleared between stopping the old IRQ source and arming the new one, and a relay failure is part of the result. Timer operations preserve a pending alarm flag and vice versa (PCF8563, RX8130). (#368)
PowerHub RTC: rewritten against the STM32 firmware register map. begin() probes the firmware, getVoltLow() no longer reads an unrelated register, weekday values are validated on read. (#368)
Unit PoE-P4 / Tab5 / Stamp-P4: the P4 boards are told apart by the M5GFX detector (Unit PoE-P4 by its Ethernet PHY ID) instead of by GPIO levels that user wiring can change. A positive BOARD_ID fixes the board in M5GFX; in 0.2.26, 31 (CoreP4X) / 35 (Tab5X) were only the ESP32-P4 default and 147 (DualKey) a probe hint. (#396, #397, #399)
StampS3 boards: with a fixed BOARD_ID, the early GPIO46 power hold is applied only when that board uses GPIO46 for power hold. (#399)
M5PM1: the first read in begin() is retried. It could be NACKed at start-up, which left the driver uninitialized until a later begin(). (#432)
Examples
Advanced/Mic_FFT: drawing follows the captured audio instead of the display speed (the history raced on fast displays and on the PC build), and the example builds on the PC (SDL) build. (#407)
Internal
Debug builds (CORE_DEBUG_LEVEL >= 4 or CONFIG_LOG_DEFAULT_LEVEL >= 4) log an error when getChargeState() returns a state that getChargeStateCaps() does not list; the returned state is unchanged. (#422)
CI: the ESP-IDF / arduino-esp32 versions are split with M5GFX so that together they cover every ESP-IDF minor from 5.1 to 6.1 and every arduino-esp32 series from 2.0 to 3.3; push builds also run when only .inl files change. (#379, #384)
Removed the empty _check_boardtype(); renamed a parameter that shadowed the file-scope i2c_freq. (#383, #398)
The library and the HowToUse example use the product name board_M5StamPLC instead of the old spelling, which M5GFX now marks deprecated. (#408)
Follows the M5GFX getter name getCandidateBoard(); comments added since 0.2.26 are in English. (#409)
Unused private I2C probe helpers removed (left over from moving display-less board detection to M5GFX). (#410)
Power: the charge and output capabilities live in one table per kind, which both the capability getters and the setters use. Results are unchanged for every board begin() initializes. (#423)
Comments describe the current behavior only (descriptions of earlier implementations, a date and an example fork owner removed). Comments only. (#426)
Japanese comments added in recent changes are in English, and the StickS3 speaker comment no longer copies the CoreS3 condition. Comments only. (#427)
Header comments: the alarm description is attached to the callable setAlarmIRQ() overloads instead of the deleted one, and the charge setters say that false means the request could not be confirmed (the setting may or may not have been applied). Comments only. (#430)