Skip to content

Restore GPIO46 after board detection on boards that expose it to the application - #345

Merged
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:gpio46_camera_restore
Aug 27, 2026
Merged

Restore GPIO46 after board detection on boards that expose it to the application#345
lovyan03 merged 1 commit into
m5stack:developfrom
ainyan03:gpio46_camera_restore

Conversation

@ainyan03

Copy link
Copy Markdown
Contributor

Problem

On ESP32-S3, M5.begin() drives GPIO46 high as an output before the board is known, because Capsule / Dial / DinMeter use it as the power hold pin and would power off otherwise. The pin was left as an output on every board afterwards.

On CoreS3 GPIO46 is the camera VSYNC. When esp_camera_init() is called before M5.begin() (a legitimate order now that the I2C bus is shared with an already opened i2c_master bus), the camera stops delivering frames after M5.begin(): cam_hal: Failed to get frame: timeout, while the sensor stays readable over I2C. Returning GPIO46 to an input makes frames flow again, which pins the cause to the driven output.

Change

Back up the pad state before asserting the hold, and restore it once the board is positively identified as one that exposes GPIO46 to the application:

  • CoreS3 / CoreS3SE / StackChan (camera VSYNC)
  • AtomS3R Cam (camera data)
  • AtomS3R Ext / StampS3 / StampS3Bat (header pin)

Every other case keeps the previous behaviour on purpose:

  • the power hold boards must stay asserted;
  • StopWatch / ChainCaptain / PaperS3 / PaperDIY have Display.init() configure GPIO46 as a display bus pin after the backup was taken, so an unconditional restore would undo that;
  • a board resolved through cfg.fallback_board is not restored, because the physical board may still be one that needs the hold;
  • Cardputer / PaperMono use GPIO46 as the mic data line, which M5Unified reconfigures itself.

The pin is still driven high for the duration of board detection; this makes the interference finite instead of permanent, it does not remove it.

Verification

CoreS3 (GC0308), ESP-IDF 5.5.1, esp32-camera 2.1.7, QVGA RGB565 in DRAM:

  • esp_camera_init() (own bus on GPIO12/11) → M5.begin(): before the change frames time out after M5.begin(); after the change frames keep streaming, and reading the PMIC through M5.In_I2C on the bus opened by esp32-camera works.
  • M5.begin()esp_camera_init() with pin_sccb_sda = -1 and sccb_i2c_port = M5.In_I2C.getPort(): unchanged, frames stream and the PMIC is read on the shared bus.

Related: #163.

…application

begin() drives GPIO46 high as the power hold pin before the board is known
(Capsule / Dial / DinMeter power off otherwise) and left it as an output on
every board. On CoreS3 GPIO46 is the camera VSYNC, so an esp_camera_init()
done before M5.begin() stopped delivering frames (frame timeout while the
sensor stayed readable over I2C).

Back up the pad before asserting and restore it once the board is positively
identified as one that exposes GPIO46 to the application: CoreS3 / CoreS3SE /
StackChan (camera VSYNC), AtomS3R Cam (camera data), AtomS3R Ext / StampS3 /
StampS3Bat (header pin). Every other case keeps the previous behaviour on
purpose: the power hold boards must stay asserted, StopWatch / ChainCaptain /
PaperS3 / PaperDIY have Display.init() configure GPIO46 as a display bus pin
after the backup was taken (an unconditional restore would undo that), and a
board resolved through cfg.fallback_board is not restored because the physical
board may still be one that needs the hold. Cardputer / PaperMono use GPIO46
as the mic data line, which M5Unified itself reconfigures, so they are left
as is.

The pin is still driven high for the duration of board detection; the change
makes the interference finite instead of permanent, it does not remove it.

Verified on CoreS3 (ESP-IDF 5.5.1, esp32-camera 2.1.7): camera first then
M5.begin() and M5.begin() first then camera (sccb_i2c_port = In_I2C port)
both stream frames while the PMIC is read over the shared bus.
@lovyan03
lovyan03 merged commit 2b45b1c into m5stack:develop Aug 27, 2026
27 checks passed
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