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
+23
View File
@@ -23,6 +23,9 @@
#include "graphics/images.h"
#include "main.h"
#include "target_specific.h"
#ifdef COMPASS_SENSOR_DEBUG
#include "motion/MotionSensor.h"
#endif
#include <OLEDDisplay.h>
#include <cstring>
#include <gps/RTC.h>
@@ -1776,6 +1779,26 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
const int *textPos = getTextPositions(display);
const bool compactPanel = graphics::isCompactPanel(display);
#ifdef COMPASS_SENSOR_DEBUG
// Optional raw IMU accel + magnetometer x/y/z readout for on-device axis/sign tuning.
{
char dbg[40];
float sx = 0, sy = 0, sz = 0;
uint32_t age = 0;
if (MotionSensor::getLatestCompassAccelSample(sx, sy, sz, age))
snprintf(dbg, sizeof(dbg), "A %.2f %.2f %.2f", sx, sy, sz);
else
snprintf(dbg, sizeof(dbg), "A ---");
display->drawString(x, textPos[line++], dbg);
if (MotionSensor::getLatestCompassMagSample(sx, sy, sz, age))
snprintf(dbg, sizeof(dbg), "M %.2f %.2f %.2f", sx, sy, sz);
else
snprintf(dbg, sizeof(dbg), "M ---");
display->drawString(x, textPos[line++], dbg);
}
#endif
// === First Row: My Location ===
#if HAS_GPS
bool origBold = config.display.heading_bold;
+23 -6
View File
@@ -235,10 +235,8 @@ DeviceScreen *deviceScreen = nullptr;
#ifdef ARCH_ESP32
// Get notified when the system is entering light sleep
CallbackObserver<DeviceScreen, void *> tftSleepObserver =
CallbackObserver<DeviceScreen, void *>(deviceScreen, &DeviceScreen::prepareSleep);
CallbackObserver<DeviceScreen, esp_sleep_wakeup_cause_t> endSleepObserver =
CallbackObserver<DeviceScreen, esp_sleep_wakeup_cause_t>(deviceScreen, &DeviceScreen::wakeUp);
static CallbackObserver<DeviceScreen, void *> *tftSleepObserver = nullptr;
static CallbackObserver<DeviceScreen, esp_sleep_wakeup_cause_t> *endSleepObserver = nullptr;
#endif
/**
@@ -277,6 +275,19 @@ class ReentrantSpiLock : public ISpiLock
depth = 1;
}
bool lock(uint32_t timeout) override
{
ThreadId self = currentThread();
if (depth && owner == self) {
depth++;
return true;
}
bool result = spiLock->lock(timeout);
owner = self;
depth = 1;
return result;
}
void unlock(void) override
{
if (--depth == 0) {
@@ -413,8 +424,14 @@ void tftSetup(void)
if (deviceScreen) {
#ifdef ARCH_ESP32
tftSleepObserver.observe(&notifyLightSleep);
endSleepObserver.observe(&notifyLightSleepEnd);
if (!tftSleepObserver) {
tftSleepObserver = new CallbackObserver<DeviceScreen, void *>(deviceScreen, &DeviceScreen::prepareSleep);
}
if (!endSleepObserver) {
endSleepObserver = new CallbackObserver<DeviceScreen, esp_sleep_wakeup_cause_t>(deviceScreen, &DeviceScreen::wakeUp);
}
tftSleepObserver->observe(&notifyLightSleep);
endSleepObserver->observe(&notifyLightSleepEnd);
xTaskCreatePinnedToCore(tft_task_handler, "tft", TFT_TASK_STACK_SIZE, NULL, 1, NULL, 0);
#elif defined(ARCH_PORTDUINO)
std::thread *tft_task = new std::thread([] { tft_task_handler(); });