From 29c67c7c6e85e6a3147f0daae58d5fb1032eb7f7 Mon Sep 17 00:00:00 2001 From: Wouter Bijen Date: Sat, 28 Feb 2026 13:43:27 +0100 Subject: [PATCH 1/4] Fix M5Stack Unit C6L: RX, flash size, pin conflicts - Initialize PI4IOE5V6408 I2C GPIO expander for RF switch/LNA control - Fix I2C bus pins (SDA=10, SCL=8) - ESP32-C6 only has one I2C peripheral - Remove SX126X_RXEN=5 (RF switch is on expander, not ESP32 GPIO) - Set flash size to 16MB (actual hardware, was defaulting to 4MB) - Remove P_LORA_TX_LED=15 (GPIO15 is OLED reset, not TX LED) - Fix TCXO voltage 1.8V -> 3.0V - Remove unused GPS_RX/GPS_TX defines - Add esp_mac.h include for ESP32-C6 BLE builds - Add WiFi companion radio build target --- src/helpers/esp32/SerialBLEInterface.cpp | 1 + variants/m5stack_unit_c6l/UnitC6LBoard.h | 98 +++++++++++++++++++++++- variants/m5stack_unit_c6l/platformio.ini | 37 +++++++-- 3 files changed, 128 insertions(+), 8 deletions(-) diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index dcfa0e1e3..b7c8509af 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -1,3 +1,4 @@ +#include #include "SerialBLEInterface.h" #include "esp_mac.h" diff --git a/variants/m5stack_unit_c6l/UnitC6LBoard.h b/variants/m5stack_unit_c6l/UnitC6LBoard.h index a4ea3ee6e..378ba63fc 100644 --- a/variants/m5stack_unit_c6l/UnitC6LBoard.h +++ b/variants/m5stack_unit_c6l/UnitC6LBoard.h @@ -1,15 +1,111 @@ + #pragma once #include +#include #include +// PI4IOE5V6408 I2C GPIO expander - controls RF switch, LNA, and LoRa reset +// Sits on the internal I2C bus (SDA=10, SCL=8), which is configured as +// the primary Wire bus via PIN_BOARD_SDA/SCL in platformio.ini. +// ESP32-C6 only has ONE I2C hardware peripheral - do NOT use TwoWire(1). + +#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_INT_MASK 0x11 +#define PI4IO_REG_IRQ_STA 0x13 + class UnitC6LBoard : public ESP32Board { public: void begin() { - ESP32Board::begin(); + ESP32Board::begin(); // This calls Wire.begin(10, 8) via PIN_BOARD_SDA/SCL + initGPIOExpander(); } const char* getManufacturerName() const override { return "Unit C6L"; } + +private: + void i2cWrite(uint8_t reg, uint8_t value) { + Wire.beginTransmission(PI4IO_ADDR); + Wire.write(reg); + Wire.write(value); + Wire.endTransmission(); + } + + uint8_t i2cRead(uint8_t reg) { + Wire.beginTransmission(PI4IO_ADDR); + Wire.write(reg); + Wire.endTransmission(); + Wire.requestFrom((uint8_t)PI4IO_ADDR, (uint8_t)1); + return Wire.read(); + } + + bool i2cProbe() { + Wire.beginTransmission(PI4IO_ADDR); + return Wire.endTransmission() == 0; + } + + void initGPIOExpander() { + // Matches Meshtastic's c6l_init() in variant.cpp + // Uses Wire (already on SDA=10, SCL=8 from ESP32Board::begin) + + if (!i2cProbe()) { + Serial.printf("C6L: ERROR - PI4IOE5V6408 not found at 0x%02X\n", PI4IO_ADDR); + return; + } + Serial.println("C6L: PI4IOE5V6408 found, initializing..."); + + // Reset expander + i2cWrite(PI4IO_REG_CHIP_RESET, 0xFF); + delay(10); + i2cRead(PI4IO_REG_CHIP_RESET); + delay(10); + + // P6 (RF switch) and P7 (LoRa reset) as outputs + i2cWrite(PI4IO_REG_IO_DIR, 0b11000000); + delay(10); + + // Disable high-impedance on P2-P5 + i2cWrite(PI4IO_REG_OUT_H_IM, 0b00111100); + delay(10); + + // Pull-up on P0, P1, P6, P7; pull-down on others + i2cWrite(PI4IO_REG_PULL_SEL, 0b11000011); + delay(10); + i2cWrite(PI4IO_REG_PULL_EN, 0b11000011); + delay(10); + + // Button defaults (P0, P1 default HIGH - active low buttons) + i2cWrite(PI4IO_REG_IN_DEF_STA, 0b00000011); + delay(10); + + // Interrupt mask: only P0, P1 generate interrupts + i2cWrite(PI4IO_REG_INT_MASK, 0b11111100); + delay(10); + + // Set P7 HIGH (LoRa out of reset) + i2cWrite(PI4IO_REG_OUT_SET, 0b10000000); + delay(10); + + // Clear pending IRQ + i2cRead(PI4IO_REG_IRQ_STA); + + // Set P6 HIGH (RF switch -> routes antenna to LoRa) + uint8_t out = i2cRead(PI4IO_REG_OUT_SET); + out |= (1 << 6); + i2cWrite(PI4IO_REG_OUT_SET, out); + + // Verify + uint8_t verify = i2cRead(PI4IO_REG_OUT_SET); + Serial.printf("C6L: OUT_SET=0x%02X (expect 0xC0: P6=RF_SW, P7=LORA_RST)\n", verify); + Serial.printf("C6L: IO_DIR=0x%02X (expect 0xC0: P6,P7 outputs)\n", i2cRead(PI4IO_REG_IO_DIR)); + } }; diff --git a/variants/m5stack_unit_c6l/platformio.ini b/variants/m5stack_unit_c6l/platformio.ini index 1dd6749a1..a921f028f 100644 --- a/variants/m5stack_unit_c6l/platformio.ini +++ b/variants/m5stack_unit_c6l/platformio.ini @@ -1,12 +1,15 @@ [M5Stack_Unit_C6L] extends = esp32c6_base board = esp32-c6-devkitm-1 +board_upload.flash_size = 16MB +board_build.flash_size = 16MB board_build.partitions = min_spiffs.csv ; get around 4mb flash limit build_flags = ${esp32c6_base.build_flags} ${sensor_base.build_flags} -I variants/m5stack_unit_c6l - -D P_LORA_TX_LED=15 + ; P_LORA_TX_LED removed - GPIO15 is SSD1306 OLED reset pin, not a TX LED + ; (C6L has WS2812C RGB LED on GPIO2, no discrete TX LED) -D P_LORA_SCLK=20 -D P_LORA_MISO=22 -D P_LORA_MOSI=21 @@ -15,19 +18,19 @@ build_flags = -D P_LORA_BUSY=19 -D P_LORA_RESET=-1 -D PIN_BUZZER=11 - -D PIN_BOARD_SDA=16 - -D PIN_BOARD_SCL=17 - -D SX126X_RXEN=5 + ; I2C bus: primary bus goes to PI4IOE5V6408 GPIO expander (internal) + ; ESP32-C6 only has ONE I2C hardware peripheral, so Wire must be on these pins. + -D PIN_BOARD_SDA=10 + -D PIN_BOARD_SCL=8 + ; SX126X_RXEN removed - RF switch is on I2C expander (PI4IOE5V6408), not ESP32 GPIO -D SX126X_DIO2_AS_RF_SWITCH=true - -D SX126X_DIO3_TCXO_VOLTAGE=1.8 + -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 - -D GPS_RX=4 - -D GPS_TX=5 build_src_filter = ${esp32c6_base.build_src_filter} +<../variants/m5stack_unit_c6l> + @@ -88,6 +91,26 @@ lib_deps = densaugeo/base64 @ ~1.4.0 end2endzone/NonBlockingRTTTL@^1.3.0 +[env:M5Stack_Unit_C6L_companion_radio_wifi] +extends = M5Stack_Unit_C6L +build_flags = ${M5Stack_Unit_C6L.build_flags} + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D WIFI_DEBUG_LOGGING=1 + -D WIFI_SSID='"myssid"' + -D WIFI_PWD='"mypwd"' + -D OFFLINE_QUEUE_SIZE=256 +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 +build_src_filter = ${M5Stack_Unit_C6L.build_src_filter} + + + - + +<../examples/companion_radio/*.cpp> +lib_deps = + ${M5Stack_Unit_C6L.lib_deps} + densaugeo/base64 @ ~1.4.0 + end2endzone/NonBlockingRTTTL@^1.3.0 + [env:M5Stack_Unit_C6L_companion_radio_usb] extends = M5Stack_Unit_C6L build_flags = ${M5Stack_Unit_C6L.build_flags} From 043bc473ab1d115a09546b9db7c7e214313af5a5 Mon Sep 17 00:00:00 2001 From: Wouter Bijen Date: Sat, 28 Feb 2026 13:57:48 +0100 Subject: [PATCH 2/4] dev already has the esp_mac fix --- src/helpers/esp32/SerialBLEInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/helpers/esp32/SerialBLEInterface.cpp b/src/helpers/esp32/SerialBLEInterface.cpp index b7c8509af..dcfa0e1e3 100644 --- a/src/helpers/esp32/SerialBLEInterface.cpp +++ b/src/helpers/esp32/SerialBLEInterface.cpp @@ -1,4 +1,3 @@ -#include #include "SerialBLEInterface.h" #include "esp_mac.h" From dd000ed024a7dead47d02f45db7818d76e6c7ca5 Mon Sep 17 00:00:00 2001 From: Wouter Bijen Date: Sat, 28 Feb 2026 14:03:29 +0100 Subject: [PATCH 3/4] Remove debug Serial prints from GPIO expander init --- variants/m5stack_unit_c6l/UnitC6LBoard.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/variants/m5stack_unit_c6l/UnitC6LBoard.h b/variants/m5stack_unit_c6l/UnitC6LBoard.h index 378ba63fc..bc26001f3 100644 --- a/variants/m5stack_unit_c6l/UnitC6LBoard.h +++ b/variants/m5stack_unit_c6l/UnitC6LBoard.h @@ -58,10 +58,8 @@ class UnitC6LBoard : public ESP32Board { // Uses Wire (already on SDA=10, SCL=8 from ESP32Board::begin) if (!i2cProbe()) { - Serial.printf("C6L: ERROR - PI4IOE5V6408 not found at 0x%02X\n", PI4IO_ADDR); return; } - Serial.println("C6L: PI4IOE5V6408 found, initializing..."); // Reset expander i2cWrite(PI4IO_REG_CHIP_RESET, 0xFF); @@ -103,9 +101,5 @@ class UnitC6LBoard : public ESP32Board { out |= (1 << 6); i2cWrite(PI4IO_REG_OUT_SET, out); - // Verify - uint8_t verify = i2cRead(PI4IO_REG_OUT_SET); - Serial.printf("C6L: OUT_SET=0x%02X (expect 0xC0: P6=RF_SW, P7=LORA_RST)\n", verify); - Serial.printf("C6L: IO_DIR=0x%02X (expect 0xC0: P6,P7 outputs)\n", i2cRead(PI4IO_REG_IO_DIR)); } }; From 10b120486f67eb8606dc1926e4f409ef96bdaa5d Mon Sep 17 00:00:00 2001 From: Wouter Bijen Date: Sat, 28 Feb 2026 14:06:08 +0100 Subject: [PATCH 4/4] Remove i2cProbe check - expander is soldered on board, matches Meshtastic --- variants/m5stack_unit_c6l/UnitC6LBoard.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/variants/m5stack_unit_c6l/UnitC6LBoard.h b/variants/m5stack_unit_c6l/UnitC6LBoard.h index bc26001f3..90d8df367 100644 --- a/variants/m5stack_unit_c6l/UnitC6LBoard.h +++ b/variants/m5stack_unit_c6l/UnitC6LBoard.h @@ -48,18 +48,11 @@ class UnitC6LBoard : public ESP32Board { return Wire.read(); } - bool i2cProbe() { - Wire.beginTransmission(PI4IO_ADDR); - return Wire.endTransmission() == 0; - } void initGPIOExpander() { // Matches Meshtastic's c6l_init() in variant.cpp // Uses Wire (already on SDA=10, SCL=8 from ESP32Board::begin) - if (!i2cProbe()) { - return; - } // Reset expander i2cWrite(PI4IO_REG_CHIP_RESET, 0xFF);