From fc051f728140c011068a94ab744e690366cde47f Mon Sep 17 00:00:00 2001 From: AtlavoxDev Date: Mon, 25 May 2026 11:43:17 -0400 Subject: [PATCH 1/2] Add MainBoard::pollButton() hook and migrate SenseCAP Solar to it Adds `virtual void pollButton()` as a no-op extension point on `mesh::MainBoard`, following the same pattern as `onBootComplete()`. Every example's `loop()` now calls `board.pollButton()` unconditionally once per iteration. Boards with physical buttons override this hook to poll for user input (e.g., hold-to-power-off); boards without buttons inherit the no-op default. Removes the device-specific `#if defined(_SEEED_SENSECAP_SOLAR_H_)` blocks from `examples/simple_repeater/main.cpp` and moves the equivalent hold-1.5s-to-power-off logic into `SenseCapSolarBoard::pollButton()` as an override. Net behavior for SenseCAP Solar is unchanged; the only difference is where the code lives. Files: - src/MeshCore.h: add virtual pollButton() no-op - examples/simple_repeater/main.cpp: remove SenseCAP #ifdef blocks; add unconditional board.pollButton() - examples/companion_radio/main.cpp: add unconditional board.pollButton() - examples/simple_room_server/main.cpp: add unconditional board.pollButton() - examples/kiss_modem/main.cpp: add unconditional board.pollButton() - variants/sensecap_solar/SenseCapSolarBoard.h: add pollButton() override (moved from main.cpp) --- examples/companion_radio/main.cpp | 2 ++ examples/kiss_modem/main.cpp | 2 ++ examples/simple_repeater/main.cpp | 20 +---------------- examples/simple_room_server/main.cpp | 2 ++ src/MeshCore.h | 4 ++++ variants/sensecap_solar/SenseCapSolarBoard.h | 23 ++++++++++++++++++++ 6 files changed, 34 insertions(+), 19 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 31923543fd..f47f38111e 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -243,6 +243,8 @@ void setup() { } void loop() { + board.pollButton(); + the_mesh.loop(); sensors.loop(); #ifdef DISPLAY_CLASS diff --git a/examples/kiss_modem/main.cpp b/examples/kiss_modem/main.cpp index 7fbcaed127..e97f857f75 100644 --- a/examples/kiss_modem/main.cpp +++ b/examples/kiss_modem/main.cpp @@ -124,6 +124,8 @@ void setup() { } void loop() { + board.pollButton(); + modem->loop(); if (!modem->isActuallyTransmitting()) { diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 297337ab5c..0713ed86b6 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -23,11 +23,6 @@ static char command[160]; unsigned long lastActive = 0; // mark last active time unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot -#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) -static unsigned long userBtnDownAt = 0; -#define USER_BTN_HOLD_OFF_MILLIS 1500 -#endif - void setup() { Serial.begin(115200); delay(1000); @@ -134,20 +129,7 @@ void loop() { command[0] = 0; // reset command buffer } -#if defined(PIN_USER_BTN) && defined(_SEEED_SENSECAP_SOLAR_H_) - // Hold the user button to power off the SenseCAP Solar repeater. - int btnState = digitalRead(PIN_USER_BTN); - if (btnState == LOW) { - if (userBtnDownAt == 0) { - userBtnDownAt = millis(); - } else if ((unsigned long)(millis() - userBtnDownAt) >= USER_BTN_HOLD_OFF_MILLIS) { - Serial.println("Powering off..."); - board.powerOff(); // does not return - } - } else { - userBtnDownAt = 0; - } -#endif + board.pollButton(); the_mesh.loop(); sensors.loop(); diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index a3798b2175..e19d6b3096 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -109,6 +109,8 @@ void loop() { command[0] = 0; // reset command buffer } + board.pollButton(); + the_mesh.loop(); sensors.loop(); #ifdef DISPLAY_CLASS diff --git a/src/MeshCore.h b/src/MeshCore.h index b4c57faf32..d495ede8a6 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -56,6 +56,10 @@ class MainBoard { // Boards may override to stop a boot-indicator LED sequence or similar. // Default no-op: boards that don't care need not implement anything. virtual void onBootComplete() { /* no op */ } + // Called once per loop() iteration from example main loops. Boards with + // physical buttons may override to poll for user input (e.g., hold-to- + // power-off). Default no-op: boards without buttons need not implement. + virtual void pollButton() { /* no op */ } virtual void sleep(uint32_t secs) { /* no op */ } virtual uint32_t getGpio() { return 0; } virtual void setGpio(uint32_t values) {} diff --git a/variants/sensecap_solar/SenseCapSolarBoard.h b/variants/sensecap_solar/SenseCapSolarBoard.h index 6799a5e921..2d8baeeaa3 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.h +++ b/variants/sensecap_solar/SenseCapSolarBoard.h @@ -10,10 +10,33 @@ class SenseCapSolarBoard : public NRF52BoardDCDC { void initiateShutdown(uint8_t reason) override; #endif +private: +#ifdef PIN_USER_BTN + unsigned long _btn_down_at = 0; // user-button press timestamp (0 = not pressed) + static constexpr unsigned long USER_BTN_HOLD_OFF_MILLIS = 1500; +#endif + public: SenseCapSolarBoard() : NRF52Board("SENSECAP_SOLAR_OTA") {} void begin(); +#ifdef PIN_USER_BTN + // Hold the user button for >= 1.5s to power off. + void pollButton() override { + int btnState = digitalRead(PIN_USER_BTN); + if (btnState == LOW) { + if (_btn_down_at == 0) { + _btn_down_at = millis(); + } else if ((unsigned long)(millis() - _btn_down_at) >= USER_BTN_HOLD_OFF_MILLIS) { + Serial.println("Powering off..."); + powerOff(); // does not return + } + } else { + _btn_down_at = 0; + } + } +#endif + #if defined(P_LORA_TX_LED) void onBeforeTransmit() override { digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on From b9f694715e11b26959e609f12c56e56dc776d7cc Mon Sep 17 00:00:00 2001 From: AtlavoxDev Date: Mon, 25 May 2026 12:05:43 -0400 Subject: [PATCH 2/2] SenseCap Solar: rename button pins to match silkscreen labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The physical buttons on the SenseCAP Solar are silkscreened PWR / RST / USR (left to right). Previously the firmware aliased PIN_USER_BTN to PIN_BUTTON1 (pin 13), but pin 13 is wired to the PWR button — both the SYSTEMOFF wake source and the hold-to-power-off target. The name "PIN_USER_BTN" was semantically misleading: it pointed at the power button, while the actual labeled USR button (pin 20) was completely unused. Renames so code matches the device silkscreen, keeping the existing project-wide PIN_USER_BTN convention for the user-labeled button: - PIN_PWR_BTN -> pin 13 (PWR, leftmost) [new name] - PIN_USER_BTN -> pin 20 (USR, rightmost) [re-pointed] RST is hardwired to the nRF52 reset line and is not exposed as a GPIO, so only the two firmware-controllable buttons are defined. The previous PIN_BUTTON1 / PIN_BUTTON2 intermediate aliases are removed since no code in the SenseCAP build path references them. The pin numbers are now declared directly against the silkscreen names, matching the simpler pattern used by other variants like thinknode_m2, thinknode_m5, and muziworks_r1_neo. SenseCapSolarBoard now uses PIN_PWR_BTN in the pollButton() override, the powerOff() SENSE-LOW wake config, and the begin() pinMode setup. The dead `#elif defined(PIN_BUTTON1)` fallbacks are removed since PIN_PWR_BTN is always defined for this variant. The redundant `-D PIN_USER_BTN=PIN_BUTTON1` is removed from platformio.ini; variant.h is now the single source of truth for the button pin mapping. Behavior unchanged: pin 13 still wakes the device, still triggers power-off on 1.5 s hold, still gets pull-up configured for SYSTEMOFF. The USR button (pin 20) remains unused but is now correctly named for future use — matching the established PIN_USER_BTN convention used across other variants. --- .../sensecap_solar/SenseCapSolarBoard.cpp | 6 ++-- variants/sensecap_solar/SenseCapSolarBoard.h | 30 ++++++++----------- variants/sensecap_solar/platformio.ini | 1 - variants/sensecap_solar/variant.h | 8 +++-- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/variants/sensecap_solar/SenseCapSolarBoard.cpp b/variants/sensecap_solar/SenseCapSolarBoard.cpp index da7964c9e6..7729752701 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.cpp +++ b/variants/sensecap_solar/SenseCapSolarBoard.cpp @@ -35,10 +35,8 @@ void SenseCapSolarBoard::begin() { analogReference(AR_INTERNAL_3_0); delay(50); -#ifdef PIN_USER_BTN - pinMode(PIN_USER_BTN, INPUT_PULLUP); -#elif defined(PIN_BUTTON1) - pinMode(PIN_BUTTON1, INPUT_PULLUP); +#ifdef PIN_PWR_BTN + pinMode(PIN_PWR_BTN, INPUT_PULLUP); #endif #if defined(PIN_WIRE_SDA) && defined(PIN_WIRE_SCL) diff --git a/variants/sensecap_solar/SenseCapSolarBoard.h b/variants/sensecap_solar/SenseCapSolarBoard.h index 2d8baeeaa3..0772ff43fa 100644 --- a/variants/sensecap_solar/SenseCapSolarBoard.h +++ b/variants/sensecap_solar/SenseCapSolarBoard.h @@ -11,28 +11,28 @@ class SenseCapSolarBoard : public NRF52BoardDCDC { #endif private: -#ifdef PIN_USER_BTN - unsigned long _btn_down_at = 0; // user-button press timestamp (0 = not pressed) - static constexpr unsigned long USER_BTN_HOLD_OFF_MILLIS = 1500; +#ifdef PIN_PWR_BTN + unsigned long _pwr_btn_down_at = 0; // PWR-button press timestamp (0 = not pressed) + static constexpr unsigned long PWR_BTN_HOLD_OFF_MILLIS = 1500; #endif public: SenseCapSolarBoard() : NRF52Board("SENSECAP_SOLAR_OTA") {} void begin(); -#ifdef PIN_USER_BTN - // Hold the user button for >= 1.5s to power off. +#ifdef PIN_PWR_BTN + // Hold the PWR button for >= 1.5s to power off. void pollButton() override { - int btnState = digitalRead(PIN_USER_BTN); + int btnState = digitalRead(PIN_PWR_BTN); if (btnState == LOW) { - if (_btn_down_at == 0) { - _btn_down_at = millis(); - } else if ((unsigned long)(millis() - _btn_down_at) >= USER_BTN_HOLD_OFF_MILLIS) { + if (_pwr_btn_down_at == 0) { + _pwr_btn_down_at = millis(); + } else if ((unsigned long)(millis() - _pwr_btn_down_at) >= PWR_BTN_HOLD_OFF_MILLIS) { Serial.println("Powering off..."); powerOff(); // does not return } } else { - _btn_down_at = 0; + _pwr_btn_down_at = 0; } } #endif @@ -64,14 +64,10 @@ class SenseCapSolarBoard : public NRF52BoardDCDC { digitalWrite(LED_WHITE, LOW); digitalWrite(LED_BLUE, LOW); -#ifdef PIN_USER_BTN - while (digitalRead(PIN_USER_BTN) == LOW); +#ifdef PIN_PWR_BTN + while (digitalRead(PIN_PWR_BTN) == LOW); // Keep pull-up enabled in system-off so the wake line doesn't float low. - nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_USER_BTN]), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); -#elif defined(PIN_BUTTON1) - while (digitalRead(PIN_BUTTON1) == LOW); - // Keep pull-up enabled in system-off so the wake line doesn't float low. - nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_BUTTON1]), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); + nrf_gpio_cfg_sense_input(digitalPinToInterrupt(g_ADigitalPinMap[PIN_PWR_BTN]), NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_SENSE_LOW); #endif #ifdef NRF52_POWER_MANAGEMENT diff --git a/variants/sensecap_solar/platformio.ini b/variants/sensecap_solar/platformio.ini index effef38ccd..fb79d54ca9 100644 --- a/variants/sensecap_solar/platformio.ini +++ b/variants/sensecap_solar/platformio.ini @@ -18,7 +18,6 @@ build_flags = ${nrf52_base.build_flags} -D P_LORA_RESET=2 -D P_LORA_BUSY=3 -D P_LORA_NSS=4 - -D PIN_USER_BTN=PIN_BUTTON1 -D LORA_TX_POWER=22 -D SX126X_RXEN=5 -D SX126X_TXEN=RADIOLIB_NC diff --git a/variants/sensecap_solar/variant.h b/variants/sensecap_solar/variant.h index ef87c7ebd8..0ae39044b9 100644 --- a/variants/sensecap_solar/variant.h +++ b/variants/sensecap_solar/variant.h @@ -30,9 +30,11 @@ #define LED_STATE_ON (1) // State when LED is litted // Buttons -#define PIN_BUTTON1 (13) -#define PIN_BUTTON2 (20) -#define PIN_USER_BTN PIN_BUTTON1 +// Physical buttons are silkscreened PWR / RST / USR (left to right). +// RST is hardwired to the nRF52 reset line and is not exposed as a GPIO, +// so only PWR and USR are defined here. +#define PIN_PWR_BTN (13) // PWR (leftmost): wake source + hold-to-power-off +#define PIN_USER_BTN (20) // USR (rightmost): general-purpose, currently unused #define VBAT_ENABLE (19) // Output LOW to enable reading of the BAT voltage.