Add Elecrow ThinkNode M8 board support (thinknode_m8) (#11226)

* Add Elecrow ThinkNode M8 variant scaffold (thinknode_m8)

nRF52840 + SX1262 + 2.4" e-paper + ATGM336H-5NR32 GPS.
All pins resolved from ThinkNode_M8_V0.3.sch; cross-checked
against meshtastic/firmware#9181 (Elecrow V0.1 reference).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Elecrow ThinkNode M8 board support (nRF52840/SX1262, 1.54in e-ink, ATGM336H GNSS, SC7A20, EC04 encoder)

* Address review: keep the stored backlight level out of blanking, match only the SC7A20 WHO_AM_I byte, and transfer detents atomically

* Use std::atomic for the press-and-turn detent counter so native builds compile

* Drop the ThinkNode M8 LED_BUILTIN redefinition that warned on every translation unit

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Göttgens
2026-07-30 15:59:35 +00:00
committed by GitHub
co-authored by Claude Sonnet 4.6 claude[bot]
parent 84322af4e0
commit 597f6767b5
22 changed files with 641 additions and 33 deletions
+40 -6
View File
@@ -35,11 +35,12 @@
#endif
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
#include "graphics/Backlight.h"
#include "input/ButtonThread.h"
#if defined(BUTTON_PIN_TOUCH)
ButtonThread *TouchButtonThread = nullptr;
#if defined(PIN_EINK_EN)
#if HAS_PWM_BACKLIGHT || defined(PIN_EINK_EN)
static bool touchBacklightWasOn = false;
static bool touchBacklightActive = false;
#endif
@@ -241,20 +242,28 @@ void InputBroker::Init()
};
touchConfig.singlePress = INPUT_BROKER_NONE;
touchConfig.longPress = INPUT_BROKER_BACK;
#if defined(PIN_EINK_EN)
// Touch pad drives the backlight on devices with e-ink backlight pin
#if HAS_PWM_BACKLIGHT || defined(PIN_EINK_EN)
// Touch pad drives the backlight on devices that have one
touchConfig.longPress = INPUT_BROKER_NONE;
touchConfig.suppressLeadUpSound = true;
touchConfig.onPress = []() {
touchBacklightWasOn = uiconfig.screen_brightness == 1;
touchBacklightWasOn = uiconfig.screen_brightness > 0;
if (!touchBacklightWasOn) {
#if HAS_PWM_BACKLIGHT
graphics::backlightOn();
#else
digitalWrite(PIN_EINK_EN, HIGH);
#endif
}
touchBacklightActive = true;
};
touchConfig.onRelease = []() {
if (touchBacklightActive && !touchBacklightWasOn) {
#if HAS_PWM_BACKLIGHT
graphics::backlightOff();
#else
digitalWrite(PIN_EINK_EN, LOW);
#endif
}
touchBacklightActive = false;
};
@@ -327,6 +336,30 @@ void InputBroker::Init()
#define BUTTON_ACTIVE_PULLUP true
#endif
#if defined(ELECROW_ThinkNode_M8)
// Rotary encoder drives the UI, so the function button keeps a fixed map.
LOG_DEBUG("ThinkNode_M8 button");
UserButtonThread = new ButtonThread("FunctionButton");
{
ButtonConfig userConfig;
userConfig.pinNumber = (uint8_t)_pinNum;
userConfig.activeLow = BUTTON_ACTIVE_LOW;
userConfig.activePullup = BUTTON_ACTIVE_PULLUP;
userConfig.pullupSense = pullup_sense;
userConfig.intRoutine = []() {
UserButtonThread->userButton.tick();
UserButtonThread->setIntervalFromNow(0);
runASAP = true;
BaseType_t higherWake = 0;
concurrency::mainDelay.interruptFromISR(&higherWake);
};
userConfig.singlePress = INPUT_BROKER_SEND_PING;
userConfig.longPress = INPUT_BROKER_SHUTDOWN;
userConfig.longPressTime = 5000;
userConfig.doublePress = INPUT_BROKER_GPS_TOGGLE;
UserButtonThread->initButton(userConfig);
}
#else
// Buttons. Moved here cause we need NodeDB to be initialized
// If your variant.h has a BUTTON_PIN defined, go ahead and define BUTTON_ACTIVE_LOW and BUTTON_ACTIVE_PULLUP
UserButtonThread = new ButtonThread("UserButton");
@@ -378,8 +411,9 @@ void InputBroker::Init()
userConfigNoScreen.triplePress = INPUT_BROKER_GPS_TOGGLE;
UserButtonThread->initButton(userConfigNoScreen);
}
#endif
#endif
#endif // ELECROW_ThinkNode_M8
#endif // BUTTON_PIN
#endif // HAS_BUTTON
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {