From 3c4c548c8eeaee398b33a6e1b2cdf2e8d683f951 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:47:27 +0200 Subject: [PATCH] add lilygo t-impulse plus --- zephcore/CMakeLists.txt | 2 +- zephcore/boards/example_board/README.md | 1 + .../Kconfig.lilygo_timpulse_plus | 15 + .../nrf52840/lilygo_timpulse_plus/README.md | 92 +++++ .../nrf52840/lilygo_timpulse_plus/board.cmake | 13 + .../nrf52840/lilygo_timpulse_plus/board.conf | 37 ++ .../lilygo_timpulse_plus/board.overlay | 7 + .../nrf52840/lilygo_timpulse_plus/board.yml | 9 + .../lilygo_timpulse_plus-pinctrl.dtsi | 118 ++++++ .../lilygo_timpulse_plus.dts | 368 ++++++++++++++++++ .../lilygo_timpulse_plus_defconfig | 29 ++ 11 files changed, 690 insertions(+), 1 deletion(-) create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/Kconfig.lilygo_timpulse_plus create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/README.md create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/board.cmake create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/board.conf create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/board.overlay create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/board.yml create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus-pinctrl.dtsi create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus.dts create mode 100644 zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus_defconfig diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index d3a90d8..300f82b 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -265,7 +265,7 @@ set(ZEPHCORE_COMMON_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/zephcore_com # Detect platform from board name and add platform-specific common config # For boards with qualifiers like "wio_tracker_l1/nrf52840", check both the full BOARD # string and the BOARD_QUALIFIERS which contains just the qualifier (e.g., "nrf52840") -if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "rak3401_1watt" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021" OR BOARD MATCHES "promicro_sx1262" OR BOARD MATCHES "sensecap_solar" OR BOARD MATCHES "xiao_nrf52840" OR BOARD MATCHES "lilygo_techo" OR BOARD MATCHES "heltec_t114" OR BOARD MATCHES "heltec_t096" OR BOARD MATCHES "gat562") +if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wismesh_tag" OR BOARD MATCHES "rak3401_1watt" OR BOARD MATCHES "wio_tracker" OR BOARD MATCHES "ikoka_nano" OR BOARD MATCHES "t1000_e" OR BOARD MATCHES "thinknode_m1" OR BOARD MATCHES "thinknode_m3" OR BOARD MATCHES "thinknode_m6" OR BOARD MATCHES "promicro_lr2021" OR BOARD MATCHES "promicro_sx1262" OR BOARD MATCHES "sensecap_solar" OR BOARD MATCHES "xiao_nrf52840" OR BOARD MATCHES "lilygo_techo" OR BOARD MATCHES "lilygo_timpulse_plus" OR BOARD MATCHES "heltec_t114" OR BOARD MATCHES "heltec_t096" OR BOARD MATCHES "gat562") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*nrf52.*") set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/nrf52_common.conf") diff --git a/zephcore/boards/example_board/README.md b/zephcore/boards/example_board/README.md index 592b9ef..fdd8fd2 100644 --- a/zephcore/boards/example_board/README.md +++ b/zephcore/boards/example_board/README.md @@ -23,6 +23,7 @@ Supported Boards | Ikoka Nano 30dBm | `west build -b ikoka_nano_30dbm zephcore` | UF2 drag-drop | | GAT562 30S Mesh Kit | `west build -b gat562_30s zephcore` | UF2 drag-drop or `west flash` | | LilyGo T-Echo | `west build -b lilygo_techo zephcore` | UF2 drag-drop or `west flash` | +| LilyGo T-Impulse Plus | `west build -b lilygo_timpulse_plus zephcore` | UF2 drag-drop or `west flash` | | Heltec T114 | `west build -b heltec_t114 zephcore` | UF2 drag-drop or `west flash` | | Heltec Mesh Node T096 | `west build -b heltec_t096 zephcore` | UF2 drag-drop or `west flash` | diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/Kconfig.lilygo_timpulse_plus b/zephcore/boards/nrf52840/lilygo_timpulse_plus/Kconfig.lilygo_timpulse_plus new file mode 100644 index 0000000..34e5ca3 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/Kconfig.lilygo_timpulse_plus @@ -0,0 +1,15 @@ +# LilyGo T-Impulse Plus board configuration +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +config BOARD_LILYGO_TIMPULSE_PLUS + select SOC_NRF52840_QIAA + +# The RT9080 LDO (P0.14) gates the whole 3V3 peripheral rail — SX1262, the +# SSD1315 OLED, the GPS and the QSPI flash all sit behind it. The fixed +# regulator that drives it (pwr_enable) must come up before those devices +# initialise. QSPI NOR init priority is 41, so pin the regulator ahead of it. +if REGULATOR_FIXED +config REGULATOR_FIXED_INIT_PRIORITY + default 30 +endif diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/README.md b/zephcore/boards/nrf52840/lilygo_timpulse_plus/README.md new file mode 100644 index 0000000..fd6ea4e --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/README.md @@ -0,0 +1,92 @@ +# LilyGo T-Impulse Plus + +A LoRa wristband/tracker built on the **nRF52840 + SX1262**. ZephCore is the +first mesh firmware ported to it — there is no upstream Meshtastic or MeshCore +variant. The port is derived from LilyGo's official +[T-Impulse-Plus repo](https://github.com/Xinyuan-LilyGO/T-Impulse-Plus) +(`pin_config.h`, `variant.h`, RadioLib examples and the shipped datasheets). + +## Build + +```bash +# Companion (BLE — default role, recommended for the wristband form factor) +west build -b lilygo_timpulse_plus zephcore --pristine + +# Repeater (USB-CDC CLI, no BLE) +west build -b lilygo_timpulse_plus zephcore --pristine -- \ + -DEXTRA_CONF_FILE="boards/common/repeater.conf" + +# Companion + debug logging +west build -b lilygo_timpulse_plus zephcore --pristine -- \ + -DEXTRA_CONF_FILE="boards/common/debug.conf" +``` + +Flash: drag `build/zephyr/zephyr.uf2` onto the UF2 drive (double-tap the BOOT +button to enter the Adafruit bootloader), or `west flash` with a J-Link. +Bootloader is Adafruit UF2 / SoftDevice **s140 v6.1.1**, app @ `0x26000` +(`SD_FWID 0x00B6`) — the stock bootloader is reused, no re-flash needed. + +## Hardware + +| Function | Part | Bus / notes | +|-----------------|--------------------|-----------------------------------------------| +| MCU | nRF52840 | BLE5, native USB, 1 MB flash / 256 KB RAM | +| LoRa | SX1262 (AcSip S62F)| 22 dBm, embedded 32 MHz TCXO on DIO3 | +| Display | SSD1315 128x64 OLED| I2C0 @ 0x3C (SSD1306-compatible, no reset) | +| GNSS | u-blox MIA-M10Q | UART0 @ 38400, enable P1.10 | +| Ext flash | ZD25WQ32C 32 Mbit | QSPI -> `/ext` LittleFS (contacts/channels) | +| IMU | ICM-20948 | 2nd I2C bus @ 0x69 — present, not used | +| Charger | SGM41562 | 2nd I2C bus @ 0x03 — present, not used | +| Battery | ADC AIN3 (P0.05) | divider enable P0.25 (HIGH to read) | +| Input | TTP223 touch | P1.04, active-HIGH | +| Haptic | vibration motor | P0.22 (PWM0) — exposed as the buzzer output | +| Peripheral rail | RT9080 LDO | enable P0.14 (gates LoRa/OLED/GPS/QSPI) | + +## Pin map (from LilyGo `pin_config.h`) + +| Signal | nRF52840 | Signal | nRF52840 | +|-------------------|----------|---------------------|----------| +| LoRa SCK | P0.03 | LoRa CS | P1.14 | +| LoRa MOSI | P0.28 | LoRa RST | P0.02 | +| LoRa MISO | P0.30 | LoRa BUSY | P0.31 | +| LoRa DIO1 | P0.29 | RF TX enable (VC1) | P1.13 | +| RF RX enable (VC2)| P1.07 | OLED SDA / SCL | P0.20 / P0.15 | +| GPS MCU-TX / RX | P1.11 / P1.12 | GPS enable | P1.10 | +| QSPI SCK / CS | P0.04 / P0.12 | QSPI IO0/1/2/3 | P0.06 / P1.09 / P0.08 / P0.26 | +| Battery ADC / en | P0.05 / P0.25 | RT9080 rail en | P0.14 | +| Touch button | P1.04 | Haptic motor | P0.22 | +| LED (blue) | P0.17 | BOOT (DFU only) | P0.24 | + +### Notes worth knowing +- **RF switch is GPIO, not DIO2.** The S62F uses an external DPDT antenna + switch on VC1/VC2. Wired via the patched native sx126x driver's + `tx-enable-gpios` (VC1) / `rx-enable-gpios` (VC2). DIO2 (P1.15) is unused. +- **GPS RX/TX are swapped relative to the net names.** LilyGo's `GPS_UART_RX`/ + `GPS_UART_TX` are labelled from the GPS module's side; the MCU receive pin is + `GPS_UART_TX` (P1.12) and the MCU transmit pin is `GPS_UART_RX` (P1.11). +- **Single-button UI** (like T-Echo): short tap = next page, double tap = + previous page, long press (>=1 s) = ENTER. Confirmation needs two ENTERs, so + `UI_CONFIRM_WINDOW_MS` is widened to 3 s. +- **The ICM-20948 IMU and SGM41562 charger** sit on a second I2C bus + (SDA P1.08 / SCL P0.11) that ZephCore does not use — that bus is left + disabled. Battery state comes from the ADC, not the charger IC. + +## To verify on real hardware + +These were derived from the vendor sources and build clean, but a physical +unit should confirm: + +1. **TCXO voltage** — set to 1.8 V. LilyGo's RadioLib example validated the + 1.6 V default; drop `dio3-tcxo-voltage` to `SX126X_DIO3_TCXO_1V6` if the + radio fails to start. +2. **Battery divider ratio** — `vbat-mv-multiplier` assumes 2:1 (7236). Adjust + if the reported voltage is off. +3. **OLED orientation** — standard 0.96" landscape config; flip + `segment-remap`/`com-invdir` in the overlay if the panel is mounted rotated. +4. **GPS baud** — 38400 (from the vendor example); adjust `current-speed` if + the M10 was left at another rate. + +The QSPI flash needs no verification: the ZD25WQ32C JEDEC id (`ba 60 16`) and +deep-power-down timings are datasheet-confirmed, and `/ext` reuses the same +`nordic,qspi-nor` + LittleFS automount path as the older MX25R boards — a blank +chip auto-formats on first mount, no formatter step required. diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.cmake b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.cmake new file mode 100644 index 0000000..f3abf93 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.cmake @@ -0,0 +1,13 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +board_runner_args(jlink "--device=nRF52840_xxAA" "--speed=4000") +board_runner_args(pyocd "--target=nrf52840" "--frequency=4000000") + +set(OPENOCD_NRF5_SUBFAMILY "nrf52") + +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) +include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/openocd-nrf5.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.conf b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.conf new file mode 100644 index 0000000..8ba74d4 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.conf @@ -0,0 +1,37 @@ +# LilyGo T-Impulse Plus (nRF52840 + SX1262 + SSD1315 OLED + u-blox M10 GPS) +# Board-specific configuration +# +# Hardware: +# - nRF52840 SoC, SX1262 LoRa (22 dBm, embedded 32 MHz TCXO via DIO3) +# - GPIO antenna switch: VC1=TX (P1.13), VC2=RX (P1.07) +# - SSD1315 128x64 OLED @ I2C 0x3C (SSD1306-compatible, no reset pin) +# - u-blox MIA-M10Q GPS on UART0 @ 38400, power enable P1.10 +# - ZD25WQ32C 32Mbit (4MB) QSPI flash -> /ext +# - Battery ADC on AIN3 (P0.05), divider enable P0.25 (drive HIGH to read) +# - Single TTP223 touch button (P1.04, active-HIGH) +# - Haptic vibration motor (P0.22) exposed as the buzzer output +# - RT9080 LDO enable P0.14 gates the whole 3V3 peripheral rail +# +# Most features are auto-detected from devicetree: +# - PWM + buzzer (buzzer nodelabel / haptic motor) +# - SSD1315 OLED (zephyr,display chosen) -> also UI_DISPLAY below +# - LoRa RX duty cycle (companion + SX1262) +# - battery ADC, GPS, LED, touch button + +# Board identification +CONFIG_ZEPHCORE_BOARD_NAME="LilyGo T-Impulse Plus" + +# Device Information Service model name +CONFIG_BT_DIS_MODEL_NUMBER_STR="LilyGo T-Impulse Plus" + +# SoftDevice firmware ID — Adafruit bootloader ships s140 v6.1.1 (app @ 0x26000, +# same as T-Echo). Matches the LilyGo PlatformIO board JSON (sd_fwid 0x00B6). +CONFIG_ZEPHCORE_SD_FWID=0x00B6 + +# Compile in the UI display stack; the OLED panel is optional at runtime. +CONFIG_ZEPHCORE_UI_DISPLAY=y + +# Single-button board: ENTER is a 1 s long-press, so two ENTERs to confirm +# span >2 s. Widen the double-press confirmation window accordingly (matches +# the T-Echo single-button mapping). +CONFIG_ZEPHCORE_UI_CONFIRM_WINDOW_MS=3000 diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.overlay b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.overlay new file mode 100644 index 0000000..c53db35 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.overlay @@ -0,0 +1,7 @@ +/* + * LilyGo T-Impulse Plus — DTS is self-contained. + * This overlay is intentionally empty; all nodes live in + * lilygo_timpulse_plus.dts. User-level tweaks (e.g. OLED orientation) go here. + * + * SPDX-License-Identifier: MIT + */ diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.yml b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.yml new file mode 100644 index 0000000..173dc4c --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/board.yml @@ -0,0 +1,9 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +board: + name: lilygo_timpulse_plus + full_name: LilyGo T-Impulse Plus (nRF52840 + SX1262 + SSD1315 OLED + M10 GPS) + vendor: lilygo + socs: + - name: nrf52840 diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus-pinctrl.dtsi b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus-pinctrl.dtsi new file mode 100644 index 0000000..21107c2 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus-pinctrl.dtsi @@ -0,0 +1,118 @@ +/* + * LilyGo T-Impulse Plus pin control definitions + * Copyright (c) 2026 ZephCore + * + * SPDX-License-Identifier: MIT + * + * Pin mappings (from LilyGo official pin_config.h, verified 2026-07-04): + * + * SPI2 (LoRa SX1262): SCK=P0.03, MOSI=P0.28, MISO=P0.30, CS=P1.14 + * I2C0 (SSD1315 OLED): SDA=P0.20, SCL=P0.15 + * UART0 (M10 GPS 38400):TX=P1.11 (->GPS RX), RX=P1.12 (<-GPS TX) + * PWM0 (haptic motor): P0.22 + * QSPI (ZD25WQ32C 4MB): SCK=P0.04, CS=P0.12, IO0=P0.06, IO1=P1.09, + * IO2=P0.08, IO3=P0.26 + * + * NOTE on the GPS RX/TX: the LilyGo net names GPS_UART_RX/TX are labelled + * from the *GPS module's* pin side. The Arduino example calls + * Serial2.setPins(GPS_UART_TX, GPS_UART_RX) and Adafruit's + * Uart::setPins(pin_rx, pin_tx) takes RX first — so the MCU receive pin is + * GPS_UART_TX (P1.12) and the MCU transmit pin is GPS_UART_RX (P1.11). + */ + +&pinctrl { + /* UART0 for u-blox MIA-M10Q GPS at 38400 baud */ + uart0_default: uart0_default { + group1 { + psels = ; /* P1.11 -> GPS RX */ + }; + group2 { + psels = ; /* P1.12 <- GPS TX */ + bias-pull-up; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* I2C0 for SSD1315 OLED (0x3C) */ + i2c0_default: i2c0_default { + group1 { + psels = , /* P0.20 */ + ; /* P0.15 */ + }; + }; + + i2c0_sleep: i2c0_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* SPI2 for LoRa SX1262 (dedicated bus, matches Arduino NRF_SPIM3) */ + spi2_default: spi2_default { + group1 { + psels = , /* P0.03 */ + , /* P0.28 */ + ; /* P0.30 */ + }; + }; + + spi2_sleep: spi2_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + /* PWM0 for the haptic vibration motor on P0.22 */ + pwm0_default: pwm0_default { + group1 { + psels = ; + }; + }; + + pwm0_sleep: pwm0_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; + + /* QSPI for ZD25WQ32C 32Mbit (4MB) external flash */ + qspi_default: qspi_default { + group1 { + psels = , + , + , + , + , + ; + }; + }; + + qspi_sleep: qspi_sleep { + group1 { + psels = , + , + , + , + ; + low-power-enable; + }; + group2 { + psels = ; + low-power-enable; + bias-pull-up; + }; + }; +}; diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus.dts b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus.dts new file mode 100644 index 0000000..926c2aa --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus.dts @@ -0,0 +1,368 @@ +/* + * LilyGo T-Impulse Plus — nRF52840 + SX1262 + SSD1315 OLED + u-blox M10 GPS + * Copyright (c) 2026 ZephCore + * + * SPDX-License-Identifier: MIT + * + * A LoRa wristband/tracker. Hardware (from LilyGo's official pin_config.h, + * variant.h and the datasheets shipped in the T-Impulse-Plus repo): + * + * - nRF52840 SoC with BLE 5, native USB, Adafruit UF2 bootloader + * (SoftDevice s140 v6.1.1, app @ 0x26000 — same layout as T-Echo) + * - SX1262 LoRa (AcSip S62F module): 22 dBm, embedded 32 MHz TCXO on DIO3, + * GPIO antenna switch (VC1=TX P1.13, VC2=RX P1.07 — NOT DIO2) + * - SSD1315 128x64 mono OLED on I2C0 @ 0x3C (SSD1306-compatible, no reset pin) + * - u-blox MIA-M10Q GPS on UART0 @ 38400 baud, power enable P1.10 + * - ZD25WQ32C 32Mbit (4MB) QSPI flash -> /ext LittleFS + * - ICM-20948 IMU (0x69) + SGM41562 charger (0x03) on a second I2C bus — + * present but NOT used by ZephCore, so that bus is left disabled + * - Battery ADC on AIN3 (P0.05), divider gated by P0.25 (drive HIGH to read) + * - TTP223 capacitive touch button (P1.04, active-HIGH) + * - Haptic vibration motor (P0.22) exposed as the buzzer/notification output + * - Single blue LED (P0.17, active-LOW, per variant.h) + * + * Power rail: + * - RT9080 LDO enable P0.14 (HIGH) powers the whole 3V3 peripheral rail + * (SX1262, OLED, GPS, QSPI). Brought up as a boot-on fixed regulator so it + * is live before those devices initialise. + */ + +/dts-v1/; +#include +#include "lilygo_timpulse_plus-pinctrl.dtsi" +#include +#include +#include +#include + +/ { + model = "LilyGo T-Impulse Plus"; + compatible = "lilygo,timpulse-plus"; + + chosen { + zephyr,sram = &sram0; + zephyr,flash = &flash0; + zephyr,code-partition = &code_partition; + zephyr,console = &cdc_acm_uart; + zephyr,shell-uart = &cdc_acm_uart; + zephyr,display = &oled; + }; + + aliases { + lora0 = &lora; + /* Only one LED on this board — use it for both status and LoRa TX. */ + led0 = &led_blue; + lora-tx-led = &led_blue; + sw0 = &touch_button; + watchdog0 = &wdt0; + buzzer = &buzzer; + gps-enable = &gps_enable_pin; + }; + + /* ---- LED ---- + * Single blue LED on P0.17, active-LOW (variant.h: LED_STATE_ON = 0). + * The wristband is primarily an OLED + haptic device; if P0.17 is + * unpopulated on a given unit, driving it is simply a no-op. */ + leds { + compatible = "gpio-leds"; + + led_blue: led_0 { + gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; + label = "Blue LED"; + }; + }; + + /* ---- Touch button (TTP223, P1.04) ---- + * + * The only user input on the wristband. The TTP223 presents an + * active-HIGH momentary output (external pull-down, Arduino uses + * INPUT_PULLDOWN), so no internal pull is configured. + * + * Single-button UI (same scheme as T-Echo / RAK4631): + * short press (release < 1s) -> KEY_A -> multi-tap filter: + * 1 tap -> KEY_1 = next page + * 2 taps -> KEY_LEFT = previous page + * long press (>= 1s) -> KEY_ENTER = activate / confirm + * + * (The recessed BOOT button on P0.24 is left as the DFU/reset button and + * is intentionally not wired as a runtime input.) + */ + buttons: buttons { + compatible = "gpio-keys"; + + touch_button: button_0 { + gpios = <&gpio1 4 GPIO_ACTIVE_HIGH>; + zephyr,code = ; + label = "Touch Button"; + }; + }; + + /* short-press -> KEY_A (multi-tap), long-press (>=1s) -> KEY_ENTER */ + touch_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; + + page_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; + + /* ---- Power control ---- + * RT9080 LDO enable (P0.14). A single fixed regulator gating the entire + * 3V3 peripheral rail: SX1262, SSD1315 OLED, GPS and QSPI flash. boot-on + * so the regulator framework raises it before those drivers init + * (REGULATOR_FIXED_INIT_PRIORITY pinned to 30 in Kconfig, ahead of the + * QSPI NOR init at 41). */ + pwr_enable: pwr-enable { + compatible = "regulator-fixed"; + regulator-name = "pwr-enable"; + enable-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>; + regulator-boot-on; + startup-delay-us = <10000>; + }; + + /* Battery-measurement enable (P0.25, active-HIGH). + * ZephyrBoard::getBattMilliVolts() calls regulator_enable/disable around + * the ADC read so the divider only draws current during a measurement. */ + vbat_enable: vbat-enable { + compatible = "regulator-fixed"; + regulator-name = "vbat-enable"; + enable-gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>; + }; + + /* ---- GPS power control ---- + * GPS_EN (P1.10). gnss-nmea-generic has no on-off-gpios property, so + * ZephyrGPSManager drives this pin directly via the gps-enable alias to + * power the M10 up/down for duty-cycled fixes. */ + gps_en: gps-enable { + compatible = "gpio-leds"; + gps_enable_pin: gps_enable { + gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>; + label = "GPS Enable"; + }; + }; + + /* ---- Haptic motor as buzzer ---- + * The vibration motor (P0.22) stands in for a piezo buzzer: message and + * key-feedback "beeps" become haptic pulses, which is what a wearable + * wants. Driven from PWM0. */ + pwmbuzzer { + compatible = "pwm-leds"; + buzzer: buzzer { + pwms = <&pwm0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>; + }; + }; + + /* ---- Battery ADC ---- + * AIN3 = P0.05. Divider ratio not published — 7236 assumes a 2:1 divider + * (0.6V ref x 1/6 gain x 2.0 x 1000, +0.5% SAADC gain error). VERIFY + * against the board and adjust vbat-mv-multiplier if the reported voltage + * is off. */ + zephyr,user { + io-channels = <&adc 3>; + vbat-mv-multiplier = <7236>; + }; +}; + +/* ---- SoC configuration ---- */ + +®0 { + status = "okay"; +}; + +®1 { + regulator-initial-mode = ; +}; + +&adc { + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + /* Battery voltage on P0.05 (AIN3) */ + channel@3 { + reg = <3>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <12>; + }; +}; + +&gpiote { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +/* ---- PWM0 for the haptic motor ---- */ +&pwm0 { + status = "okay"; + pinctrl-0 = <&pwm0_default>; + pinctrl-1 = <&pwm0_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* ---- GPS on UART0 (u-blox MIA-M10Q, 38400 baud) ---- + * The M10 streams standard NMEA at power-on, so we use gnss-nmea-generic + * (passive listener) rather than a u-blox UBX driver — no config commands, + * no blocking init. GPS power is gated by the gps-enable alias (P1.10); + * deferred-init lets ZephyrGPSManager raise power before the driver probes. */ +&uart0 { + compatible = "nordic,nrf-uarte"; + status = "okay"; + current-speed = <38400>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; + + gnss: gnss { + compatible = "gnss-nmea-generic"; + zephyr,deferred-init; + }; +}; + +/* ---- I2C0: SSD1315 OLED ---- + * SSD1315 is SSD1306-compatible; the solomon,ssd1306 driver drives it. No + * hardware reset line on this board (SCREEN_RST = -1). Standard 0.96" 128x64 + * orientation — if the panel is mounted rotated, flip segment-remap/com-invdir. */ +&i2c0 { + compatible = "nordic,nrf-twim"; + status = "okay"; + clock-frequency = ; + pinctrl-0 = <&i2c0_default>; + pinctrl-1 = <&i2c0_sleep>; + pinctrl-names = "default", "sleep"; + + oled: ssd1306@3c { + compatible = "solomon,ssd1306"; + reg = <0x3c>; + width = <128>; + height = <64>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <63>; + segment-remap; + com-invdir; + prechargep = <0x22>; + }; +}; + +/* ---- SPI2 for LoRa SX1262 ---- */ +&spi2 { + compatible = "nordic,nrf-spim"; + status = "okay"; + pinctrl-0 = <&spi2_default>; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; + cs-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; /* P1.14 LORA_CS */ + + lora: lora@0 { + compatible = "semtech,sx1262"; + reg = <0>; + spi-max-frequency = <8000000>; + + /* LoRa control pins */ + reset-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; /* P0.02 */ + busy-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; /* P0.31 */ + dio1-gpios = <&gpio0 29 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* P0.29 */ + + /* GPIO antenna switch (not DIO2). The patched native sx126x driver + * drives tx-enable on TX and rx-enable on RX, both low in standby — + * matching the RadioLib setRfSwitchPins(rxEn=VC2, txEn=VC1) call in + * LilyGo's example. */ + tx-enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; /* VC1 = P1.13 (TX) */ + rx-enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>; /* VC2 = P1.07 (RX) */ + + /* Embedded 32 MHz TCXO on the S62F module, fed via DIO3. + * (AcSip S62F datasheet: "Embedded TCXO of 32MHz". Without DIO3 + * control the module has no clock and stays permanently BUSY.) + * 1.8V mirrors the other LilyGo/ZephCore nRF SX1262 boards; the + * LilyGo RadioLib example validated the 1.6V default, so drop to + * SX126X_DIO3_TCXO_1V6 if the radio fails to start. */ + dio3-tcxo-voltage = ; + tcxo-power-startup-delay-ms = <10>; + + /* RX boosted mode for better sensitivity (matches Arduino default). */ + rx-boosted; + }; +}; + +/* ---- QSPI Flash — ZD25WQ32C 32Mbit (4MB) ---- + * Same nordic,qspi-nor driver + LittleFS /ext automount as the MX25R parts on + * T-Echo/ThinkNode — no new driver and no formatter: a blank chip auto-formats + * on first mount (littlefs mount-fail -> format -> mount). Configured + * single-line (fastread/pp) for reliable bring-up; quad can be enabled later. + * + * All values below are datasheet-confirmed (Zetta ZD25WQ32C, 2022): + * - JEDEC RDID (9Fh) = BA 60 16 (MID 0xBA Zetta, type 0x60, capacity 0x16). + * The nordic driver hard-fails init (-ENODEV) on an ID mismatch, so this + * must be exact for /ext to mount; identity/prefs stay on internal /lfs. + * - Deep power-down (B9h) supported: tDP 3us, tRES1 8us — the 10us/35us + * waits below are conservative and safe. */ +&qspi { + status = "okay"; + pinctrl-0 = <&qspi_default>; + pinctrl-1 = <&qspi_sleep>; + pinctrl-names = "default", "sleep"; + + qspi_flash: qspi_flash@0 { + compatible = "nordic,qspi-nor"; + reg = <0>; + writeoc = "pp"; + readoc = "fastread"; + sck-frequency = <16000000>; + jedec-id = [ba 60 16]; /* ZD25WQ32C — Zetta datasheet Table-9 */ + size = <33554432>; /* size is in BITS: 32Mbit = 4MB */ + has-dpd; + t-enter-dpd = <10000>; /* tDP 3us (conservative) */ + t-exit-dpd = <35000>; /* tRES1 8us (conservative) */ + }; +}; + +/* ---- USB CDC for console / repeater CLI / USB companion ---- */ +zephyr_udc0: &usbd { + compatible = "nordic,nrf-usbd"; + status = "okay"; + + cdc_acm_uart: cdc_acm_uart { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +/* ---- Flash partitions (SoftDevice v6, app @ 0x26000) ---- */ +#include "../../common/nrf52_partitions_sdv6.dtsi" + +/* ---- External QSPI flash partitions ---- */ +&qspi_flash { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Full 4MB for LittleFS storage */ + qspi_storage_partition: partition@0 { + label = "qspi_storage"; + reg = <0x00000000 0x00400000>; + }; + }; +}; + +/* ---- External QSPI /ext automount ---- */ +#include "../../common/qspi-ext.dtsi" +#include "../../common/nrf52_wakeup.dtsi" diff --git a/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus_defconfig b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus_defconfig new file mode 100644 index 0000000..22c38a2 --- /dev/null +++ b/zephcore/boards/nrf52840/lilygo_timpulse_plus/lilygo_timpulse_plus_defconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: MIT +# LilyGo T-Impulse Plus default configuration + +# Enable MPU +CONFIG_ARM_MPU=y +CONFIG_HW_STACK_PROTECTION=y + +# Enable GPIO +CONFIG_GPIO=y + +# Enable console on USB CDC +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +# Enable flash/NVS storage +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_NVS=y + +# Pinctrl +CONFIG_PINCTRL=y + +# SPI for LoRa SX1262 +CONFIG_SPI=y + +# ADC for battery +CONFIG_ADC=y