Add support for M5Stack CoreMatrix (ESP32-C61) - #299
Merged
Conversation
- I2C pin table: the internal bus is SDA=G0 / SCL=G1. The Grove port shares the same bus through a level shifter, so Port A uses the same pins. - SD: SPI SCLK=G25 / MOSI=G27 / MISO=G26 / CS=G28. - M-Bus 30 pin table, taken from the V0.7 schematic and cross-checked against the factory firmware continuity test order. - BtnA/B/C: KEY1/2/3 are wired to PM1 GPIO0/1/2 (pressed = LOW), not to the ESP, so they are polled over I2C. The whole GPIO input register is read in one transaction (new M5PM1 getGPIOInputBits), and the button state is only updated when the read succeeds, so a bus error is not reported as a button press. BtnPWR maps to the PM1 power button. - PM1 GPIO3 is the IRQ output wired to ESP32 G2; configure it as a push-pull high output before switching it to the IRQ function, the same way as ToughC5. PM1 GPIO4 is the BMI270 INT1 input. - The M5IOE1 is exposed through getIOExpander(0).
- Battery voltage and level are read from the PM1. - Charge state: the AW32901 CHG_STAT output is wired to M5IOE1 G8 (low = charging). A new error-aware IOExpander_Base::getInputLevel (overridden by M5IOE1_Class with an I2C result) is used so a bus failure reports charge_unknown instead of "charging"; the base implementation returns false to mean "not supported". - The TF card power gate (M5IOE1 G1) is off at reset; enable it in Power.begin so the SD card is usable after M5.begin. - setExtOutput/getExtOutput drive the Grove port power gate (M5IOE1 G5, both the 3.3V rail and the 5V boost). Verified on hardware: battery readings, charge state, Grove power readback, and SD card mount after power-up.
The BMI270 is mounted differently on CoreMatrix; remap the accelerometer and gyro axes as X=-Y, Y=-X, Z=-Z (the same form of correction as ChainCaptain). Determined with an interactive tilt calibration on hardware.
KEY presses and IMU wake events are funneled into the PM1, whose IRQ output (wired to ESP32 G2) is the only wakeup pin; arm it as an EXT1 ANY_LOW source. The IRQ line has no external pull-up, so keep the RTC-domain pull-up enabled while sleeping, the same way as ToughC5. On wakeup, clear WAKE_SRC before the IRQ status registers: while WAKE_SRC is set, the WAKEUP bit of IRQ status 3 keeps getting re-asserted and the IRQ output never releases. On the ESP32-C61, the wakeup pin is still owned by the RTC IO mux after an EXT1 wakeup and the digital GPIO input reads low forever, which made the release wait on the next sleep entry spin without ever sleeping. Call rtc_gpio_deinit in Power.begin to return the pad to the digital function. (Verified on a ToughC5 that the C5 does not exhibit this, so this is handled in the CoreMatrix path.) Verified on hardware: repeated timer wake and EXT1 wake cycles with re-entry into deep sleep.
There was a problem hiding this comment.
Pull request overview
Adds board-level support for the upcoming M5Stack CoreMatrix (ESP32-C61) across M5Unified’s pin mapping, power management, button handling, and IMU axis normalization so the board can be used with existing M5Unified APIs.
Changes:
- Add CoreMatrix pin tables (internal/Port A I2C, SD SPI, and M-Bus map) and CoreMatrix-specific PM1/IOE1 initialization.
- Add error-aware GPIO/input reads (PM1 GPIO input bit read; IO expander
getInputLevel) and use them for CoreMatrix button polling and charge-state detection. - Add CoreMatrix-specific power behavior (Grove power gate, TF power enable, battery presence probe, EXT1 wake handling) and IMU axis remap for BMI270.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utility/power/M5PM1_Class.hpp | Adds PM1 APIs for bulk GPIO input read and charger enable state query. |
| src/utility/power/M5PM1_Class.cpp | Implements the new PM1 APIs used by CoreMatrix integration. |
| src/utility/Power_Class.hpp | Adds cached battery-presence probe state for ESP32-C61 builds. |
| src/utility/Power_Class.cpp | CoreMatrix PM1/IOE1 setup, Grove/TF power handling, battery presence probing, charge-state logic, EXT1 wake tweaks. |
| src/utility/M5IOE1_Class.hpp | Extends IOE1 expander interface with error-aware input read. |
| src/utility/M5IOE1_Class.cpp | Implements IOE1 error-aware input read used for charge status. |
| src/utility/IOExpander_Base.hpp | Adds default getInputLevel API to support error-aware reads across expanders. |
| src/utility/IMU_Class.cpp | Adds CoreMatrix BMI270 axis remap to normalize orientation. |
| src/M5Unified.cpp | Adds CoreMatrix pin tables and CoreMatrix-specific button polling via PM1 GPIO input bits. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+1788
to
+1801
| bool Power_Class::_batteryPresent(void) | ||
| { | ||
| if (_batt_present < 0) | ||
| { | ||
| bool chg_enabled = M5pm1.getBatteryCharge(); | ||
| std::int32_t pre_mv = M5pm1.getBatteryVoltage(); | ||
| M5pm1.setBatteryCharge(false); | ||
| m5gfx::delay(1200); | ||
| std::int32_t post_mv = M5pm1.getBatteryVoltage(); | ||
| M5pm1.setBatteryCharge(chg_enabled); | ||
| _batt_present = (post_mv > 2000) && (pre_mv - post_mv < 500); | ||
| } | ||
| return _batt_present; | ||
| } |
Comment on lines
+221
to
+224
| bool M5PM1_Class::getBatteryCharge(void) | ||
| { | ||
| return readRegister8(M5PM1_REG_PWR_CFG) & M5PM1_PWR_CFG_CHG_EN; | ||
| } |
With no battery attached, the PM1 VBAT ADC reads the AW32901 charger float voltage (about 4.2V), so getBatteryVoltage() reported a fully charged battery on battery-less units. Distinguish the two by briefly pausing the charger on the first battery API call: without a battery VBAT collapses well below 2V within one PM1 ADC update cycle (about 1 second), while a real battery holds its voltage. The result is cached, so only the first call blocks (~1.2s). With no battery attached: - getBatteryVoltage() returns 0 - getBatteryLevel() returns -1 - isCharging() returns is_discharging (without a battery the charger retries periodically and CHG_STAT blips low, which would otherwise be reported as charging)
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.
Adds support for the upcoming M5Stack CoreMatrix (ESP32-C61). Companion PR: m5stack/M5GFX#242.
charge_unknownon I2C failure), TF card power enabled inPower.begin(off at reset), andsetExtOutput/getExtOutputdriving the Grove port power gate.rtc_gpio_deinit, otherwise the next sleep entry never completes (the C5 does not exhibit this; verified on a ToughC5).Verified on hardware: buttons, battery presence detection (battery-less unit), charge state, Grove power readback, SD card mount, IMU axis directions (three-way against CoreS3 and Fire), and repeated timer / EXT1 deep sleep wake cycles.