From 691327b2db474e2cf30e3dc099f2ee1ddd08cceb Mon Sep 17 00:00:00 2001 From: Jonathan Bennett Date: Thu, 7 Aug 2025 06:28:15 -0500 Subject: [PATCH 1/2] Initial support for the ThinkNode M5 (#7502) * Initial support for the ThinkNode M5 * Update variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini Co-authored-by: Austin * Cleanup variant.h for Elecrow Thinknode M5 * Properly detect battery voltage * Turn backlight off when screen sleeps --------- Co-authored-by: Ben Meadors Co-authored-by: Austin --- src/graphics/EInkDisplay2.cpp | 15 ++++ src/graphics/EInkDisplay2.h | 2 +- src/graphics/Screen.cpp | 9 ++ src/main.cpp | 13 +++ src/main.h | 5 ++ src/modules/SerialModule.cpp | 11 ++- src/platform/esp32/architecture.h | 2 + .../ELECROW-ThinkNode-M5/pins_arduino.h | 28 +++++++ .../ELECROW-ThinkNode-M5/platformio.ini | 21 +++++ .../esp32s3/ELECROW-ThinkNode-M5/variant.h | 83 +++++++++++++++++++ 10 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 variants/esp32s3/ELECROW-ThinkNode-M5/pins_arduino.h create mode 100644 variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini create mode 100644 variants/esp32s3/ELECROW-ThinkNode-M5/variant.h diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp index 3bd20feec..a627a42cc 100644 --- a/src/graphics/EInkDisplay2.cpp +++ b/src/graphics/EInkDisplay2.cpp @@ -151,6 +151,21 @@ bool EInkDisplay::connect() #else adafruitDisplay->setRotation(3); #endif + adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); + } +#elif defined(ELECROW_ThinkNode_M5) + { + // Start HSPI + hspi = new SPIClass(HSPI); + hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS + + auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi); + + adafruitDisplay = new GxEPD2_BW(*lowLevel); + adafruitDisplay->init(); + + adafruitDisplay->setRotation(4); + adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight); } #elif defined(MESHLINK) diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h index 284337627..b840ce9ba 100644 --- a/src/graphics/EInkDisplay2.h +++ b/src/graphics/EInkDisplay2.h @@ -80,7 +80,7 @@ class EInkDisplay : public OLEDDisplay // If display uses HSPI #if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \ defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \ - defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) + defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || defined(ELECROW_ThinkNode_M5) SPIClass *hspi = NULL; #endif diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 33dc7efcc..8d5635f89 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -391,6 +391,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) dispdev->displayOn(); #endif +#ifdef ELECROW_ThinkNode_M5 + io.digitalWrite(PCA_PIN_EINK_EN, HIGH); +#endif + #if defined(ST7789_CS) && \ !defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here. static_cast(dispdev)->setDisplayBrightness(brightness); @@ -425,6 +429,11 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver) digitalWrite(PIN_EINK_EN, LOW); } #endif + +#ifdef ELECROW_ThinkNode_M5 + io.digitalWrite(PCA_PIN_EINK_EN, LOW); +#endif + dispdev->displayOff(); #ifdef USE_ST7789 SPI1.end(); diff --git a/src/main.cpp b/src/main.cpp index 605fbe50a..7fc1d2cf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,10 @@ #include #include +#ifdef ELECROW_ThinkNode_M5 +PCA9557 io(0x18, &Wire); +#endif + #ifdef ARCH_ESP32 #include "freertosinc.h" #if !MESHTASTIC_EXCLUDE_WEBSERVER @@ -296,6 +300,15 @@ void setup() digitalWrite(PIN_POWER_EN, HIGH); #endif +#if defined(ELECROW_ThinkNode_M5) + Wire.begin(48, 47); + io.pinMode(PCA_PIN_EINK_EN, OUTPUT); + io.pinMode(PCA_PIN_POWER_EN, OUTPUT); + io.digitalWrite(PCA_PIN_EINK_EN, HIGH); + io.digitalWrite(PCA_PIN_POWER_EN, HIGH); + // io.pinMode(C2_PIN, OUTPUT); +#endif + #ifdef LED_POWER pinMode(LED_POWER, OUTPUT); digitalWrite(LED_POWER, LED_STATE_ON); diff --git a/src/main.h b/src/main.h index 7105bd62b..3568daad2 100644 --- a/src/main.h +++ b/src/main.h @@ -51,6 +51,11 @@ extern Adafruit_DRV2605 drv; extern AudioThread *audioThread; #endif +#ifdef ELECROW_ThinkNode_M5 +#include +extern PCA9557 io; +#endif + #ifdef HAS_UDP_MULTICAST #include "mesh/udp/UdpMulticastHandler.h" extern UdpMulticastHandler *udpHandler; diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp index f3091e5bf..39b297965 100644 --- a/src/modules/SerialModule.cpp +++ b/src/modules/SerialModule.cpp @@ -60,7 +60,8 @@ SerialModule *serialModule; SerialModuleRadio *serialModuleRadio; -#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) +#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \ + defined(ELECROW_ThinkNode_M5) SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial") {} static Print *serialPrint = &Serial; #elif defined(CONFIG_IDF_TARGET_ESP32C6) @@ -178,7 +179,8 @@ int32_t SerialModule::runOnce() Serial.begin(baud); Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT); } -#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) +#elif !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && \ + !defined(ELECROW_ThinkNode_M5) if (moduleConfig.serial.rxd && moduleConfig.serial.txd) { #ifdef ARCH_RP2040 Serial2.setFIFOSize(RX_BUFFER); @@ -234,7 +236,8 @@ int32_t SerialModule::runOnce() } } -#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) +#if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && \ + !defined(ELECROW_ThinkNode_M5) else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) { processWXSerial(); @@ -494,7 +497,7 @@ ParsedLine parseLine(const char *line) void SerialModule::processWXSerial() { #if !defined(TTGO_T_ECHO) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(MESHLINK) && \ - !defined(ELECROW_ThinkNode_M1) + !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) static unsigned int lastAveraged = 0; static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded. static double dir_sum_sin = 0; diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h index 3168d9121..522e862ac 100644 --- a/src/platform/esp32/architecture.h +++ b/src/platform/esp32/architecture.h @@ -146,6 +146,8 @@ #define HW_VENDOR meshtastic_HardwareModel_EBYTE_ESP32_S3 #elif defined(ELECROW_ThinkNode_M2) #define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M2 +#elif defined(ELECROW_ThinkNode_M5) +#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M5 #elif defined(ESP32_S3_PICO) #define HW_VENDOR meshtastic_HardwareModel_ESP32_S3_PICO #elif defined(SENSELORA_S3) diff --git a/variants/esp32s3/ELECROW-ThinkNode-M5/pins_arduino.h b/variants/esp32s3/ELECROW-ThinkNode-M5/pins_arduino.h new file mode 100644 index 000000000..46415d30f --- /dev/null +++ b/variants/esp32s3/ELECROW-ThinkNode-M5/pins_arduino.h @@ -0,0 +1,28 @@ +// Need this file for ESP32-S3 +// No need to modify this file, changes to pins imported from variant.h +// Most is similar to https://github.com/espressif/arduino-esp32/blob/master/variants/esp32s3/pins_arduino.h + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Serial +static const uint8_t TX = UART_TX; +static const uint8_t RX = UART_RX; + +// Default SPI will be mapped to Radio +static const uint8_t SS = LORA_CS; +static const uint8_t SCK = LORA_SCK; +static const uint8_t MOSI = LORA_MOSI; +static const uint8_t MISO = LORA_MISO; + +// The default Wire will be mapped to PMU and RTC +static const uint8_t SCL = I2C_SCL; +static const uint8_t SDA = I2C_SDA; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini b/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini new file mode 100644 index 000000000..7dac6e66e --- /dev/null +++ b/variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini @@ -0,0 +1,21 @@ +[env:thinknode_m5] +extends = esp32s3_base +board = ESP32-S3-WROOM-1-N4 +build_flags = + ${esp32s3_base.build_flags} + -D ELECROW_ThinkNode_M5 + -I variants/esp32s3/ELECROW-ThinkNode-M5 + -DEINK_DISPLAY_MODEL=GxEPD2_154_D67 + -DEINK_WIDTH=200 + -DEINK_HEIGHT=200 + -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk + -DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted //20 + -DEINK_LIMIT_RATE_BACKGROUND_SEC=10 ; Minimum interval between BACKGROUND updates //30 + -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates +; -DEINK_LIMIT_GHOSTING_PX=2000 ; (Optional) How much image ghosting is tolerated + -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached. + +lib_deps = ${esp32s3_base.lib_deps} + https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip + lewisxhe/PCF8563_Library@^1.0.1 + maxpromer/PCA9557-arduino @ ^1.0.0 \ No newline at end of file diff --git a/variants/esp32s3/ELECROW-ThinkNode-M5/variant.h b/variants/esp32s3/ELECROW-ThinkNode-M5/variant.h new file mode 100644 index 000000000..61d6149d2 --- /dev/null +++ b/variants/esp32s3/ELECROW-ThinkNode-M5/variant.h @@ -0,0 +1,83 @@ +#ifndef ELECROW_ThinkNode_M5_VAR +#define ELECROW_ThinkNode_M5_VAR + +#define UART_TX 43 +#define UART_RX 44 + +// LED +// Both of these are on the GPIO expander +#define PCA_LED_USER 1 // the Blue LED +#define PCA_LED_POWER 3 // the Red LED? Seems to have hardware logic to blink when USB is plugged in. + +// USB_CHECK +#define EXT_PWR_DETECT 12 +#define BATTERY_PIN 8 +#define ADC_CHANNEL ADC1_GPIO8_CHANNEL + +#define PIN_BUZZER 9 + +// Buttons + +#define PIN_BUTTON2 14 +#define PIN_BUTTON1 21 + +// Wire Interfaces + +#define I2C_SCL 1 +#define I2C_SDA 2 + +// GPS pins +#define GPS_SWITH 10 +#define HAS_GPS 1 +#define GPS_L76K +#define PIN_GPS_REINIT 13 // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K + +#define PIN_GPS_STANDBY 11 // An output to wake GPS, low means allow sleep, high means force wake + +#define GPS_TX_PIN 20 // This is for bits going TOWARDS the CPU +#define GPS_RX_PIN 19 // This is for bits going TOWARDS the GPS + +#define GPS_THREAD_INTERVAL 50 + +#define PIN_SERIAL1_RX GPS_TX_PIN +#define PIN_SERIAL1_TX GPS_RX_PIN + +// PCF8563 RTC Module +#define PCF8563_RTC 0x51 + +#define SX126X_CS 17 +#define LORA_SCK 16 +#define LORA_MOSI 15 +#define LORA_MISO 7 +#define SX126X_RESET 6 +#define SX126X_BUSY 5 +#define SX126X_DIO1 4 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 3.3 +#define SX126X_POWER_EN 46 +#define SX126X_MAX_POWER 22 // SX126xInterface.cpp defaults to 22 if not defined, but here we define it for good practice +#define USE_SX1262 +#define LORA_CS SX126X_CS // FIXME: for some reason both are used in /src +#define LORA_DIO1 SX126X_DIO1 + +#define USE_EINK +#define PIN_EINK_EN -1 // Note: this is really just backlight power +#define PCA_PIN_EINK_EN 5 // This is the pin number on the GPIO expander +#define PIN_EINK_CS 39 +#define PIN_EINK_BUSY 42 +#define PIN_EINK_DC 40 +#define PIN_EINK_RES 41 +#define PIN_EINK_SCLK 38 +#define PIN_EINK_MOSI 45 // also called SDI + +// Controls power for all peripherals (eink + GPS + LoRa + Sensor) +#define PIN_POWER_EN -1 +#define PCA_PIN_POWER_EN 4 // This is the pin number on the GPIO expander + +#define PIN_SPI_MISO 7 +#define PIN_SPI_MOSI 15 +#define PIN_SPI_SCK 16 + +#define BUTTON_PIN PIN_BUTTON1 +#define BUTTON_PIN_ALT PIN_BUTTON2 +#endif From f2a880f81361d252427632d1c77ddb967382e2a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:56:17 +0200 Subject: [PATCH 2/2] chore(deps): update adafruit shtc3 to v1.0.2 (#7557) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 8bf56cf5b..6330f3908 100644 --- a/platformio.ini +++ b/platformio.ini @@ -178,7 +178,7 @@ lib_deps = # renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X adafruit/Adafruit MAX1704X@1.0.3 # renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library - adafruit/Adafruit SHTC3 Library@1.0.1 + adafruit/Adafruit SHTC3 Library@1.0.2 # renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X adafruit/Adafruit LPS2X@2.0.6 # renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library