rework buttons, add quintuple press for disabling leds

This commit is contained in:
liquidraver
2026-03-16 12:56:10 +01:00
parent e2729553ec
commit 71684af590
12 changed files with 85 additions and 16 deletions
+18
View File
@@ -0,0 +1,18 @@
/*
* Common: configure the 'buttons' gpio-keys node as a wakeup source.
*
* On nRF52840, sys_poweroff() enters System OFF (~1µA). Waking via the user
* button requires GPIO SENSE bits to be set before poweroff. Zephyr's gpio-keys
* driver adds GPIO_INT_WAKEUP to the interrupt flags when wakeup-source is set,
* which causes the nRF52 GPIO driver to call nrf_gpio_cfg_sense_set(). Those
* SENSE bits persist through System OFF and trigger a reset on button press.
*
* Include this in any nRF52 board DTS that defines a 'buttons' gpio-keys node.
* Boards without buttons (e.g. ikoka_nano, rak4631) should NOT include this.
*
* SPDX-License-Identifier: Apache-2.0
*/
&buttons {
wakeup-source;
};
+15
View File
@@ -157,6 +157,21 @@ If you write a fully custom DTS that includes neither, add it yourself:
};
**nRF52 boards with a user button: button wakeup from System OFF**
`sys_poweroff()` on nRF52840 enters System OFF (~1µA). To allow waking via
button press (instead of only via USB/charger), GPIO SENSE bits must be set
before poweroff. This is done via the `wakeup-source` property on the
`gpio-keys` node, which the nRF52 GPIO driver handles automatically.
Boards that define a `buttons:` gpio-keys node must add at the END of their DTS:
#include "../../common/nrf52_wakeup.dtsi"
Boards WITHOUT a `buttons` label (ikoka_nano, rak4631) must NOT include it —
referencing an undefined `&buttons` label is a hard build error.
What Goes in board.conf
-----------------------
+16 -3
View File
@@ -659,6 +659,8 @@
* / {
* buttons: buttons {
* compatible = "gpio-keys";
* /* nRF52 only: omit wakeup-source here; use the common include below instead.
* * Other platforms: add wakeup-source; directly if the driver supports it. */
* user_button: button_0 {
* gpios = <&gpio0 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
* zephyr,code = <INPUT_KEY_0>;
@@ -669,18 +671,29 @@
* compatible = "zephyr,input-longpress";
* input = <&buttons>;
* input-codes = <INPUT_KEY_0>;
* short-codes = <INPUT_KEY_A>;
* long-codes = <INPUT_KEY_POWER>;
* short-codes = <INPUT_KEY_A>; /* short press → multi-tap input */
* long-codes = <INPUT_KEY_POWER>; /* long press (≥1s) → deep sleep */
* long-delay-ms = <1000>;
* };
*
* /* Multi-tap action mapping (standard ZephCore assignment):
* * 1 tap (400ms wait) → KEY_1 = page next
* * 2 taps (400ms wait) → KEY_B = flood advert
* * 3 taps (400ms wait) → KEY_D = buzzer mute toggle
* * 4 taps (400ms wait) → KEY_C = GPS on/off
* * 5 taps (immediate) → KEY_E = LED heartbeat toggle */
* user_btn_multitap {
* compatible = "zephcore,input-multi-tap";
* input-codes = <INPUT_KEY_A>;
* tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
* tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
* tap-delay-ms = <400>;
* };
* };
*
* /* nRF52 boards: add this at the END of the DTS file so the button GPIO
* * is configured for System OFF wakeup (press button to boot after shutdown).
* * Do NOT add for boards without a 'buttons' label (ikoka_nano, rak4631). */
* #include "../../common/nrf52_wakeup.dtsi"
*/
/* --- GPS on UART (nRF52840 typically on &uart0) ---
@@ -203,3 +203,4 @@ zephyr_udc0: &usbd {
/* ---- Flash partitions (SoftDevice v6, app@0x26000) ---- */
#include "../../common/nrf52_partitions_sdv6.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -95,11 +95,12 @@
* 1 tap → KEY_1 = (reserved / next page if display added)
* 2 taps → KEY_B = flood advert
* 3 taps → KEY_D = buzzer mute toggle
* 4 taps → KEY_C = GPS on/off */
* 4 taps → KEY_C = GPS on/off
* 5 taps → KEY_E = LED heartbeat toggle */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
tap-delay-ms = <400>;
};
@@ -277,3 +278,4 @@ zephyr_udc0: &usbd {
/* ---- Flash partitions (SoftDevice v6, app@0x26000) ---- */
#include "../../common/nrf52_partitions_sdv6.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -76,14 +76,15 @@
* 1 tap (400ms wait) → KEY_1 = page next (no-op without display)
* 2 taps (400ms wait) → KEY_B = flood advert
* 3 taps (400ms wait) → KEY_D = buzzer mute toggle
* 4 taps (immediate) → KEY_C = GPS on/off */
* 4 taps (400ms wait) → KEY_C = GPS on/off
* 5 taps (immediate) → KEY_E = LED heartbeat toggle */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
/* No 'input' phandle — listen to ALL devices.
* KEY_A is emitted by the longpress pseudo-device,
* not by &buttons, so we must not filter by device. */
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
tap-delay-ms = <400>;
};
@@ -279,3 +280,4 @@ zephyr_udc0: &usbd {
/* Arduino MeshCore compatible partition layout (SoftDevice v7) */
#include "../../common/nrf52_partitions_sdv7.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -389,3 +389,4 @@ zephyr_udc0: &usbd {
/* ---- External QSPI /ext automount ---- */
#include "../../common/qspi-ext.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -82,11 +82,12 @@
* 1 tap → KEY_1 = page next
* 2 taps → KEY_B = flood advert
* 3 taps → KEY_D = buzzer mute toggle
* 4 taps → KEY_C = GPS on/off */
* 4 taps → KEY_C = GPS on/off
* 5 taps → KEY_E = LED heartbeat toggle */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
tap-delay-ms = <400>;
};
@@ -255,3 +256,4 @@ zephyr_udc0: &usbd {
/* SoftDevice v6 partition layout (S140 v6 — Adafruit nRF52 bootloader) */
#include "../../common/nrf52_partitions_sdv6.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -81,11 +81,12 @@
* 1 tap → KEY_1 = page next
* 2 taps → KEY_B = flood advert
* 3 taps → KEY_D = buzzer mute toggle
* 4 taps → KEY_C = GPS on/off */
* 4 taps → KEY_C = GPS on/off
* 5 taps → KEY_E = LED heartbeat toggle */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
tap-delay-ms = <400>;
};
@@ -323,3 +324,4 @@ zephyr_udc0: &usbd {
/* External QSPI /ext automount */
#include "../../common/qspi-ext.dtsi"
#include "../../common/nrf52_wakeup.dtsi"
@@ -103,14 +103,15 @@
* 1 tap (400ms wait) → KEY_1 = page next on OLED
* 2 taps (400ms wait) → KEY_B = flood advert
* 3 taps (400ms wait) → KEY_D = buzzer mute toggle
* 4 taps (immediate) → KEY_C = GPS on/off */
* 4 taps (400ms wait) → KEY_C = GPS on/off
* 5 taps (immediate) → KEY_E = LED heartbeat toggle */
user_btn_multitap {
compatible = "zephcore,input-multi-tap";
/* No 'input' phandle — listen to ALL devices.
* KEY_A is emitted by the longpress pseudo-device,
* not by &buttons, so we must not filter by device. */
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_B INPUT_KEY_D INPUT_KEY_C INPUT_KEY_E>;
tap-delay-ms = <400>;
};
@@ -340,3 +341,4 @@ zephyr_udc0: &usbd {
};
};
};
#include "../../common/nrf52_wakeup.dtsi"
+2 -2
View File
@@ -133,8 +133,8 @@ static int __maybe_unused multi_tap_init(const struct device *dev)
"input-codes must have at least 1 entry"); \
BUILD_ASSERT(DT_INST_PROP_LEN(inst, tap_codes) >= 1, \
"tap-codes must have at least 1 entry"); \
BUILD_ASSERT(DT_INST_PROP_LEN(inst, tap_codes) <= 4, \
"tap-codes must have at most 4 entries"); \
BUILD_ASSERT(DT_INST_PROP_LEN(inst, tap_codes) <= 5, \
"tap-codes must have at most 5 entries"); \
\
INPUT_CALLBACK_DEFINE_NAMED( \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, input), \
+12 -1
View File
@@ -76,6 +76,9 @@ LOG_MODULE_REGISTER(ui_task, CONFIG_ZEPHCORE_BOARD_LOG_LEVEL);
#define MELODY_GPS_ON "gon:d=16,o=7,b=200:c,p,c,p,c,p,c,p,p,8e"
#define MELODY_GPS_OFF "gof:d=16,o=7,b=200:c,p,c,p,c,p,c,p,p,8g5"
#define MELODY_LED_ON "lon:d=16,o=7,b=200:c,p,c,p,c,p,c,p,c,p,p,8e"
#define MELODY_LED_OFF "lof:d=16,o=7,b=200:c,p,c,p,c,p,c,p,c,p,p,8g5"
/* ========== LED Heartbeat ========== */
/* Match Arduino: 4s cycle, 20ms pulse (normal) or 200ms (unread messages).
* Uses led0 or led1 alias — whichever exists in the board's DTS.
@@ -475,6 +478,9 @@ static void action_leds_toggle(void)
s->leds_disabled = new_disabled;
ui_set_heartbeat_led(!new_disabled);
mesh_set_leds_disabled(new_disabled);
#ifdef CONFIG_ZEPHCORE_UI_BUZZER
buzzer_play(new_disabled ? MELODY_LED_OFF : MELODY_LED_ON);
#endif
LOG_INF("LEDs %s (user toggle)", new_disabled ? "disabled" : "enabled");
schedule_render();
}
@@ -698,10 +704,15 @@ static void ui_input_cb(struct input_event *evt, void *user_data)
break;
case INPUT_KEY_C:
/* Quadruple tap (immediate): toggle GPS */
/* Quadruple tap (400ms delayed): toggle GPS */
action_gps_toggle();
break;
case INPUT_KEY_E:
/* Quintuple tap (immediate): toggle LED heartbeat */
action_leds_toggle();
break;
/* ===== Longpress output ===== */
case INPUT_KEY_POWER:
case INPUT_KEY_F: