Skip to content

feat: add M5Stack Unit C6L variant support - #3100

Draft
vistalba wants to merge 1 commit into
meshcore-dev:devfrom
vistalba:pr-to-dev
Draft

feat: add M5Stack Unit C6L variant support#3100
vistalba wants to merge 1 commit into
meshcore-dev:devfrom
vistalba:pr-to-dev

Conversation

@vistalba

@vistalba vistalba commented Aug 2, 2026

Copy link
Copy Markdown

Summary

Adds full support for the M5Stack Unit C6L (ESP32-C6 + SX1262 LoRa), including PI4IO I/O expander for RF switch control and a custom minimal UITask for the 64x48 SSD1306 SPI display.

Changes

New variant: variants/m5stack_unit_c6l/

  • Board class (UnitC6LBoard): PI4IO I/O expander (reset, direction, pull-up/pull-down), SPI bus management, display/RF switch init
  • Hardware config (platformio.ini): corrected pins (NeoPixel TX on 2, I2C SDA=10/SCL=8, SPI shared with LoRa)
  • DIO flash mode: board_build.flash_mode = dio — fixes bootloop on ESP32-C6 rev v0.2 (see Added flash_mode=dio to avoid boot loop when flashing using merge.bin #2570)
  • 4 firmware environments: companion USB, companion BLE, repeater, room server

Display support (SSD1306SPIDisplay)

  • New SPI-bus-sharing OLED driver in src/helpers/ui/SSD1306SPIDisplay.*
  • Shares SPI bus with LoRa SX1262 via lazy init (spi_initialized flag)
  • Custom minimal UITask in variants/m5stack_unit_c6l/companion/ — shows PIN, scrolling node name, frequency, unread count
  • Fits 64x48 display (standard UITask overflows)

Shared fixes

  • src/helpers/esp32/SerialBLEInterface.cpp: fixed include "esp_mac.h"<esp_mac.h>
  • src/helpers/ui/DisplayDriver.h: added MarqueeScroller struct

@vistalba

vistalba commented Aug 2, 2026

Copy link
Copy Markdown
Author

DisplayDriver.h Conflict
Issue: Upstream added UIColor class and ColorVal type, while our C6L implementation uses MarqueeScroller struct and enum Color.

Resolution: Kept our C6L-compatible implementation:

✅ Retained MarqueeScroller struct for companion radio display scrolling
✅ Removed upstream's UIColor/ColorVal (not needed for C6L)
✅ Activated enum Color for display driver color handling

platformio.ini Conflict
Issue: Upstream has a different C6L implementation with different pin configuration (SDA=16/SCL=17 vs our PI4IO SDA=10/SCL=8), no display support, and different env naming.

Resolution: Kept our hardware-correct implementation:

✅ PI4IO I/O expander pins (SDA=10, SCL=8)
✅ SSD1306 64x48 display support
✅ DIO flash mode fix for ESP32-C6
✅ NeoPixel TX LED (GPIO 2)
✅ All environments: companion_usb, companion_ble, repeater, room_server

Build successful on c6l-dev-ci branch: https://github.com/vistalba/MeshCore/actions

@vistalba
vistalba marked this pull request as ready for review August 2, 2026 16:30
@vistalba
vistalba marked this pull request as draft August 2, 2026 17:12
@jirogit

jirogit commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Saw this while it's in progress — I've been working on the same board
from a different angle, so here are a few things I hit on real hardware
that the companion_ble env will likely run into. Sharing now rather
than later in case any of it affects how you want to shape this.

1. USB CDC flags missing on the BLE env

ARDUINO_USB_CDC_ON_BOOT=1 / ARDUINO_USB_MODE=1 need to be set on the
BLE env too, not just the USB one. Without them Serial goes to UART0,
which isn't physically wired to this board's USB connector — so the BLE
build produces no serial output at all, which makes everything else much
harder to debug.

2. Android fails to pair on first boot after a fresh erase

When _prefs.ble_pin == 0, MyMesh.cpp generates a random PIN every
boot but never persists it, so Android's bond cache goes stale on each
restart. iOS tolerates this; Android does not.

I worked around it by persisting the generated PIN on first boot, which
trades away the per-boot randomness — not great from a security
standpoint, so I'm flagging the behaviour rather than recommending my
workaround. A cleaner fix is probably to persist only after the user
confirms the displayed PIN, which your UITask is already well positioned
to do since it shows the PIN.

3. BLE notify can be silently dropped (ESP32-specific)

BLECharacteristic::notify() discards the payload silently when the
client hasn't subscribed to the CCCD (0x2902) yet. The nRF52 path is
asymmetric — bleuart.write() returns written == 0 so the caller
knows to retry; on ESP32 the response is just gone.

I added a CCCD subscription check in checkRecvFrame() that holds the
frame and retries next loop:
https://github.com/jirogit/MeshCore/tree/fix/esp32-ble-cccd-race
I haven't submitted it upstream because I couldn't fully demonstrate the
improvement, so treat it as a lead, not a fix.

Still open on my side: iPhone X shows a "Fail to fetch device info"
popup even with the CCCD change. Current suspicion is that the async
push commands in MyMesh.cpp are guarded on isConnected() alone, so
they can fire after pairing but before the app's init handshake
finishes. Not board-specific and not confirmed.


Two smaller notes, take or leave while this is still a draft:

Happy to build and test this branch on my hardware whenever you want a
second device on it.

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