From dce34b37edd4ae6e823c797308fb83530f18242f Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Tue, 20 Jan 2026 16:03:04 +0100 Subject: [PATCH 1/2] Add M5Stack C6L variant with PI4IO GPIO expander support --- variants/m5stack_c6l/M5StackC6LBoard.cpp | 60 +++++++++++++++++++ variants/m5stack_c6l/M5StackC6LBoard.h | 38 ++++++++++++ variants/m5stack_c6l/platformio.ini | 76 ++++++++++++++++++++++++ variants/m5stack_c6l/target.cpp | 49 +++++++++++++++ variants/m5stack_c6l/target.h | 21 +++++++ 5 files changed, 244 insertions(+) create mode 100644 variants/m5stack_c6l/M5StackC6LBoard.cpp create mode 100644 variants/m5stack_c6l/M5StackC6LBoard.h create mode 100644 variants/m5stack_c6l/platformio.ini create mode 100644 variants/m5stack_c6l/target.cpp create mode 100644 variants/m5stack_c6l/target.h diff --git a/variants/m5stack_c6l/M5StackC6LBoard.cpp b/variants/m5stack_c6l/M5StackC6LBoard.cpp new file mode 100644 index 000000000..0bf527476 --- /dev/null +++ b/variants/m5stack_c6l/M5StackC6LBoard.cpp @@ -0,0 +1,60 @@ +#include "M5StackC6LBoard.h" + +void M5StackC6LBoard::pi4ioWriteByte(uint8_t reg, uint8_t value) { + Wire.beginTransmission(PI4IO_ADDR); + Wire.write(reg); + Wire.write(value); + Wire.endTransmission(); +} + +uint8_t M5StackC6LBoard::pi4ioReadByte(uint8_t reg) { + Wire.beginTransmission(PI4IO_ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom(PI4IO_ADDR, (uint8_t)1); + return Wire.read(); +} + +void M5StackC6LBoard::initGpioExpander() { + pi4ioWriteByte(PI4IO_REG_CHIP_RESET, 0xFF); + delay(10); + + pi4ioReadByte(PI4IO_REG_CHIP_RESET); + delay(10); + + pi4ioWriteByte(PI4IO_REG_IO_DIR, 0b11000000); + delay(10); + + pi4ioWriteByte(PI4IO_REG_OUT_H_IM, 0b00111100); + delay(10); + + pi4ioWriteByte(PI4IO_REG_PULL_SEL, 0b11000011); + delay(10); + + pi4ioWriteByte(PI4IO_REG_PULL_EN, 0b11000011); + delay(10); + + pi4ioWriteByte(PI4IO_REG_IN_DEF_STA, 0b00000011); + delay(10); + + pi4ioWriteByte(PI4IO_REG_INT_MASK, 0b11111100); + delay(10); + + pi4ioWriteByte(PI4IO_REG_OUT_SET, 0b10000000); + delay(10); + + pi4ioReadByte(PI4IO_REG_IRQ_STA); + + uint8_t out_set = pi4ioReadByte(PI4IO_REG_OUT_SET); + out_set |= (1 << 6); + pi4ioWriteByte(PI4IO_REG_OUT_SET, out_set); +} + +void M5StackC6LBoard::begin() { + ESP32Board::begin(); + initGpioExpander(); +} + +const char* M5StackC6LBoard::getManufacturerName() const { + return "M5Stack C6L"; +} diff --git a/variants/m5stack_c6l/M5StackC6LBoard.h b/variants/m5stack_c6l/M5StackC6LBoard.h new file mode 100644 index 000000000..dc4f0cc40 --- /dev/null +++ b/variants/m5stack_c6l/M5StackC6LBoard.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#define PI4IO_ADDR 0x43 + +#define PI4IO_REG_CHIP_RESET 0x01 +#define PI4IO_REG_IO_DIR 0x03 +#define PI4IO_REG_OUT_SET 0x05 +#define PI4IO_REG_OUT_H_IM 0x07 +#define PI4IO_REG_IN_DEF_STA 0x09 +#define PI4IO_REG_PULL_EN 0x0B +#define PI4IO_REG_PULL_SEL 0x0D +#define PI4IO_REG_IN_STA 0x0F +#define PI4IO_REG_INT_MASK 0x11 +#define PI4IO_REG_IRQ_STA 0x13 + +class M5StackC6LBoard : public ESP32Board { +public: + void begin(); + const char* getManufacturerName() const override; + +#ifdef P_LORA_TX_NEOPIXEL + void onBeforeTransmit() override { + neopixelWrite(P_LORA_TX_NEOPIXEL, 64, 64, 64); + } + void onAfterTransmit() override { + neopixelWrite(P_LORA_TX_NEOPIXEL, 0, 0, 0); + } +#endif + +private: + void pi4ioWriteByte(uint8_t reg, uint8_t value); + uint8_t pi4ioReadByte(uint8_t reg); + void initGpioExpander(); +}; diff --git a/variants/m5stack_c6l/platformio.ini b/variants/m5stack_c6l/platformio.ini new file mode 100644 index 000000000..82e7e46ba --- /dev/null +++ b/variants/m5stack_c6l/platformio.ini @@ -0,0 +1,76 @@ +[m5stack_c6l] +extends = esp32c6_base +board = esp32-c6-devkitc-1 +board_upload.flash_size = 16MB +board_build.partitions = default_16MB.csv +build_flags = + ${esp32c6_base.build_flags} + -I variants/m5stack_c6l + -D ARDUINO_USB_CDC_ON_BOOT=1 + -D ARDUINO_USB_MODE=1 + -D P_LORA_TX_NEOPIXEL=2 + -D P_LORA_SCLK=20 + -D P_LORA_MISO=22 + -D P_LORA_MOSI=21 + -D P_LORA_NSS=23 + -D P_LORA_DIO_1=7 + -D P_LORA_BUSY=19 + -D P_LORA_RESET=RADIOLIB_NC + -D PIN_BOARD_SDA=10 + -D PIN_BOARD_SCL=8 + -D SX126X_DIO2_AS_RF_SWITCH=true + -D SX126X_DIO3_TCXO_VOLTAGE=3.0 + -D SX126X_CURRENT_LIMIT=140 + -D SX126X_RX_BOOSTED_GAIN=1 + -D RADIO_CLASS=CustomSX1262 + -D WRAPPER_CLASS=CustomSX1262Wrapper + -D LORA_TX_POWER=22 + -D DISABLE_WIFI_OTA=1 +build_src_filter = ${esp32c6_base.build_src_filter} + +<../variants/m5stack_c6l> + +[env:m5stack_c6l_repeater] +extends = m5stack_c6l +build_src_filter = ${m5stack_c6l.build_src_filter} + +<../examples/simple_repeater/*.cpp> +build_flags = + ${m5stack_c6l.build_flags} + -D ADVERT_NAME='"M5Stack C6L Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=50 +lib_deps = + ${m5stack_c6l.lib_deps} + +[env:m5stack_c6l_room_server] +extends = m5stack_c6l +build_src_filter = ${m5stack_c6l.build_src_filter} + +<../examples/simple_room_server> +build_flags = + ${m5stack_c6l.build_flags} + -D ADVERT_NAME='"M5Stack C6L Room"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D ROOM_PASSWORD='"hello"' +lib_deps = + ${m5stack_c6l.lib_deps} + +[env:m5stack_c6l_companion_radio_ble] +extends = m5stack_c6l +build_flags = ${m5stack_c6l.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D BLE_PIN_CODE=123456 + -D BLE_DEBUG_LOGGING=1 + -D OFFLINE_QUEUE_SIZE=256 + -D ENABLE_PRIVATE_KEY_IMPORT=1 + -D ENABLE_PRIVATE_KEY_EXPORT=1 +build_src_filter = ${m5stack_c6l.build_src_filter} + + + - + +<../examples/companion_radio/*.cpp> +lib_deps = + ${m5stack_c6l.lib_deps} + densaugeo/base64 @ ~1.4.0 diff --git a/variants/m5stack_c6l/target.cpp b/variants/m5stack_c6l/target.cpp new file mode 100644 index 000000000..3a2912bd7 --- /dev/null +++ b/variants/m5stack_c6l/target.cpp @@ -0,0 +1,49 @@ +#include +#include "target.h" + +M5StackC6LBoard board; + +#if defined(P_LORA_SCLK) + static SPIClass spi(0); + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi); +#else + RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY); +#endif + +WRAPPER_CLASS radio_driver(radio, board); + +ESP32RTCClock fallback_clock; +AutoDiscoverRTCClock rtc_clock(fallback_clock); +SensorManager sensors; + +bool radio_init() { + fallback_clock.begin(); + rtc_clock.begin(Wire); + +#if defined(P_LORA_SCLK) + spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI); + return radio.std_init(&spi); +#else + return radio.std_init(); +#endif +} + +uint32_t radio_get_rng_seed() { + return radio.random(0x7FFFFFFF); +} + +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { + radio.setFrequency(freq); + radio.setSpreadingFactor(sf); + radio.setBandwidth(bw); + radio.setCodingRate(cr); +} + +void radio_set_tx_power(uint8_t dbm) { + radio.setOutputPower(dbm); +} + +mesh::LocalIdentity radio_new_identity() { + RadioNoiseListener rng(radio); + return mesh::LocalIdentity(&rng); +} diff --git a/variants/m5stack_c6l/target.h b/variants/m5stack_c6l/target.h new file mode 100644 index 000000000..49baf2b58 --- /dev/null +++ b/variants/m5stack_c6l/target.h @@ -0,0 +1,21 @@ +#pragma once + +#define RADIOLIB_STATIC_ONLY 1 +#include +#include +#include +#include +#include +#include +#include + +extern M5StackC6LBoard board; +extern WRAPPER_CLASS radio_driver; +extern AutoDiscoverRTCClock rtc_clock; +extern SensorManager sensors; + +bool radio_init(); +uint32_t radio_get_rng_seed(); +void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); +void radio_set_tx_power(uint8_t dbm); +mesh::LocalIdentity radio_new_identity(); From e274ca91ee5c1e89a99241953329ba24aa6dd535 Mon Sep 17 00:00:00 2001 From: ViezeVingertjes Date: Tue, 20 Jan 2026 16:19:30 +0100 Subject: [PATCH 2/2] Add SPI SSD1306 display support for M5Stack C6L --- src/helpers/ui/SSD1306SpiDisplay.cpp | 71 ++++++++++++++++++++++++++++ src/helpers/ui/SSD1306SpiDisplay.h | 43 +++++++++++++++++ variants/m5stack_c6l/platformio.ini | 39 +++++++++++++++ variants/m5stack_c6l/target.cpp | 4 ++ variants/m5stack_c6l/target.h | 7 +++ 5 files changed, 164 insertions(+) create mode 100644 src/helpers/ui/SSD1306SpiDisplay.cpp create mode 100644 src/helpers/ui/SSD1306SpiDisplay.h diff --git a/src/helpers/ui/SSD1306SpiDisplay.cpp b/src/helpers/ui/SSD1306SpiDisplay.cpp new file mode 100644 index 000000000..6613751f4 --- /dev/null +++ b/src/helpers/ui/SSD1306SpiDisplay.cpp @@ -0,0 +1,71 @@ +#include "SSD1306SpiDisplay.h" + +bool SSD1306SpiDisplay::begin() { +#ifdef DISPLAY_ROTATION + display.setRotation(DISPLAY_ROTATION); +#endif + return display.begin(SSD1306_SWITCHCAPVCC); +} + +void SSD1306SpiDisplay::turnOn() { + display.ssd1306_command(SSD1306_DISPLAYON); + _isOn = true; +} + +void SSD1306SpiDisplay::turnOff() { + display.ssd1306_command(SSD1306_DISPLAYOFF); + _isOn = false; +} + +void SSD1306SpiDisplay::clear() { + display.clearDisplay(); + display.display(); +} + +void SSD1306SpiDisplay::startFrame(Color bkg) { + display.clearDisplay(); + _color = SSD1306_WHITE; + display.setTextColor(_color); + display.setTextSize(1); + display.cp437(true); +} + +void SSD1306SpiDisplay::setTextSize(int sz) { + display.setTextSize(sz); +} + +void SSD1306SpiDisplay::setColor(Color c) { + _color = (c != 0) ? SSD1306_WHITE : SSD1306_BLACK; + display.setTextColor(_color); +} + +void SSD1306SpiDisplay::setCursor(int x, int y) { + display.setCursor(x, y); +} + +void SSD1306SpiDisplay::print(const char* str) { + display.print(str); +} + +void SSD1306SpiDisplay::fillRect(int x, int y, int w, int h) { + display.fillRect(x, y, w, h, _color); +} + +void SSD1306SpiDisplay::drawRect(int x, int y, int w, int h) { + display.drawRect(x, y, w, h, _color); +} + +void SSD1306SpiDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { + display.drawBitmap(x, y, bits, w, h, SSD1306_WHITE); +} + +uint16_t SSD1306SpiDisplay::getTextWidth(const char* str) { + int16_t x1, y1; + uint16_t w, h; + display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); + return w; +} + +void SSD1306SpiDisplay::endFrame() { + display.display(); +} diff --git a/src/helpers/ui/SSD1306SpiDisplay.h b/src/helpers/ui/SSD1306SpiDisplay.h new file mode 100644 index 000000000..1757a0070 --- /dev/null +++ b/src/helpers/ui/SSD1306SpiDisplay.h @@ -0,0 +1,43 @@ +#pragma once + +#include "DisplayDriver.h" +#include +#include +#define SSD1306_NO_SPLASH +#include + +#ifndef SSD1306_SPI_WIDTH + #define SSD1306_SPI_WIDTH 64 +#endif + +#ifndef SSD1306_SPI_HEIGHT + #define SSD1306_SPI_HEIGHT 48 +#endif + +class SSD1306SpiDisplay : public DisplayDriver { + Adafruit_SSD1306 display; + bool _isOn; + uint8_t _color; + +public: + SSD1306SpiDisplay() : DisplayDriver(SSD1306_SPI_WIDTH, SSD1306_SPI_HEIGHT), + display(SSD1306_SPI_WIDTH, SSD1306_SPI_HEIGHT, &SPI, PIN_SPI_DISPLAY_DC, PIN_SPI_DISPLAY_RST, PIN_SPI_DISPLAY_CS) { + _isOn = false; + } + + bool begin(); + bool isOn() override { return _isOn; } + void turnOn() override; + void turnOff() override; + void clear() override; + void startFrame(Color bkg = DARK) override; + void setTextSize(int sz) override; + void setColor(Color c) override; + void setCursor(int x, int y) override; + void print(const char* str) override; + void fillRect(int x, int y, int w, int h) override; + void drawRect(int x, int y, int w, int h) override; + void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override; + uint16_t getTextWidth(const char* str) override; + void endFrame() override; +}; diff --git a/variants/m5stack_c6l/platformio.ini b/variants/m5stack_c6l/platformio.ini index 82e7e46ba..ed951bb72 100644 --- a/variants/m5stack_c6l/platformio.ini +++ b/variants/m5stack_c6l/platformio.ini @@ -18,6 +18,12 @@ build_flags = -D P_LORA_RESET=RADIOLIB_NC -D PIN_BOARD_SDA=10 -D PIN_BOARD_SCL=8 + -D PIN_SPI_DISPLAY_CS=6 + -D PIN_SPI_DISPLAY_DC=18 + -D PIN_SPI_DISPLAY_RST=15 + -D SSD1306_SPI_WIDTH=64 + -D SSD1306_SPI_HEIGHT=48 + -D PIN_BUZZER=11 -D SX126X_DIO2_AS_RF_SWITCH=true -D SX126X_DIO3_TCXO_VOLTAGE=3.0 -D SX126X_CURRENT_LIMIT=140 @@ -32,9 +38,11 @@ build_src_filter = ${esp32c6_base.build_src_filter} [env:m5stack_c6l_repeater] extends = m5stack_c6l build_src_filter = ${m5stack_c6l.build_src_filter} + + +<../examples/simple_repeater/*.cpp> build_flags = ${m5stack_c6l.build_flags} + -D DISPLAY_CLASS=SSD1306SpiDisplay -D ADVERT_NAME='"M5Stack C6L Repeater"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -42,13 +50,16 @@ build_flags = -D MAX_NEIGHBOURS=50 lib_deps = ${m5stack_c6l.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 [env:m5stack_c6l_room_server] extends = m5stack_c6l build_src_filter = ${m5stack_c6l.build_src_filter} + + +<../examples/simple_room_server> build_flags = ${m5stack_c6l.build_flags} + -D DISPLAY_CLASS=SSD1306SpiDisplay -D ADVERT_NAME='"M5Stack C6L Room"' -D ADVERT_LAT=0.0 -D ADVERT_LON=0.0 @@ -56,10 +67,32 @@ build_flags = -D ROOM_PASSWORD='"hello"' lib_deps = ${m5stack_c6l.lib_deps} + adafruit/Adafruit SSD1306 @ ^2.5.13 + +[env:m5stack_c6l_companion_radio_usb] +extends = m5stack_c6l +build_flags = ${m5stack_c6l.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306SpiDisplay + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 +build_src_filter = ${m5stack_c6l.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${m5stack_c6l.lib_deps} + densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + end2endzone/NonBlockingRTTTL@^1.3.0 [env:m5stack_c6l_companion_radio_ble] extends = m5stack_c6l build_flags = ${m5stack_c6l.build_flags} + -I examples/companion_radio/ui-new + -D DISPLAY_CLASS=SSD1306SpiDisplay -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 -D BLE_PIN_CODE=123456 @@ -68,9 +101,15 @@ build_flags = ${m5stack_c6l.build_flags} -D ENABLE_PRIVATE_KEY_IMPORT=1 -D ENABLE_PRIVATE_KEY_EXPORT=1 build_src_filter = ${m5stack_c6l.build_src_filter} + + + + + + + - +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> lib_deps = ${m5stack_c6l.lib_deps} densaugeo/base64 @ ~1.4.0 + adafruit/Adafruit SSD1306 @ ^2.5.13 + end2endzone/NonBlockingRTTTL@^1.3.0 diff --git a/variants/m5stack_c6l/target.cpp b/variants/m5stack_c6l/target.cpp index 3a2912bd7..64c07bcbd 100644 --- a/variants/m5stack_c6l/target.cpp +++ b/variants/m5stack_c6l/target.cpp @@ -16,6 +16,10 @@ ESP32RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); SensorManager sensors; +#ifdef DISPLAY_CLASS + DISPLAY_CLASS display; +#endif + bool radio_init() { fallback_clock.begin(); rtc_clock.begin(Wire); diff --git a/variants/m5stack_c6l/target.h b/variants/m5stack_c6l/target.h index 49baf2b58..39c8cf55c 100644 --- a/variants/m5stack_c6l/target.h +++ b/variants/m5stack_c6l/target.h @@ -8,12 +8,19 @@ #include #include #include +#ifdef DISPLAY_CLASS + #include +#endif extern M5StackC6LBoard board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; extern SensorManager sensors; +#ifdef DISPLAY_CLASS + extern DISPLAY_CLASS display; +#endif + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);