heltec v3/v4 oled&button changes

This commit is contained in:
liquidraver
2026-04-16 13:40:30 +02:00
parent e71c3894a2
commit cba81af196
6 changed files with 171 additions and 181 deletions
@@ -1,126 +1,134 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Heltec WiFi LoRa 32 V3 — ZephCore overlay
*
* The upstream Zephyr DTS already defines SX1262, SSD1306 OLED, button,
* LED, battery ADC, Vext/ADC control, I2C0, SPI2, BLE, and WiFi.
* This overlay adds ZephCore-specific integration on top.
*
* LoRa SPI2 pins: SCLK=9, MOSI=10, MISO=11, NSS=8
* LoRa control: DIO1=14, BUSY=13, RESET=12
* Display I2C0: SDA=17, SCL=18 (SSD1306 @ 0x3C)
* Battery ADC: GPIO1 (ADC1 ch0), enable GPIO37, divider 100K/390K
* Vext: GPIO36 (active-LOW, powers OLED)
* Button: GPIO0
* LED: GPIO35
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
chosen {
/* Keep console/shell on UART0 for stable host logging on V3 */
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
};
/* Add label to upstream buttons node (defined without one) */
buttons: buttons {
};
/* Battery ADC — ZephCore reads via zephyr,user { io-channels }
* Upstream vbatt node uses voltage-divider compatible which ZephCore
* doesn't use. Multiplier: 3300mV full-scale * 5.42x = 17886.
* (5.42x is Arduino's empirical divider factor for V3/V4.) */
zephyr,user {
io-channels = <&adc1 0>;
vbat-mv-multiplier = <17886>;
};
/* Battery ADC enable — GPIO37 (gpio1 pin 5) must be driven to read.
* Upstream has adc_ctrl power-domain-gpio; ZephCore expects a
* regulator-fixed named vbat_enable. */
vbat_enable: vbat-enable {
compatible = "regulator-fixed";
regulator-name = "vbat-enable";
enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
};
/* ---- Button input filter chain ----
* Upstream button emits INPUT_KEY_0. Split into:
* short press → KEY_A (consumed by multi-tap)
* long press (1s) → KEY_POWER (deep sleep) */
user_btn_longpress {
compatible = "zephyr,input-longpress";
input = <&buttons>;
input-codes = <INPUT_KEY_0>;
short-codes = <INPUT_KEY_A>;
long-codes = <INPUT_KEY_POWER>;
long-delay-ms = <1000>;
};
/* Multi-tap decodes short presses:
* 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 (immediate) → KEY_C = GPS on/off */
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-delay-ms = <400>;
};
};
/* Enable native USB serial for console/CLI */
&usb_serial {
status = "okay";
};
/* Add RX boosted mode and DIO1 pull-down to LoRa radio.
* Upstream omits rx-boosted and has no pull-down on DIO1. */
&lora0 {
dio1-gpios = <&gpio0 14 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
rx-boosted;
};
/* Reduce CPU clock from 240 MHz to 160 MHz — saves power, HAL minimum */
&cpu0 {
clock-frequency = <DT_FREQ_M(160)>;
};
&cpu1 {
clock-frequency = <DT_FREQ_M(160)>;
};
/* I2C sensors — auto-detected at runtime */
&i2c0 {
#include "../../common/sensors-i2c.dtsi"
};
/*
* Flash partitions — reclaim unused space from 8MB layout.
* Delete lpcore slots, storage, scratch, and coredump partitions.
* Repurpose 0x7A0000 - 0x800000 (384KB) for LittleFS.
*/
/delete-node/ &slot0_lpcore_partition;
/delete-node/ &slot1_lpcore_partition;
/delete-node/ &storage_partition;
/delete-node/ &scratch_partition;
/delete-node/ &coredump_partition;
&flash0 {
partitions {
/* LittleFS 384KB for DataStore + file-based BLE settings */
lfs_partition: partition@7a0000 {
label = "lfs";
reg = <0x7A0000 0x60000>;
};
};
};
/* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi"
/*
* SPDX-License-Identifier: Apache-2.0
* Heltec WiFi LoRa 32 V3 — ZephCore overlay
*
* The upstream Zephyr DTS already defines SX1262, SSD1306 OLED, button,
* LED, battery ADC, Vext/ADC control, I2C0, SPI2, BLE, and WiFi.
* This overlay adds ZephCore-specific integration on top.
*
* LoRa SPI2 pins: SCLK=9, MOSI=10, MISO=11, NSS=8
* LoRa control: DIO1=14, BUSY=13, RESET=12
* Display I2C0: SDA=17, SCL=18 (SSD1306 @ 0x3C)
* Battery ADC: GPIO1 (ADC1 ch0), enable GPIO37, divider 100K/390K
* Vext: GPIO36 (active-LOW, powers OLED) — regulator + vin-supply in overlay
* Button: GPIO0
* LED: GPIO35
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
chosen {
/* Keep console/shell on UART0 for stable host logging on V3 */
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
};
/* Add label to upstream buttons node (defined without one) */
buttons: buttons {
};
/* Battery ADC — ZephCore reads via zephyr,user { io-channels }
* Upstream vbatt node uses voltage-divider compatible which ZephCore
* doesn't use. Multiplier: 3300mV full-scale * 5.42x = 17886.
* (5.42x is Arduino's empirical divider factor for V3/V4.) */
zephyr,user {
io-channels = <&adc1 0>;
vbat-mv-multiplier = <17886>;
};
/* Battery ADC enable — GPIO37 (gpio1 pin 5) must be driven to read.
* Upstream has adc_ctrl power-domain-gpio; ZephCore expects a
* regulator-fixed named vbat_enable. */
vbat_enable: vbat-enable {
compatible = "regulator-fixed";
regulator-name = "vbat-enable";
enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
};
/* Vext — GPIO36, active-LOW enables rail (matches Arduino periph_power / MeshCore
* PIN_VEXT_EN). Replaces upstream vext_ctrl power-domain so SSD1306 init works with
* CONFIG_PM_DEVICE=n. */
vext_enable: vext-enable {
compatible = "regulator-fixed";
regulator-name = "vext";
enable-gpios = <&gpio1 4 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
regulator-boot-on;
regulator-always-on;
};
/* Button chain — same mapping as RAK4631 / WisMesh Pocket (rak4631/board.overlay). */
user_btn_longpress {
compatible = "zephyr,input-longpress";
input = <&buttons>;
input-codes = <INPUT_KEY_0>;
short-codes = <INPUT_KEY_A>;
long-codes = <INPUT_KEY_ENTER>;
long-delay-ms = <1000>;
};
page_btn_multitap {
compatible = "zephcore,input-multi-tap";
input-codes = <INPUT_KEY_A>;
tap-codes = <INPUT_KEY_1 INPUT_KEY_LEFT>;
tap-delay-ms = <400>;
};
};
/* Enable native USB serial for console/CLI */
&usb_serial {
status = "okay";
};
/* Add RX boosted mode and DIO1 pull-down to LoRa radio.
* Upstream omits rx-boosted and has no pull-down on DIO1. */
&lora0 {
dio1-gpios = <&gpio0 14 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>;
rx-boosted;
};
/* Reduce CPU clock from 240 MHz to 160 MHz — saves power, HAL minimum */
&cpu0 {
clock-frequency = <DT_FREQ_M(160)>;
};
&cpu1 {
clock-frequency = <DT_FREQ_M(160)>;
};
/* I2C sensors — auto-detected at runtime */
&i2c0 {
#include "../../common/sensors-i2c.dtsi"
ssd1306_128x64: display-controller@3c {
/delete-property/ power-domains;
vin-supply = <&vext_enable>;
};
};
/*
* Flash partitions — reclaim unused space from 8MB layout.
* Delete lpcore slots, storage, scratch, and coredump partitions.
* Repurpose 0x7A0000 - 0x800000 (384KB) for LittleFS.
*/
/delete-node/ &slot0_lpcore_partition;
/delete-node/ &slot1_lpcore_partition;
/delete-node/ &storage_partition;
/delete-node/ &scratch_partition;
/delete-node/ &coredump_partition;
&flash0 {
partitions {
/* LittleFS 384KB for DataStore + file-based BLE settings */
lfs_partition: partition@7a0000 {
label = "lfs";
reg = <0x7A0000 0x60000>;
};
};
};
/* LittleFS auto-mount — standard /lfs mount point */
#include "../../common/filesystem.dtsi"
/* After SSD1306 no longer references vext_ctrl, remove upstream power-domain node. */
/delete-node/ &vext_ctrl;
@@ -3,7 +3,7 @@
* Heltec WiFi LoRa 32 V4 — ZephCore overlay
*
* The DTS already defines: SX1262 (lora0), SSD1306 OLED, button, LED, PWM,
* vext_ctrl, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi.
* vext_enable, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi.
* This overlay adds ZephCore-specific integration on top.
*
* LoRa SPI2: SCLK=9, MOSI=10, MISO=11, NSS=8
@@ -43,30 +43,20 @@
enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; /* GPIO37 = gpio1 pin 5 */
};
/* ---- Button input filter chain ----
* Button emits INPUT_KEY_0. Split into:
* short press → KEY_A (consumed by multi-tap)
* long press (1 s) → KEY_POWER (deep sleep) */
/* Button chain — same mapping as RAK4631 / WisMesh Pocket (rak4631/board.overlay). */
user_btn_longpress {
compatible = "zephyr,input-longpress";
input = <&buttons>;
input-codes = <INPUT_KEY_0>;
short-codes = <INPUT_KEY_A>;
long-codes = <INPUT_KEY_POWER>;
long-codes = <INPUT_KEY_ENTER>;
long-delay-ms = <1000>;
};
/* Multi-tap decodes short presses:
* 1 tap (400 ms wait) → KEY_1 = page next
* 2 taps (400 ms wait) → KEY_B = flood advert
* 3 taps (400 ms wait) → KEY_D = buzzer mute toggle
* 4 taps (immediate) → KEY_C = GPS on/off */
user_btn_multitap {
page_btn_multitap {
compatible = "zephcore,input-multi-tap";
/* No 'input' phandle — listen to ALL devices.
* KEY_A is emitted by the longpress pseudo-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_LEFT>;
tap-delay-ms = <400>;
};
};
@@ -85,14 +85,15 @@
full-ohms = <(100000 + 390000)>;
};
/* Vext — powers OLED and external peripherals.
* V4 DIFFERENCE: active-HIGH (GPIO36 HIGH = power on).
* V3 was active-LOW on the same GPIO. */
vext_ctrl: vext_ctrl {
compatible = "power-domain-gpio";
/* Vext — powers OLED and external peripherals (active-HIGH on GPIO36).
* regulator-fixed + SSD1306 vin-supply: powers rail at boot without relying on
* power-domains (CONFIG_PM_DEVICE is disabled in prj.conf). */
vext_enable: vext-enable {
compatible = "regulator-fixed";
regulator-name = "vext";
enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; /* GPIO36 = gpio1 pin 4 */
#power-domain-cells = <0>;
label = "Vext Control";
regulator-boot-on;
regulator-always-on;
};
/* ADC enable — GPIO37 (gpio1 pin 5) must be driven HIGH to read battery.
@@ -186,7 +187,7 @@
inversion-on;
prechargep = <0x22>;
reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; /* RST_OLED = GPIO21 */
power-domains = <&vext_ctrl>;
vin-supply = <&vext_enable>;
};
};
@@ -3,7 +3,7 @@
* Heltec WiFi LoRa 32 V4.3 — ZephCore overlay
*
* The DTS already defines: SX1262 (lora0), SSD1306 OLED, button, LED, PWM,
* vext_ctrl, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi.
* vext_enable, adc_ctrl, fem_power, I2C0, SPI2, BLE, WiFi.
* This overlay adds ZephCore-specific integration on top.
*
* LoRa SPI2: SCLK=9, MOSI=10, MISO=11, NSS=8
@@ -43,30 +43,20 @@
enable-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; /* GPIO37 = gpio1 pin 5 */
};
/* ---- Button input filter chain ----
* Button emits INPUT_KEY_0. Split into:
* short press → KEY_A (consumed by multi-tap)
* long press (1 s) → KEY_POWER (deep sleep) */
/* Button chain — same mapping as RAK4631 / WisMesh Pocket (rak4631/board.overlay). */
user_btn_longpress {
compatible = "zephyr,input-longpress";
input = <&buttons>;
input-codes = <INPUT_KEY_0>;
short-codes = <INPUT_KEY_A>;
long-codes = <INPUT_KEY_POWER>;
long-codes = <INPUT_KEY_ENTER>;
long-delay-ms = <1000>;
};
/* Multi-tap decodes short presses:
* 1 tap (400 ms wait) → KEY_1 = page next
* 2 taps (400 ms wait) → KEY_B = flood advert
* 3 taps (400 ms wait) → KEY_D = buzzer mute toggle
* 4 taps (immediate) → KEY_C = GPS on/off */
user_btn_multitap {
page_btn_multitap {
compatible = "zephcore,input-multi-tap";
/* No 'input' phandle — listen to ALL devices.
* KEY_A is emitted by the longpress pseudo-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_LEFT>;
tap-delay-ms = <400>;
};
};
@@ -84,12 +84,15 @@
full-ohms = <(100000 + 390000)>;
};
/* Vext — powers OLED and external peripherals (active-HIGH, same as V4.2) */
vext_ctrl: vext_ctrl {
compatible = "power-domain-gpio";
/* Vext — powers OLED and external peripherals (active-HIGH on GPIO36).
* regulator-fixed + SSD1306 vin-supply: powers rail at boot without relying on
* power-domains (CONFIG_PM_DEVICE is disabled in prj.conf). */
vext_enable: vext-enable {
compatible = "regulator-fixed";
regulator-name = "vext";
enable-gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; /* GPIO36 = gpio1 pin 4 */
#power-domain-cells = <0>;
label = "Vext Control";
regulator-boot-on;
regulator-always-on;
};
/* ADC enable — GPIO37 (gpio1 pin 5) must be driven HIGH to read battery */
@@ -182,7 +185,7 @@
inversion-on;
prechargep = <0x22>;
reset-gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; /* RST_OLED = GPIO21 */
power-domains = <&vext_ctrl>;
vin-supply = <&vext_enable>;
};
};
+9 -11
View File
@@ -8,14 +8,14 @@
*
* Input flow (after longpress + multi-tap filter chain):
* KEY_1 → action_page_next() (1 tap, 400ms delayed)
* KEY_B → action_flood_advert() (2 taps, 400ms delayed)
* KEY_D → action_buzzer_toggle() (3 taps, 400ms delayed)
* KEY_LEFT → action_page_prev() (2 taps — RAK4631 / Pocket / Heltec V3V4.3)
* KEY_B → action_flood_advert() (2 taps on extended multitap overlays)
* KEY_D → action_buzzer_toggle() (3 taps)
* KEY_C → action_gps_toggle() (4 taps, immediate)
* KEY_G → GPS switch on/off (hardware toggle, ThinkNode M1)
* KEY_POWER → action_deep_sleep() (long press ≥1s)
* KEY_RIGHT → action_page_next() (joystick, Wio Tracker)
* KEY_LEFT → action_page_prev() (joystick, Wio Tracker)
* KEY_ENTER → action_page_enter() (joystick center, Wio Tracker)
* KEY_POWER / KEY_F → action_deep_sleep() (long press — boards that emit these)
* KEY_ENTER → action_page_enter() (long press — Pocket / Heltec; joystick center Wio)
* KEY_RIGHT → action_page_next() (joystick, Wio Tracker)
*
* Notification flow:
* LoRa RX → CompanionMesh → ui_notify(UI_EVENT_CONTACT_MSG)
@@ -753,11 +753,9 @@ static void ui_input_cb(struct input_event *evt, void *user_data)
/* Map input key codes to UI actions.
*
* KEY_1: 1 tap (from multi-tap, 400ms delayed)
* KEY_B: 2 taps (from multi-tap, 400ms delayed)
* KEY_D: 3 taps (from multi-tap, 400ms delayed)
* KEY_C: 4 taps (from multi-tap, immediate — max count)
* KEY_POWER: long press (from longpress filter, ≥1s)
* RAK4631 / WisMesh Pocket / Heltec V3V4.3: 1 tap KEY_1, 2 taps KEY_LEFT;
* long press KEY_ENTER (page enter). Other boards: up to 45 tap codes
* (KEY_B/D/C/E) and KEY_POWER or KEY_F long → deep sleep.
* KEY_RIGHT/LEFT/ENTER/UP/DOWN: joystick (Wio Tracker)
*
* NOTE: KEY_A (raw short-press from longpress filter) is NOT handled