mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 20:09:17 +00:00
port thinknode m9
This commit is contained in:
@@ -269,7 +269,7 @@ if(BOARD MATCHES ".*nrf52.*" OR BOARD MATCHES "rak4631" OR BOARD MATCHES "rak_wi
|
||||
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")
|
||||
elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES "lilygo_tlora_c6" OR BOARD MATCHES "heltec_wifi_lora32_v3" OR BOARD MATCHES "heltec_wifi_lora32_v4")
|
||||
elseif(BOARD MATCHES ".*esp32.*" OR BOARD MATCHES "station_g2" OR BOARD MATCHES "thinknode_m9" OR BOARD MATCHES "lilygo_tlora_c6" OR BOARD MATCHES "heltec_wifi_lora32_v3" OR BOARD MATCHES "heltec_wifi_lora32_v4")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf")
|
||||
elseif(DEFINED BOARD_QUALIFIERS AND BOARD_QUALIFIERS MATCHES ".*esp32.*")
|
||||
set(ZEPHCORE_PLATFORM_CONF "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_common.conf")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
config HEAP_MEM_POOL_ADD_SIZE_BOARD
|
||||
int
|
||||
default 4096
|
||||
@@ -0,0 +1,3 @@
|
||||
config BOARD_THINKNODE_M9
|
||||
select SOC_ESP32S3_WROOM_N16R8
|
||||
select SOC_ESP32S3_PROCPU if BOARD_THINKNODE_M9_ESP32S3_PROCPU
|
||||
@@ -0,0 +1,170 @@
|
||||
# Elecrow ThinkNode M9
|
||||
|
||||
ESP32-S3 handheld with LR1110 radio, ST7789 320x240 TFT, CC1167Q GPS, PCF8563
|
||||
RTC, buzzer, STC8H I2C keyboard, QMI8658 IMU + QMC6309 magnetometer, and an SD
|
||||
slot (keyboard/IMU/SD not ported — see below).
|
||||
|
||||
Ported 2026-07-18 from Arduino MeshCore `variants/thinknode_m9` (which itself
|
||||
landed upstream only days earlier), cross-checked against a second,
|
||||
independently developed firmware for this board and against the Elecrow V1.0
|
||||
schematic. **Not yet validated on hardware.**
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Companion
|
||||
west build -b thinknode_m9/esp32s3/procpu zephcore --pristine
|
||||
west flash --esp-device COMX
|
||||
|
||||
# Repeater
|
||||
west build -b thinknode_m9/esp32s3/procpu zephcore --pristine -- \
|
||||
-DEXTRA_CONF_FILE="boards/common/repeater.conf"
|
||||
```
|
||||
|
||||
Plain builds are ESP Simple Boot (self-contained `zephyr.bin`); WiFi-OTA and
|
||||
release builds use `--sysbuild` + MCUboot like the other S3 boards (see
|
||||
top-level CLAUDE.md).
|
||||
|
||||
## LR1110 radio firmware updater
|
||||
|
||||
First ESP32 target for `tools/lr1110_updater`:
|
||||
|
||||
```bash
|
||||
west build -b thinknode_m9/esp32s3/procpu zephcore/tools/lr1110_updater --pristine
|
||||
west flash --esp-device COMX
|
||||
```
|
||||
|
||||
Console output arrives on the native USB Serial/JTAG port. After the updater
|
||||
reports success, flash the main firmware over the same port (`west flash` —
|
||||
no DFU mode needed; esptool resets the chip itself). Identity/prefs/contacts
|
||||
in high flash are untouched by the updater flash.
|
||||
|
||||
## Verified against Arduino MeshCore (2026-07-18 desk check)
|
||||
|
||||
- **PSRAM**: upstream `boards/thinknode_m9.json` says `memory_type: qio_opi`,
|
||||
16 MB flash → WROOM-1-**N16R8** + `CONFIG_SPIRAM_MODE_OCT=y` confirmed.
|
||||
- **MADCTL**: Arduino ends at **0xA0** (`landscapeScreen()` 0x60, then
|
||||
`DISPLAY_FLIP_VERTICALLY` overwrites with MV|MY) — DTS uses 0xA0.
|
||||
- **Panel inversion**: Arduino init sends INVON; Zephyr st7789v inverts by
|
||||
default (no `inversion-off` prop set) — matched.
|
||||
- **GPS UART direction**: `Serial1.setPins(PIN_GPS_TX=2, PIN_GPS_RX=3)` and
|
||||
arduino-esp32 `setPins(rx, tx)` → MCU RX=GPIO2, TX=GPIO3 (the apparent
|
||||
ini-vs-variant.h swap is only naming perspective; both agree).
|
||||
- **GPS EN/RESET behavior**: `MicroNMEALocationProvider` drives EN LOW-to-run
|
||||
and holds RESET HIGH when stopped, 10 ms release pulse on start — exactly
|
||||
what `ZephyrGPSManager`'s sequence does with the `gps-enable` (active-LOW)
|
||||
and `gps-reset` (active-HIGH) aliases here.
|
||||
- **RF switch at TX**: our LR11xx driver always selects the HP PA, so the chip
|
||||
applies `rfswitch-tx-hp` (0x02 = DIO5 L, DIO6 H) — same as Arduino's
|
||||
`MODE_TX_HP` at `LORA_TX_POWER=22`.
|
||||
- **Battery**: MeshCore's ESP32Board hardcodes `2 * analogReadMilliVolts()` —
|
||||
the 2:1 divider assumption (`vbat-mv-multiplier = 8800`) matches Arduino.
|
||||
- **Upstream quirk not copied**: `ThinkNodeM9Board::getIRQGpio()` returns
|
||||
`LORA_DIO0`=41, which is the **BUSY** pin; the RadioLib `Module(...)` ctor
|
||||
uses DIO1=42 as IRQ. We follow the working wiring (dio1-gpios = 42) —
|
||||
the second implementation names them correctly (IRQ = DIO1 = 42).
|
||||
|
||||
## Verified against a second independent firmware (second desk check)
|
||||
|
||||
- **GPS module is NOT an L76K** — MeshCore's `GPS_L76K` define is another
|
||||
copy-paste artifact. The module identifies as **CC1167Q** to the *Unicore*
|
||||
(`$PDTINFO`) probe family, which also explains the 115200 default baud
|
||||
(a CASIC L76K would default to 9600). The DTS therefore uses
|
||||
`gnss-nmea-generic` (plain NMEA, no vendor commands) rather than the CASIC
|
||||
air530z driver.
|
||||
- **GPS baud 115200** — both implementations agree; resolved.
|
||||
- **GPS STANDBY (GPIO10)** — driven by *neither* implementation; left
|
||||
completely untouched, matching both.
|
||||
- **Battery** — the second implementation uses a 2.0 multiplier on ADC unit
|
||||
2 channel 2 → triple-confirms GPIO13 = ADC unit 2 ch 2 at 2:1. Its
|
||||
measured OCV table is imported as [battery_curve.c](battery_curve.c).
|
||||
- **No user button** — no button GPIO in either implementation (input is
|
||||
the keyboard; canned-messages UI). No `sw0` is correct, not a gap.
|
||||
- **No LED** — no LED pin in either implementation; MeshCore's `PIN_LED 13`
|
||||
(colliding with `PIN_VBAT_READ 13`) confirmed a bug.
|
||||
- **RF switch table** — both implementations carry the identical DIO5/DIO6
|
||||
truth table; our DTS bitmasks match both.
|
||||
- **SD card CS (GPIO48)** — the second implementation parks all three CS
|
||||
pins HIGH before radio init because the slot shares SPI2. Our base DTS
|
||||
does the same with a `sd-cs-park` gpio-hog (also covers the standalone
|
||||
LR1110 updater).
|
||||
- **Charge/power sense** — GPIO1 = external-power detect, **active LOW**;
|
||||
GPIO8 = charger DONE. Not consumed by ZephCore yet.
|
||||
- **Keyboard identified** — STC8H helper MCU at **I2C 0x6C** on the second
|
||||
bus (SDA=20 SCL=21), `KB_INT` GPIO12 idle-low/rising-edge, backlight
|
||||
GPIO46. Registers: 0x01 battery, 0x03 long-press ms (16-bit write), 0x05
|
||||
matrix key, 0x06 state.
|
||||
- **Also on the sensor bus** — QMI8658 IMU + QMC6309 magnetometer.
|
||||
Unported; no address collision with the PCF8563 @0x51.
|
||||
|
||||
## Verified against the Elecrow V1.0 schematic (third desk check)
|
||||
|
||||
Net-level facts from `Think Node M9_V1.0.sch` (2026-06-23). The text layer
|
||||
gives nets and part numbers, not GPIO pairings — pin numbers stay
|
||||
firmware-sourced (both firmwares agree on all of them).
|
||||
|
||||
- **VEXT (GPIO18) scope resolved** — the net is `VDD_PERIPH_EN`, gating a
|
||||
rail that feeds the display (`DISPLAY_VDD`), the RTC's main supply
|
||||
(`RTC_3V3`; the button cell `BUTTON-CELL`/BAT2 only backs up timekeeping)
|
||||
and the I2C sensors. `tft_pwr_enable` therefore has `regulator-boot-on`
|
||||
so the rail is up before RTC autodiscover / sensor scan, matching
|
||||
Arduino's unconditional `board.begin()` write.
|
||||
- **GPS rails confirmed** — the module has its own switched supply
|
||||
(`GPS_EN` → `GPS_VDD`), so screenless/VEXT-off states never affect it;
|
||||
its `ON_OFF` and `N_RESET` pins are wired (`GPS_ON/OFF`, `GPS_RST` nets),
|
||||
and a `GPS_BAT`/`BACKUP_POWER` rail keeps ephemeris alive for warm starts.
|
||||
Module symbol is generic ("GNSS MOD", 18-pin, TXD/RXD/1PPS/ON_OFF/VBAT/
|
||||
SDA/SCL) — no part number in the schematic; CC1167Q identification stands
|
||||
on the `$PDTINFO` probe response.
|
||||
- **USB is a UART bridge** — USB-C data goes through a CP2102/CH9102-class
|
||||
bridge (U3, DTR/RTS auto-program transistors Q12/Q13) onto `RXD0_H`/
|
||||
`TXD0_H` = UART0. Our console-on-uart0 routing therefore lands exactly on
|
||||
the USB-C port. Whether the S3's native D+/D− (GPIO19/20) also reach the
|
||||
connector is unclear — MeshCore claims `ST7789_TE`=19, which would rule it
|
||||
out — so the USB-OTG companion transport may be dead hardware on this
|
||||
board; BLE is the primary companion path (verify OTG enumeration once,
|
||||
drop the include if dead).
|
||||
- **Power button is a hardware latch** — `POWER_EN` latch nets + an
|
||||
`ESP32_WAKEUP` line; no firmware-readable user button at schematic level
|
||||
either. Confirms the no-`sw0` model for the third time.
|
||||
- **RF switch is a discrete SP3T** (U8: V1/V2 control, J1/J2/J3 ports) driven
|
||||
by `RFSW0_V1`/`RFSW1_V2` = LR1110 DIO5/DIO6 — the 2-line table confirmed
|
||||
at net level; DIO1 is the `INTERRUPT` net; `VTCXO` net feeds the TCXO.
|
||||
- **Charger is an LGS4056HDA-4.35** — the suffix is the 4.35 V-termination
|
||||
variant, so the pack may charge above the 4200 mV top of the imported OCV
|
||||
curve (100% would then read early). Check the charged-battery voltage at
|
||||
bring-up and stretch the curve if it really terminates at 4.35 V.
|
||||
- **Keyboard matrix is driven by a second MCU** (nets `ESP32-2_*` — the
|
||||
schematic symbol is another ESP32-S3 template; whatever ships speaks the
|
||||
STC8H protocol at I2C 0x6C). SD card has its own switched `SD_3V3` rail.
|
||||
|
||||
## Bring-up verification list
|
||||
|
||||
Still unverified on hardware, in rough priority order:
|
||||
|
||||
1. **Display init params** — gamma/porch/vcom inherited from Heltec T114's
|
||||
ST7789V (no board-specific init values exist in any reference); check
|
||||
contrast/colors. If mirrored or upside down, swap `mdac` 0xA0 ↔ 0x60.
|
||||
2. **GPS identity/output** — confirm NMEA at 115200 and that the CC1167Q
|
||||
talks standard sentences (it should; both upstreams parse plain NMEA).
|
||||
If a fix never arrives, probe `$PDTINFO` manually.
|
||||
3. **Battery calibration** — the 2:1 divider is what both upstreams assume;
|
||||
sanity-check `get bat` against a multimeter once, and check whether the
|
||||
4.35 V charger variant actually charges above 4.20 V (OCV curve top).
|
||||
4. **USB-OTG companion transport** — see the schematic section above:
|
||||
verify whether the native USB pads reach the USB-C connector at all.
|
||||
5. **rx-boosted** — deliberately ON (both upstreams leave boosted gain off /
|
||||
unset; every other ZephCore LR1110 board uses it). Verify RX sensitivity
|
||||
and TX power on air.
|
||||
6. **ADC2 vs WiFi** — battery reads ride ADC unit 2 (GPIO13), which WiFi also
|
||||
uses; expect intermittent read failures in `wifi_ota.conf` builds.
|
||||
|
||||
## Not ported
|
||||
|
||||
- **STC8H I2C keyboard** (@0x6C, second bus SDA=20 SCL=21, INT=12,
|
||||
backlight=46) — Arduino MeshCore doesn't consume it either; the register
|
||||
map above is the protocol reference when ZephCore grows keyboard support.
|
||||
- **QMI8658 IMU / QMC6309 magnetometer** — no ZephCore use case yet.
|
||||
- **SD card** (CS=48 on the shared SPI) — unused, CS parked HIGH via gpio-hog.
|
||||
- **Ext-power detect** (GPIO1, active LOW) / **charge DONE** (GPIO8),
|
||||
**GPS PPS** (GPIO4), **GPS STANDBY** (GPIO10).
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* ThinkNode M9 single-cell LiPo OCV curve — 11 points at 10% steps.
|
||||
* Index 0 = 100% (4200 mV), index 10 = 0% (3100 mV).
|
||||
*/
|
||||
|
||||
#include "battery_curve.h"
|
||||
|
||||
static const uint16_t ocv_thinknode_m9[11] = {
|
||||
4200, 4080, 3980, 3920, 3870, /* 100 .. 60% */
|
||||
3820, 3790, 3750, 3700, 3600, /* 50 .. 10% */
|
||||
3100, /* 0% */
|
||||
};
|
||||
|
||||
const battery_curve_t battery_curve_default = {
|
||||
.ocv_mv = ocv_thinknode_m9,
|
||||
.num_points = 11,
|
||||
.num_cells = 1,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
if(NOT "${OPENOCD}" MATCHES "^${ESPRESSIF_TOOLCHAIN_PATH}/.*")
|
||||
set(OPENOCD OPENOCD-NOTFOUND)
|
||||
endif()
|
||||
find_program(OPENOCD openocd PATHS ${ESPRESSIF_TOOLCHAIN_PATH}/openocd-esp32/bin NO_DEFAULT_PATH)
|
||||
|
||||
include(${ZEPHYR_BASE}/boards/common/esp32.board.cmake)
|
||||
include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake)
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# Elecrow ThinkNode M9 — ZephCore board configuration
|
||||
#
|
||||
# Hardware:
|
||||
# ESP32-S3-WROOM-1 (16MB flash, 8MB OPI PSRAM assumed — N16R8)
|
||||
# LR1110 on shared SPI2 (TCXO 3.3V, DIO5/DIO6 RF switch, HP PA)
|
||||
# ST7789 320x240 TFT on shared SPI2 (mono-tft wrapper)
|
||||
# L76K GPS on UART1 @ 115200
|
||||
# PCF8563 RTC @ 0x51 on I2C0; buzzer GPIO9 (LEDC)
|
||||
#
|
||||
# Include order: prj.conf → zephcore_common.conf → esp32_common.conf → board.conf
|
||||
|
||||
# Board identification (matches Arduino variant name)
|
||||
CONFIG_ZEPHCORE_BOARD_NAME="ThinkNode M9"
|
||||
CONFIG_BT_DIS_MODEL_NUMBER_STR="Elecrow ThinkNode M9"
|
||||
|
||||
# Radio type (TCXO, RF switch, PA config in DTS)
|
||||
CONFIG_ZEPHCORE_RADIO_LR1110=y
|
||||
|
||||
# Flash size — 16MB (override Zephyr default)
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
|
||||
# PSRAM mode — WROOM-1-N16R8 is OPI octal.
|
||||
# Enable is auto-detected from DTS by Kconfig.psram; only the mode is set here.
|
||||
CONFIG_SPIRAM_MODE_OCT=y
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Elecrow ThinkNode M9 — ZephCore overlay
|
||||
*
|
||||
* Adds the display stack, GPS driver, buzzer, battery ADC, RTC and
|
||||
* filesystem on top of the base board DTS (which already carries the
|
||||
* LR1110 radio — kept there so tools/lr1110_updater works without this
|
||||
* overlay). Pin mapping from Arduino MeshCore variants/thinknode_m9.
|
||||
*
|
||||
* Display SPI2: CS=16 (cs-gpios[1]), DC=15, RST=14, BL=17 (active-LOW)
|
||||
* Panel power: VEXT=18 (active-LOW)
|
||||
* GPS UART1: RX=2, TX=3 @ 115200; EN=11 (active-LOW), RESET=5
|
||||
* Buzzer: GPIO9 (LEDC ch0)
|
||||
* Battery ADC: GPIO13 = ADC unit 2 ch 2 (&adc1), 2:1 divider (matches
|
||||
* MeshCore ESP32Board's hardcoded 2x)
|
||||
*
|
||||
* Not ported (unused by Arduino MeshCore): STC8H I2C keyboard @0x6C (bus
|
||||
* SDA=20 SCL=21, INT=12, LED=46), SD card (CS=48 — parked HIGH by a hog in
|
||||
* the base DTS), ext-power detect GPIO1 (active-LOW) + charge-DONE GPIO8,
|
||||
* GPS PPS (4), GPS STANDBY (10), QMI8658 IMU + QMC6309 magnetometer on the
|
||||
* sensor bus.
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/pwm/pwm.h>
|
||||
|
||||
/ {
|
||||
aliases {
|
||||
pwm-0 = &ledc0;
|
||||
buzzer = &buzzer;
|
||||
gps-enable = &gps_enable_pin;
|
||||
gps-reset = &gps_reset_pin;
|
||||
};
|
||||
|
||||
chosen {
|
||||
zephyr,display = &mono_tft;
|
||||
};
|
||||
|
||||
/*
|
||||
* VDD_PERIPH rail — GPIO18 (VEXT_ENABLE), active-LOW.
|
||||
* The V1.0 schematic shows this is NOT display-only: VDD_PERIPH_EN
|
||||
* gates a rail feeding the panel (DISPLAY_VDD), the RTC's main supply
|
||||
* (button cell only backs up timekeeping) and the I2C sensors —
|
||||
* `regulator-boot-on` powers it at driver init, before the RTC
|
||||
* autodiscover and sensor scan run, matching Arduino's unconditional
|
||||
* board.begin() write. display.c still treats it as the panel supply
|
||||
* (never disables it); the label stays tft_pwr_enable for that hookup.
|
||||
*/
|
||||
tft_pwr_enable: tft-pwr-enable {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "tft-pwr-enable";
|
||||
enable-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>;
|
||||
regulator-boot-on;
|
||||
};
|
||||
|
||||
/*
|
||||
* TFT backlight — GPIO17 (TFT_LEDA_CTL), active-LOW.
|
||||
* Named disp_pwr_enable so display.c auto-gates it with display on/off.
|
||||
* (Arduino dims it with 44 kHz PWM; fixed on/off is the first pass.)
|
||||
*/
|
||||
disp_pwr_enable: disp-pwr-enable {
|
||||
compatible = "regulator-fixed";
|
||||
regulator-name = "disp-pwr-enable";
|
||||
enable-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
/*
|
||||
* Monochrome wrapper around the ST7789.
|
||||
* Reports PIXEL_FORMAT_MONO01 to CFB (~9.6 KB framebuffer instead of
|
||||
* ~150 KB RGB565) and converts each row on flush.
|
||||
*/
|
||||
mono_tft: mono-tft {
|
||||
compatible = "zephcore,mono-tft";
|
||||
tft-dev = <&tft>;
|
||||
width = <320>;
|
||||
height = <240>;
|
||||
};
|
||||
|
||||
/*
|
||||
* MIPI-DBI SPI wrapper for the ST7789 — rides the shared SPI2 bus.
|
||||
* The child's reg (1) picks cs-gpios[1] (GPIO16) declared in the base DTS.
|
||||
* DC=GPIO15, RST=GPIO14 (active-LOW). Write-only (MISO belongs to LR1110).
|
||||
*/
|
||||
mipi_dbi {
|
||||
compatible = "zephyr,mipi-dbi-spi";
|
||||
spi-dev = <&spi2>;
|
||||
dc-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
|
||||
write-only;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
tft: st7789v@1 {
|
||||
compatible = "sitronix,st7789v";
|
||||
mipi-max-frequency = <20000000>;
|
||||
reg = <1>;
|
||||
width = <320>;
|
||||
height = <240>;
|
||||
x-offset = <0>;
|
||||
y-offset = <0>;
|
||||
/* MADCTL=0xA0: MY|MV, RGB order (landscape 320x240 on the
|
||||
* native-portrait 240x320 panel). This is what Arduino ends
|
||||
* up with: landscapeScreen() sets 0x60 (MX|MV), then
|
||||
* DISPLAY_FLIP_VERTICALLY=1 calls flipScreenVertically()
|
||||
* which overwrites MADCTL with MV|MY = 0xA0 (ST7789Spi.h).
|
||||
* If mirrored/upside-down at bring-up, try 0x60.
|
||||
* Panel inversion (INVON): Arduino sends it, and the Zephyr
|
||||
* st7789v driver defaults to inversion ON (no `inversion-off`
|
||||
* prop here) — matched. */
|
||||
mdac = <0xA0>;
|
||||
colmod = <0x55>;
|
||||
vcom = <0x20>;
|
||||
gctrl = <0x35>;
|
||||
vrhs = <0x0b>;
|
||||
vdvs = <0x20>;
|
||||
gamma = <0x01>;
|
||||
lcm = <0x2c>;
|
||||
porch-param = [0c 0c 00 33 33];
|
||||
cmd2en-param = [5a 69 02 01];
|
||||
pwctrl1-param = [a4 a1];
|
||||
pvgam-param = [70 2c 2e 15 10 09 48 33 53 0b 19 18 20 25];
|
||||
nvgam-param = [70 2c 2e 15 10 09 48 33 53 0b 19 18 20 25];
|
||||
ram-param = [00 f0];
|
||||
rgb-param = [40 02 14];
|
||||
mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE";
|
||||
};
|
||||
};
|
||||
|
||||
/* GPS power enable — GPIO11, active-LOW (PIN_GPS_EN_ACTIVE=LOW).
|
||||
* ZephCore's GPS manager drives this via the gps-enable alias. */
|
||||
gps_en: gps-enable {
|
||||
compatible = "gpio-leds";
|
||||
gps_enable_pin: gps_enable {
|
||||
gpios = <&gpio0 11 GPIO_ACTIVE_LOW>;
|
||||
label = "GPS Enable";
|
||||
};
|
||||
};
|
||||
|
||||
/* GPS reset — GPIO5, active-HIGH per Arduino (PIN_GPS_RESET_ACTIVE=HIGH). */
|
||||
gps_rst: gps-reset {
|
||||
compatible = "gpio-leds";
|
||||
gps_reset_pin: gps_reset {
|
||||
gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
|
||||
label = "GPS Reset";
|
||||
};
|
||||
};
|
||||
|
||||
/* Buzzer on LEDC channel 0 (GPIO9) */
|
||||
pwmbuzzer {
|
||||
compatible = "pwm-leds";
|
||||
buzzer: buzzer {
|
||||
pwms = <&ledc0 0 PWM_MSEC(20) PWM_POLARITY_NORMAL>;
|
||||
};
|
||||
};
|
||||
|
||||
/* Battery ADC — ZephCore reads via zephyr,user { io-channels }.
|
||||
* GPIO13 = ADC **unit 2** channel 2 → &adc1 (the DT label is 0-indexed,
|
||||
* the unit is 1-indexed — same trap as heltec_wireless_tracker).
|
||||
* ADC_GAIN_1_4 = 12 dB attenuation ≈ 0–3.1 V usable; nominal full scale
|
||||
* 1100 mV × 4 = 4400 mV. Divider ratio unverified — assumed 2:1
|
||||
* (multiplier 4400 × 2.0 = 8800). Calibrate at bring-up.
|
||||
* Caveat: ADC unit 2 is shared with WiFi — battery reads can fail while
|
||||
* WiFi is active (wifi_ota builds). */
|
||||
zephyr,user {
|
||||
io-channels = <&adc1 2>;
|
||||
vbat-mv-multiplier = <8800>;
|
||||
};
|
||||
};
|
||||
|
||||
/* Battery ADC channel configuration (ADC unit 2 → GPIO13). */
|
||||
&adc1 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
zephyr,gain = "ADC_GAIN_1_4";
|
||||
zephyr,reference = "ADC_REF_INTERNAL";
|
||||
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
|
||||
zephyr,resolution = <12>;
|
||||
};
|
||||
};
|
||||
|
||||
/* GPS on UART1 — generic NMEA driver (no vendor commands).
|
||||
* The module is NOT the L76K that MeshCore's variant.h claims — it
|
||||
* identifies as "CC1167Q" to the Unicore-family $PDTINFO probe, which also
|
||||
* explains the 115200 default baud (a CASIC L76K would be 9600). Under
|
||||
* module-identity uncertainty gnss-nmea-generic is the safe choice —
|
||||
* CASIC-specific driver init would be wrong here, and plain NMEA works
|
||||
* either way. Power is gated by the manager via gps-enable/gps-reset.
|
||||
* GPS_STANDBY (GPIO10) is deliberately left untouched — Arduino MeshCore
|
||||
* never drives it either. */
|
||||
&uart1 {
|
||||
gnss: gnss-nmea-generic {
|
||||
compatible = "gnss-nmea-generic";
|
||||
};
|
||||
};
|
||||
|
||||
/* LEDC PWM controller — buzzer timer/channel */
|
||||
&ledc0 {
|
||||
pinctrl-0 = <&ledc0_default>;
|
||||
pinctrl-names = "default";
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel0@0 {
|
||||
reg = <0x0>;
|
||||
timer = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* ThinkNode M9 has a battery-backed PCF8563 @ 0x51 — opt in to boot-time
|
||||
* time restore (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/*
|
||||
* Flash partitions live in partitions.overlay (this dir) so the identical map
|
||||
* is fed to both the app and the MCUboot image — see that file and
|
||||
* sysbuild/CMakeLists.txt.
|
||||
*/
|
||||
|
||||
/* LittleFS auto-mount — standard /lfs mount point */
|
||||
#include "../../common/filesystem.dtsi"
|
||||
|
||||
/* USB OTG companion transport. thinknode_m9 consoles over USB Serial/JTAG,
|
||||
* which shares D+/D- with USB OTG, so the console is rerouted to uart0. */
|
||||
#include "../../common/esp32s3_console_uart0.dtsi"
|
||||
#include "../../common/esp32s3_usb_otg.dtsi"
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2026 ZephCore
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
board:
|
||||
name: thinknode_m9
|
||||
full_name: Elecrow ThinkNode M9 (ESP32-S3 + LR1110 + ST7789 TFT)
|
||||
vendor: elecrow
|
||||
socs:
|
||||
- name: esp32s3
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Flash partition layout — SHARED by the app image and MCUboot.
|
||||
*
|
||||
* Fed to BOTH images (app via EXTRA_DTC_OVERLAY_FILE, MCUboot via
|
||||
* mcuboot_EXTRA_DTC_OVERLAY_FILE in sysbuild/CMakeLists.txt) so their flash maps
|
||||
* are identical — otherwise MCUboot stages/looks for the OTA image at a different
|
||||
* address than the app writes it to and OTA silently reverts on reboot.
|
||||
*
|
||||
* slot0 starts at 0x10000 (right after the 64KB MCUboot region), matching the
|
||||
* Arduino/ESP-IDF app offset (Mesh America "flash-update" compatible). sys_partition
|
||||
* is reclaimed. Slots are equal size and slot1 ends exactly on the unchanged
|
||||
* storage/lfs offsets, so a -merged reflash keeps identity/prefs/contacts/BLE-bonds.
|
||||
*
|
||||
* 16MB layout.
|
||||
*/
|
||||
/delete-node/ &sys_partition;
|
||||
/delete-node/ &slot0_lpcore_partition;
|
||||
/delete-node/ &slot1_lpcore_partition;
|
||||
/delete-node/ &storage_partition;
|
||||
/delete-node/ &scratch_partition;
|
||||
/delete-node/ &coredump_partition;
|
||||
/delete-node/ &slot0_partition;
|
||||
/delete-node/ &slot1_partition;
|
||||
|
||||
/ {
|
||||
chosen {
|
||||
zephyr,settings-partition = &storage_partition;
|
||||
};
|
||||
};
|
||||
|
||||
&flash0 {
|
||||
partitions {
|
||||
/* App slots — equal size (swap-mode compatible), slot0 at 0x10000. */
|
||||
slot0_partition: partition@10000 {
|
||||
label = "image-0";
|
||||
reg = <0x10000 0x7C6000>;
|
||||
};
|
||||
slot1_partition: partition@7D6000 {
|
||||
label = "image-1";
|
||||
reg = <0x7D6000 0x7C6000>;
|
||||
};
|
||||
/* BLE bond NVS — offset unchanged so bonds survive the reflash. */
|
||||
storage_partition: partition@F9C000 {
|
||||
label = "storage";
|
||||
reg = <0xF9C000 0x4000>;
|
||||
};
|
||||
/* LittleFS (identity/prefs/contacts) — offset unchanged. */
|
||||
lfs_partition: partition@FA0000 {
|
||||
label = "lfs";
|
||||
reg = <0xFA0000 0x60000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Elecrow ThinkNode M9 — Pin Control Definitions
|
||||
*
|
||||
* Pin mapping (from Arduino MeshCore variants/thinknode_m9):
|
||||
* Shared SPI2: SCLK=GPIO40, MOSI=GPIO47, MISO=GPIO38
|
||||
* (LR1110 NSS=GPIO39, ST7789 CS=GPIO16, SD CS=GPIO48 unused)
|
||||
* I2C0: SDA=GPIO7, SCL=GPIO6 (PCF8563 RTC + env sensors)
|
||||
* UART1: RX=GPIO2, TX=GPIO3 (L76K GPS; upstream setPins(2,3))
|
||||
* LEDC ch0: GPIO9 (buzzer)
|
||||
* Console: uart0 GPIO43/44 via esp32s3_console_uart0.dtsi (board.overlay)
|
||||
*/
|
||||
|
||||
#include <zephyr/dt-bindings/pinctrl/esp-pinctrl-common.h>
|
||||
#include <zephyr/dt-bindings/pinctrl/esp32s3-pinctrl.h>
|
||||
#include <zephyr/dt-bindings/pinctrl/esp32s3-gpio-sigmap.h>
|
||||
|
||||
&pinctrl {
|
||||
/* SPI2 — LR1110 radio + ST7789 TFT (CS per device via cs-gpios) */
|
||||
spim2_default: spim2_default {
|
||||
group1 {
|
||||
pinmux = <SPIM2_MISO_GPIO38>,
|
||||
<SPIM2_SCLK_GPIO40>;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <SPIM2_MOSI_GPIO47>;
|
||||
output-low;
|
||||
};
|
||||
};
|
||||
|
||||
/* I2C0 — PCF8563 RTC + optional environment sensors */
|
||||
i2c0_default: i2c0_default {
|
||||
group1 {
|
||||
pinmux = <I2C0_SDA_GPIO7>,
|
||||
<I2C0_SCL_GPIO6>;
|
||||
bias-pull-up;
|
||||
drive-open-drain;
|
||||
output-high;
|
||||
};
|
||||
};
|
||||
|
||||
/* UART1 — L76K GPS (GPIO2 = GPS TX → CPU RX, GPIO3 = GPS RX → CPU TX) */
|
||||
uart1_default: uart1_default {
|
||||
group1 {
|
||||
pinmux = <UART1_TX_GPIO3>;
|
||||
output-high;
|
||||
};
|
||||
group2 {
|
||||
pinmux = <UART1_RX_GPIO2>;
|
||||
bias-pull-up;
|
||||
};
|
||||
};
|
||||
|
||||
/* LEDC channel 0 — buzzer PWM on GPIO9 */
|
||||
ledc0_default: ledc0_default {
|
||||
group1 {
|
||||
pinmux = <LEDC_CH0_GPIO9>;
|
||||
output-enable;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* Elecrow ThinkNode M9 — ESP32-S3, 16MB flash, 8MB PSRAM
|
||||
*
|
||||
* Hardware:
|
||||
* MCU: ESP32-S3-WROOM-1 (Xtensa LX7, dual-core @ 160 MHz)
|
||||
* Flash: 16 MB
|
||||
* PSRAM: 8 MB (OPI) — module variant assumed N16R8, verify at bring-up
|
||||
* Radio: Semtech LR1110 on shared SPI2 (TCXO 3.3V, DIO5/DIO6 RF switch)
|
||||
* Display: ST7789 320x240 TFT on shared SPI2 (defined in board.overlay)
|
||||
* GPS: CC1167Q (Unicore family; NOT the L76K MeshCore's variant.h
|
||||
* claims) on UART1 @ 115200
|
||||
* RTC: PCF8563 @ 0x51 on I2C0
|
||||
* Console: Native USB Serial/JTAG (main firmware reroutes to uart0 and
|
||||
* gives the USB port to the OTG companion transport — see
|
||||
* board.overlay).
|
||||
*
|
||||
* The LR1110 node lives HERE (not board.overlay) so the standalone
|
||||
* tools/lr1110_updater build — which replaces board.overlay with its own
|
||||
* minimal overlay — still sees the radio.
|
||||
*/
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
#include <espressif/esp32s3/esp32s3_wroom_n16r8.dtsi>
|
||||
#include "thinknode_m9-pinctrl.dtsi"
|
||||
#include <espressif/partitions_0x0_default_16M.dtsi>
|
||||
|
||||
/ {
|
||||
model = "Elecrow ThinkNode M9";
|
||||
compatible = "elecrow,thinknode-m9";
|
||||
|
||||
chosen {
|
||||
zephyr,sram = &sram1;
|
||||
zephyr,console = &usb_serial;
|
||||
zephyr,shell-uart = &usb_serial;
|
||||
zephyr,flash = &flash0;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
zephyr,bt-hci = &esp32_bt_hci;
|
||||
};
|
||||
|
||||
aliases {
|
||||
uart-0 = &uart0;
|
||||
i2c-0 = &i2c0;
|
||||
watchdog0 = &wdt0;
|
||||
lora0 = &lora;
|
||||
};
|
||||
};
|
||||
|
||||
/* Reduce CPU clock from 240 MHz to 160 MHz — HAL Kconfig minimum supported */
|
||||
&cpu0 {
|
||||
clock-frequency = <DT_FREQ_M(160)>;
|
||||
};
|
||||
|
||||
&cpu1 {
|
||||
clock-frequency = <DT_FREQ_M(160)>;
|
||||
};
|
||||
|
||||
&usb_serial {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&i2c0 {
|
||||
status = "okay";
|
||||
clock-frequency = <I2C_BITRATE_FAST>;
|
||||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&spi2 {
|
||||
status = "okay";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
pinctrl-0 = <&spim2_default>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
/* CS 0 = LR1110 NSS (GPIO39), CS 1 = ST7789 TFT (GPIO16).
|
||||
* The TFT itself is declared in board.overlay (mipi-dbi child @1). */
|
||||
cs-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>,
|
||||
<&gpio0 16 GPIO_ACTIVE_LOW>;
|
||||
|
||||
lora: lora@0 {
|
||||
compatible = "semtech,lr1110";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <8000000>;
|
||||
|
||||
reset-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; /* GPIO45 */
|
||||
busy-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; /* GPIO41 */
|
||||
dio1-gpios = <&gpio1 10 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* GPIO42 */
|
||||
|
||||
/* TCXO 3.3V via DIO3 (Arduino LR11X0_DIO3_TCXO_VOLTAGE=3.3) */
|
||||
tcxo-voltage-mv = <3300>;
|
||||
tcxo-startup-delay-ms = <5>;
|
||||
|
||||
/* +3dB RX sensitivity for +2mA — deliberate divergence from the
|
||||
* Arduino variant, which leaves boosted gain off (looks like an
|
||||
* oversight; T1000-E and every other ZephCore LR1110 board use it). */
|
||||
rx-boosted;
|
||||
|
||||
/* RF switch truth table from Arduino rfswitch_table[]:
|
||||
* bit0 = DIO5, bit1 = DIO6.
|
||||
* STBY: DIO5=L DIO6=L RX: DIO5=H DIO6=L
|
||||
* TX: DIO5=H DIO6=H TX_HP: DIO5=L DIO6=H
|
||||
* (MODE_TX = low-power PA path, MODE_TX_HP = high-power PA path) */
|
||||
rfswitch-enable = <0x03>;
|
||||
rfswitch-standby = <0x00>;
|
||||
rfswitch-rx = <0x01>;
|
||||
rfswitch-tx = <0x03>;
|
||||
rfswitch-tx-hp = <0x02>;
|
||||
rfswitch-gnss = <0x00>;
|
||||
};
|
||||
};
|
||||
|
||||
/* GPS — 115200 baud (the macro Arduino MeshCore actually consumes; the
|
||||
* variant.h 9600 value is a dead macro). GNSS driver node + enable/reset
|
||||
* pins live in board.overlay. */
|
||||
&uart1 {
|
||||
status = "okay";
|
||||
current-speed = <115200>;
|
||||
pinctrl-0 = <&uart1_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
&gpio0 { status = "okay"; };
|
||||
|
||||
&gpio1 {
|
||||
status = "okay";
|
||||
|
||||
/* Park the SD card's CS (GPIO48) HIGH (deselected — the CS itself is
|
||||
* active-low). The SD slot shares SPI2 with the LR1110 and the TFT but
|
||||
* has no Zephyr driver; left floating, an inserted card could drive MISO
|
||||
* and corrupt radio/display transfers. Also protects the standalone
|
||||
* lr1110_updater (this hog lives in the base DTS). */
|
||||
sd-cs-park {
|
||||
gpio-hog;
|
||||
gpios = <16 GPIO_ACTIVE_HIGH>; /* GPIO48 */
|
||||
output-high; /* physical HIGH = deselected */
|
||||
};
|
||||
};
|
||||
&wdt0 { status = "okay"; };
|
||||
&trng0 { status = "okay"; };
|
||||
|
||||
&esp32_bt_hci {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&wifi {
|
||||
status = "okay";
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
# Elecrow ThinkNode M9 — minimal Zephyr defconfig (procpu)
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_UART_CONSOLE=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_CLOCK_CONTROL=y
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_NVS=y
|
||||
@@ -47,6 +47,7 @@ SWD flash: `west flash` (requires J-Link, pyocd, or nrfjprog connected).
|
||||
| Heltec Wireless Tracker V1.1 | `west build -b heltec_wireless_tracker/esp32s3/procpu zephcore` | `west flash` |
|
||||
| Heltec Wireless Tracker V2 | `west build -b heltec_wireless_tracker_v2/esp32s3/procpu zephcore` | `west flash` |
|
||||
| LilyGo T-Beam v1.2 | `west build -b ttgo_tbeam/esp32/procpu zephcore` | `west flash` |
|
||||
| ThinkNode M9 | `west build -b thinknode_m9/esp32s3/procpu zephcore` | `west flash` |
|
||||
|
||||
**Heltec V3 console:** ZephCore routes console/shell to `uart0` on V3. Use the UART serial port for boot logs and CLI.
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
# ZephCore LR1110 Firmware Updater — updates LR1110 radio firmware to 0x0401.
|
||||
#
|
||||
# Board-specific: only builds for boards with an LR1110 radio (e.g., t1000_e).
|
||||
# Board-specific: only builds for boards with an LR1110 radio
|
||||
# (t1000_e, thinknode_m9).
|
||||
#
|
||||
# Build: west build -b t1000_e/nrf52840 zephcore/tools/lr1110_updater --pristine
|
||||
# Flash: drag-drop build/zephyr/zephyr.uf2 onto UF2 drive
|
||||
# west build -b thinknode_m9/esp32s3/procpu zephcore/tools/lr1110_updater --pristine
|
||||
# Flash: nRF52: drag-drop build/zephyr/zephyr.uf2 onto UF2 drive
|
||||
# (or: nrfjprog --program build/zephyr/zephyr.hex --sectorerase -r)
|
||||
# ESP32: west flash --esp-device COMX
|
||||
#
|
||||
# After the updater finishes, flash ZephCore main firmware.
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# ThinkNode M9 (ESP32-S3) LR1110 updater — overrides for the nRF-flavored prj.conf
|
||||
#
|
||||
# Console is the native USB Serial/JTAG (base DTS chosen) — no USB device
|
||||
# stack, no CDC ACM, no UF2, no DT code partition (ESP Simple Boot links the
|
||||
# image itself and west flash writes it from 0x0).
|
||||
|
||||
CONFIG_BUILD_OUTPUT_UF2=n
|
||||
CONFIG_USE_DT_CODE_PARTITION=n
|
||||
CONFIG_USB_DEVICE_STACK_NEXT=n
|
||||
CONFIG_UART_LINE_CTRL=n
|
||||
CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n
|
||||
CONFIG_CDC_ACM_SERIAL_ENABLE_AT_BOOT=n
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* ThinkNode M9 LR1110 Updater overlay
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* Minimal overlay — the LR1110 lives in the base board DTS (SPI2), and the
|
||||
* base chosen console is the native USB Serial/JTAG, which is exactly what
|
||||
* the updater wants (the main firmware's uart0 reroute lives in the main
|
||||
* board.overlay, which this file replaces).
|
||||
*
|
||||
* Disables peripherals not needed for the update.
|
||||
*/
|
||||
|
||||
/* Disable UART1 (GPS) — not needed */
|
||||
&uart1 {
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
/* Disable I2C0 (RTC/sensors) — not needed */
|
||||
&i2c0 {
|
||||
status = "disabled";
|
||||
};
|
||||
@@ -64,16 +64,26 @@ static void led_toggle(void) {}
|
||||
|
||||
/* ── Helpers ──────────────────────────────────────────────────── */
|
||||
|
||||
static void reboot_to_uf2(void)
|
||||
static void updater_done(void)
|
||||
{
|
||||
#if defined(CONFIG_SOC_SERIES_NRF52)
|
||||
printk("\nRebooting into UF2 DFU mode...\n");
|
||||
printk("You can now drag-drop ZephCore firmware UF2.\n");
|
||||
k_msleep(500);
|
||||
|
||||
#if defined(CONFIG_SOC_SERIES_NRF52)
|
||||
nrf_power_gpregret_set(NRF_POWER, 0, BOOTLOADER_DFU_UF2_MAGIC);
|
||||
#endif
|
||||
sys_reboot(SYS_REBOOT_COLD);
|
||||
#else
|
||||
/* ESP32: no persistent DFU mode to reboot into, and a plain reboot
|
||||
* would just re-run the updater. Idle here — west flash / esptool
|
||||
* resets the chip itself when writing the main firmware. */
|
||||
printk("\nFlash ZephCore main firmware over USB now:\n");
|
||||
printk(" west flash --esp-device <COMx>\n");
|
||||
while (1) {
|
||||
led_toggle();
|
||||
k_msleep(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fatal_error(const char *msg)
|
||||
@@ -149,7 +159,7 @@ int main(void)
|
||||
printk("\nAlready running target firmware 0x%04X — no update needed!\n",
|
||||
TARGET_FW_VERSION);
|
||||
led_off();
|
||||
reboot_to_uf2();
|
||||
updater_done();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -306,9 +316,9 @@ int main(void)
|
||||
led_off();
|
||||
}
|
||||
|
||||
/* ── Reboot to UF2 DFU ── */
|
||||
printk("\n[10/10] Done! Rebooting to UF2 DFU mode...\n");
|
||||
reboot_to_uf2();
|
||||
/* ── Hand off to main-firmware flashing ── */
|
||||
printk("\n[10/10] Done!\n");
|
||||
updater_done();
|
||||
|
||||
return 0; /* never reached */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user