feat: Support Elecrow ThinkNode M9 (#10908)

* thinknode-m9 variant

* move lora to SPI1 device

* enable SDcard

* use HSPI

* BaseUI tft -> HSPI

* buzzer, webdav lib

* fix build issues

* M9 default to MUI, no BT, short ringtone

* add keyboard long-press config

* update variant

* add ThingNode-M9 GPS string

* GPS 115200 baud

* Basic BaseUI support

* Fixup power detection

* Compass and KB fixes for M9

* add timed Lock::lock()

* add SD card

* point device-ui to thinknode m9 draft branch

* trunk fmt

* fix FusionCompass

* Fix t-deck-tft linker arg list overflow in CI

* SDcard/lora fix: SPI1 must not be declared twice in arduino 3.x -> reuse SPI1 defined in FSCommon.cpp

* update battery parameters

* reinit SD card when updating; fix PSRAM size

* update lib versions

* fix wakeup on key press (KB_INT)

* fix default nag_timeout for TFT/MUI devices with buzzer

* increase PSRAM and SD freq

* trunk fmt

* update lovyanGFX 1.2.26

* update device-ui commit reference

* fix screen definition

* remove DONE; maybe a keyword or other used identifier

* fixed CI error nag_timeout

* fix prepareSleep initialization

* trunk fmt

* reduce SD SPI frequency

* update device-ui

* fix SDcard issue

* stage

* fix device-ui commit reference

* fix device-ui commit

* update device-ui commit (fixed keyboard lag)

* fix QMI8658

* trunk fmt

* update .ini meta information, align SD freq

* fix device-ui reference to target (ready to merge)

* device-ui for all other targets

* make the rabbit happy

* trunk fmt

* fixed lock screen

* fix compile error

* SPI lock timeout

* apply device-ui fix

* revert bad RadioLib commit hash in platformio.ini

Co-authored-by: mverch67 <71137295+mverch67@users.noreply.github.com>

* fix wrong commit hash change

* fix fix commit fix

* I love changing random numbers in random files

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Manuel
2026-08-17 17:28:33 +00:00
committed by GitHub
co-authored by Jonathan Bennett Ben Meadors copilot-swe-agent[bot]
parent ef2be877a5
commit c308d0aca4
32 changed files with 1098 additions and 38 deletions
@@ -0,0 +1,20 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
static const uint8_t TX = 43;
static const uint8_t RX = 44;
static const uint8_t SDA = 20;
static const uint8_t SCL = 21;
static const uint8_t SS = 39;
static const uint8_t MOSI = 47;
static const uint8_t MISO = 38;
static const uint8_t SCK = 40;
#endif
@@ -0,0 +1,99 @@
[thinknode_m9_base]
custom_meshtastic_hw_model = 131
custom_meshtastic_hw_model_slug = ELECROW_ThinkNode_M9
custom_meshtastic_architecture = esp32-s3
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = ThinkNode M9
custom_meshtastic_images = thinknode_m9.svg
custom_meshtastic_tags = Elecrow
custom_meshtastic_requires_dfu = false
custom_meshtastic_partition_scheme = 16MB
custom_meshtastic_has_mui = true
extends = esp32s3_base
board = crowpanel
board_level = release
board_build.partitions = default_16MB.csv
upload_protocol = esptool
build_src_filter =
${esp32s3_base.build_src_filter}
+<../variants/esp32s3/ELECROW-ThinkNode-M9>
build_flags =
${esp32s3_base.build_flags}
-I variants/esp32s3/ELECROW-ThinkNode-M9
-D ELECROW_ThinkNode_M9
-D BOARD_HAS_PSRAM
-D HAS_SDCARD=1
-D SDCARD_CS=48
-D SDCARD_USE_SPI1
-D SDCARD_SHARE_SPI
-D SDCARD_INIT_SPI
-D SD_SPI_FREQUENCY=75000000U
-D HAS_SCREEN=1
; -D COMPASS_SENSOR_DEBUG=1
lib_deps = ${esp32s3_base.lib_deps}
# renovate: datasource=custom depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@1.2.26
# renovate: datasource=custom depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.4.1
[env:thinknode_m9]
extends = thinknode_m9_base
build_flags =
${thinknode_m9_base.build_flags}
[env:thinknode_m9-tft]
extends = thinknode_m9_base
build_flags =
${thinknode_m9_base.build_flags}
-D RADIOLIB_SPI_PARANOID=0
-D CONFIG_DISABLE_HAL_LOCKS=1
-D HAS_TFT=1
-D USE_PACKET_API
-D USE_PIN_BUZZER=PIN_BUZZER
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D LV_USE_SYSMON=0
-D LV_USE_PROFILER=0
-D LV_USE_PERF_MONITOR=0
-D LV_USE_MEM_MONITOR=0
-D LV_USE_LOG=0
-D LV_BUILD_TEST=0
-D USE_LOG_DEBUG
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
-D RAM_SIZE=5120
-D LGFX_BUFSIZE=153600
-D LGFX_DRIVER_TEMPLATE
-D DISPLAY_SIZE=320x240
-D LGFX_DRIVER=LGFX_GENERIC
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\"
-D VIEW_320x240
-D LGFX_PANEL=ST7789
-D LGFX_ROTATION=3
-D LGFX_CFG_HOST=SPI3_HOST
-D LGFX_PIN_SCK=40
-D LGFX_PIN_MOSI=47
-D LGFX_PIN_DC=15
-D LGFX_PIN_CS=16
-D LGFX_PIN_BL=17
-D LGFX_PIN_RST=14
-D LGFX_SCREEN_WIDTH=240
-D LGFX_SCREEN_HEIGHT=320
-D LGFX_INVERT_LIGHT=true
-D SPI_FREQUENCY=75000000
; -D MAP_FULL_REDRAW
-D MUI_WIFI_PS_MIN_MODEM
-D DEFAULT_FTP_SERVER_NETWORK_TYPE_ESP32=NETWORK_ESP32
-D DEFAULT_STORAGE_TYPE_ESP32=STORAGE_SD
-D CHARGING_VOLTAGE=4.25
lib_deps =
${thinknode_m9_base.lib_deps}
https://github.com/meshtastic/device-ui/archive/e8a5ff337d1ead20b290307fb2159ad27fe47f86.zip ; PR314 input-policy
https://github.com/mverch67/MultiFTPServer/archive/0e854335b9916ed9f2d3bcfe68975ce746992ccd.zip
custom_sdkconfig =
${esp32s3_base.custom_sdkconfig}
${device-ui_base.custom_sdkconfig}
@@ -0,0 +1,11 @@
#include "RadioLib.h"
static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[] = {
// mode DIO5 DIO6
{LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
{LR11x0::MODE_TX, {HIGH, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
{LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
{LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
};
@@ -0,0 +1,34 @@
#include "variant.h"
#include "Arduino.h"
#include "SPILock.h"
#include "Wire.h"
void earlyInitVariant()
{
pinMode(LORA_CS, OUTPUT);
digitalWrite(LORA_CS, HIGH);
pinMode(SDCARD_CS, OUTPUT);
digitalWrite(SDCARD_CS, HIGH);
pinMode(TFT_CS, OUTPUT);
digitalWrite(TFT_CS, HIGH);
delay(100);
}
void lateInitVariant()
{
// configure keyboard long-press time
const uint16_t ms = 700;
concurrency::LockGuard g(spiLock);
Wire.beginTransmission(0x6C);
Wire.write(0x03);
Wire.write((ms >> 8) & 0xFF);
Wire.write(ms & 0xFF);
Wire.endTransmission();
}
void variant_shutdown()
{
uint64_t gpioMask = (1ULL << KB_INT);
gpio_pulldown_en((gpio_num_t)KB_INT);
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_HIGH);
}
@@ -0,0 +1,107 @@
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define PRESET_MESSAGE_MODULE_ENABLE 1
/*Power*/
#define VEXT_ENABLE 18
#define VEXT_ON_VALUE LOW
#define PIN_GPS_EN 11
#define GPS_EN_ACTIVE LOW
#define USE_POWERSAVE
#define SLEEP_TIME 120
/*Wire Interface*/
#define WIRE_INTERFACES_COUNT 2
// I2C keyboard
#define I2C_SCL 21
#define I2C_SDA 20
#define KB_INT 12 // STC8H key-press interrupt (idle low, rising edge on press)
#define KB_INT_WAKE_ON_HIGH 1 // KB_INT rests low; wake light sleep on its HIGH (active) level
#define KB_LED 46 // STC8H keypad backlight LED
// I2C peripheral
#define I2C_SCL1 6
#define I2C_SDA1 7
/*BUZZER*/
#define PIN_BUZZER 9
/*CHARGE_CHECK*/
#define EXT_PWR_DETECT 1
// #define EXT_CHRG_DETECT 1
#define EXT_PWR_DETECT_VALUE LOW
/*GPS*/
#define HAS_GPS 1
#define GPS_BAUDRATE 115200
#define PIN_GPS_RESET 5
#define PIN_GPS_PPS 4
#define GPS_TX_PIN 3
#define GPS_RX_PIN 2
#define GPS_THREAD_INTERVAL 50
/*SPI*/
#define SPI_MOSI 47
#define SPI_SCK 40
#define SPI_MISO 38
/*Screen*/
#define ST7789_CS 16
#define ST7789_RS 15
#define ST7789_TE 19
#define ST7789_SDA SPI_MOSI // MOSI
#define ST7789_SCK SPI_SCK
#define ST7789_RESET 14
#define ST7789_MISO SPI_MISO
#define ST7789_BUSY -1
#define ST7789_BL 17
#define ST7789_SPI_HOST SPI3_HOST
#define SPI_READ_FREQUENCY 16000000
#define USE_TFTDISPLAY 1
#define HAS_SPI_TFT 1
#define TFT_CS ST7789_CS
#define TFT_BL ST7789_BL
#define TFT_HEIGHT 320
#define TFT_WIDTH 240
#define TFT_OFFSET_X 0
#define TFT_OFFSET_Y 0
#define TFT_OFFSET_ROTATION 0
#define TFT_PWM_FREQ 44000
#define TFT_PWM_CHANNEL 7
#define TFT_INVERT_LIGHT true
#define TFT_BACKLIGHT_ON LOW
#define SCREEN_ROTATE
#define SCREEN_TRANSITION_FRAMERATE 10
#define BRIGHTNESS_DEFAULT 128
/*Lora radio*/
#define HW_SPI1_DEVICE
#define LORA_SCK SPI_SCK
#define LORA_MISO SPI_MISO
#define LORA_MOSI SPI_MOSI
#define LORA_CS 39
#define LORA_RESET 45
#define LORA_DIO0 41
#define LORA_DIO1 42
#define USE_LR1110
#define LR1110_IRQ_PIN LORA_DIO1
#define LR1110_NRESET_PIN LORA_RESET
#define LR1110_BUSY_PIN LORA_DIO0
#define LR1110_SPI_NSS_PIN LORA_CS
#define LR1110_SPI_SCK_PIN LORA_SCK
#define LR1110_SPI_MOSI_PIN LORA_MOSI
#define LR1110_SPI_MISO_PIN LORA_MISO
#define LR11X0_DIO3_TCXO_VOLTAGE 3.3
#define LR11X0_DIO_AS_RF_SWITCH
/*RTC*/
#define PCF8563_RTC 0x51
/*BATTERY*/
#define BATTERY_PIN 13
#define BATTERY_IMMUTABLE
#define ADC_MULTIPLIER 2.0f
#define BAT_MEASURE_ADC_UNIT ADC_UNIT_2
#define ADC_CHANNEL ADC_CHANNEL_2
#define OCV_ARRAY 4200, 4080, 3980, 3920, 3870, 3820, 3790, 3750, 3700, 3600, 3100
+4
View File
@@ -39,6 +39,10 @@ lib_deps = ${esp32s3_base.lib_deps}
extends = env:t-deck
board_level = pr
extra_scripts =
${env:t-deck.extra_scripts}
extra_scripts/ld_response_file.py
build_flags =
${env:t-deck.build_flags}
-D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks