mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 19:38:20 +00:00
cleanup/bugfix run
This commit is contained in:
@@ -54,7 +54,7 @@ Other benefits:
|
||||
| **Heltec Wireless Tracker V2** | ESP32-S3FN8 | SX1262 + KCT8103L PA/FEM | ST7735R 160x80 TFT, UC6580 GNSS, battery ADC |
|
||||
| **ThinkNode M9** | ESP32-S3 | LR1110 | ST7789 320x240 TFT, CC1167Q GPS, PCF8563 RTC, buzzer, 16MB flash, 8MB PSRAM |
|
||||
| **LilyGo T-Beam v1.2** | ESP32 (PICO-D4) | SX1262 | AXP2101 PMU, GNSS, USB-UART CLI |
|
||||
| **TTGO LoRa32** | ESP32 (PICO-D4) | **SX1276** (loramac-node backend) | SX127x reference board, USB-UART CLI |
|
||||
| **TTGO LoRa32** | ESP32 (PICO-D4) | **SX1276** (loramac-node backend) | SX127x reference board — **source-only, no published firmware** |
|
||||
|
||||
### Other
|
||||
|
||||
|
||||
@@ -245,11 +245,15 @@ Proposed by **bisbille** ([@bisbille](https://github.com/bisbille)) —
|
||||
|
||||
Housekeeping, listed for completeness — nothing here changes how a node behaves day to day.
|
||||
|
||||
- **The XIAO nRF54L15 now ships firmware with every release.** The board has been supported for a
|
||||
while but was never part of the published build, so it had to be compiled by hand. Companion and
|
||||
repeater images are now built and listed alongside every other board. Like the ME25LS02 it is a
|
||||
`.hex` flashed over SWD — though the XIAO's own USB cable is enough, since it has a debug bridge
|
||||
built into the board.
|
||||
- **Three boards that were supported but never published now ship firmware with every release.** All
|
||||
three have been buildable for a while but were missing from the published build, so they had to be
|
||||
compiled by hand. Companion and repeater images are now built and listed alongside every other
|
||||
board:
|
||||
- **XIAO nRF54L15** — like the ME25LS02, a `.hex` flashed over SWD, though the XIAO's own USB cable
|
||||
is enough since it has a debug bridge built into the board.
|
||||
- **XIAO MG24** — also SWD-only, and the first Silicon Labs board to get published firmware.
|
||||
- **LoRa-E5 mini** — SWD-only. No Bluetooth on this chip, so the companion talks to the app over
|
||||
the USB serial port instead.
|
||||
- **The Zephyr operating system underneath was updated** to a newer snapshot, and our radio patches were
|
||||
reorganised on top of it: five separate SX126x patches are now one. Same behaviour, fewer things to go
|
||||
wrong the next time Zephyr moves.
|
||||
|
||||
@@ -1221,10 +1221,10 @@ Timeouts and deadlines that are watchdogs in everything but name:
|
||||
|
||||
| Mechanism | Location | Bounds |
|
||||
|-----------|----------|--------|
|
||||
| RX-latch payload deadline | all three custom radio paths: SX126x `patch 0013`, `lr11xx_lora.c`, `lr20xx_lora.c` (`header_seen_at_ms` + `*_max_payload_ms()`) | A `HEADER_VALID` whose packet never completes would pin the TX gate closed and silently mute the node — continuous RX has no symbol timer. Released at 255-byte airtime +25% +100 ms. See [5.2.1](#521-rx-busy-gate-tx-during-rx-prevention). |
|
||||
| RX-latch payload deadline | all three custom radio paths: SX126x `patch 0003` ("Bound the lifetime of the RX-busy latch"), `lr11xx_lora.c`, `lr20xx_lora.c` (`header_seen_at_ms` + `*_max_payload_ms()`) | A `HEADER_VALID` whose packet never completes would pin the TX gate closed and silently mute the node — continuous RX has no symbol timer. Released at 255-byte airtime +25% +100 ms. See [5.2.1](#521-rx-busy-gate-tx-during-rx-prevention). |
|
||||
| Stuck-DIO1 counter | `lr11xx_lora.c` and `lr20xx_lora.c` | 5 empty DIO1 cycles → hardware reset. Counting rather than timing; on the LR11xx it complements the wedge watchdog rather than replacing it. |
|
||||
| CAD timeout | `Dispatcher::checkSend()` | 4 s (~20 retry attempts) → `ERR_EVENT_CAD_TIMEOUT` + `recoverRxState()`, rather than falling through to TX. See [5.2.2](#522-cad-timeout-recovery). |
|
||||
| Chip-side TX timeout | SX126x `SetTx` deadline (`patch 0014`, airtime +25% +500 ms, floored at 10 s, clamped 262143 ms); LR2021 `TIMEOUT` IRQ handler | The chip stops the transmission when this fires, so a fixed value is a truncation, not a safeguard — at SF12/BW62.5 the old flat 10 s cut every packet from 76 bytes up. |
|
||||
| Chip-side TX timeout | SX126x `SetTx` deadline (`patch 0003`, "Scale the chip-side Tx timeout from airtime instead of a fixed 10 s"; airtime +25% +500 ms, floored at 10 s, clamped 262143 ms); LR2021 `TIMEOUT` IRQ handler | The chip stops the transmission when this fires, so a fixed value is a truncation, not a safeguard — at SF12/BW62.5 the old flat 10 s cut every packet from 76 bytes up. |
|
||||
| Serial partial-frame resync | `SerialCompanionTransport.c` (`FRAME_PARTIAL_TIMEOUT_MS`) | 2 s. Parser-level only — deliberately **not** a session or idle timeout; an idle-but-connected companion sits in `RX_IDLE` indefinitely. |
|
||||
| TCP send timeout | `LinuxTCPTransport.c` | Native sim only. A peer that can't accept a frame in the window is wedged → close it, rather than hang the whole queue. |
|
||||
| Bounded RXTO wait | `patches/zephyr/0010-uarte-pm-suspend-bounded-rxto-wait.patch` | `uarte_pm_suspend()` busy-waits for RXTO with no timeout upstream. Landing in the STOPRX race with bytes in flight spins forever on the main thread and wedges the entire mesh (observed: RAK3401 1W repeater on 1.16.6, CLI answering only `-> busy`). Backstop for the GPS UART PM path in [7.3](#73-gps-adaptersgps). |
|
||||
|
||||
@@ -1170,8 +1170,15 @@ bool LoRaRadioBase::isReceiving()
|
||||
|
||||
void LoRaRadioBase::agcMaintenance()
|
||||
{
|
||||
/* Never mid-transmit: both operations sleep the chip. */
|
||||
if (atomic_get(&_tx_active)) {
|
||||
/* Never mid-transmit or mid-receive: both operations warm-sleep the
|
||||
* chip, which aborts a TX and destroys an in-flight packet. The RX
|
||||
* half cannot be inferred from the activity counters below — those
|
||||
* only move at RX_DONE/CRC_ERR, so a packet whose preamble is landing
|
||||
* right now still reads as silence. isReceiving() is the latch that
|
||||
* knows (HEADER_VALID promotion + preamble grace); it is the same gate
|
||||
* checkSend() and the noise-floor sampler use before touching the chip.
|
||||
* Bailing here just defers the work to the next maintenance pass. */
|
||||
if (atomic_get(&_tx_active) || isReceiving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,10 +63,12 @@ static const uint32_t ntc_res[NTC_TABLE_LEN] = {
|
||||
* Photocell
|
||||
*
|
||||
* Seeed maps the divider voltage onto 0-100 with a dead band at each end.
|
||||
* LIGHT_SPAN_MV is deliberately not (LIGHT_MAX_MV - LIGHT_MIN_MV): the
|
||||
* stock firmware divides the 80..2480 mV range by 2400 while subtracting
|
||||
* only the 80 mV floor, and the top of the range is clamped rather than
|
||||
* reached. Reproduced as-is so readings match.
|
||||
* LIGHT_SPAN_MV happens to equal LIGHT_MAX_MV - LIGHT_MIN_MV today, but it is
|
||||
* kept as its own constant rather than derived from them: the stock firmware
|
||||
* treats the divisor as an independent number (subtract the 80 mV floor,
|
||||
* divide by 2400, clamp at the top rather than reach it). Deriving it would
|
||||
* let a future tweak to either endpoint silently move the scale factor away
|
||||
* from what the stock firmware uses, and the readings would stop matching.
|
||||
* ================================================================ */
|
||||
|
||||
#define LIGHT_MIN_MV 80
|
||||
@@ -198,6 +200,14 @@ static int t1000e_power(const struct t1000e_config *cfg, bool on)
|
||||
int rc = 0;
|
||||
|
||||
if (on) {
|
||||
/* The rail is shared with the battery divider (one devicetree node,
|
||||
* two labels: sensor_power / vbat_enable). The regulator core
|
||||
* refcounts, so if another holder already has it up there is no
|
||||
* off->on transition to wait out — settling only costs time when we
|
||||
* are the one turning it on. */
|
||||
bool already_on = (cfg->power_supply != NULL) &&
|
||||
regulator_is_enabled(cfg->power_supply);
|
||||
|
||||
if (cfg->power_supply != NULL) {
|
||||
rc = regulator_enable(cfg->power_supply);
|
||||
if (rc < 0) {
|
||||
@@ -210,7 +220,9 @@ static int t1000e_power(const struct t1000e_config *cfg, bool on)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
k_msleep(cfg->settle_time_ms);
|
||||
if (!already_on) {
|
||||
k_msleep(cfg->settle_time_ms);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1117,6 +1117,13 @@ bool CompanionMesh::vcontactHandleFrame(const uint8_t *data, size_t len)
|
||||
if (ack == 0) ack = 1;
|
||||
|
||||
sendPacketSent(MSG_SEND_SENT_DIRECT, ack, 3000);
|
||||
/* One slot, not a queue: a second v-contact message inside
|
||||
* VCONTACT_CONFIRM_DELAY_MS would overwrite the first ack before
|
||||
* its work ever ran, so that message stayed un-confirmed until the
|
||||
* app hit est_timeout and resent it. Flush the pending one first —
|
||||
* it is already past the sub-millisecond window that made deferring
|
||||
* necessary, so emitting it now is safe. */
|
||||
vcontactFlushConfirm();
|
||||
_vcontact_confirm_ack = ack;
|
||||
k_work_reschedule(&_vcontact_confirm_work.work,
|
||||
K_MSEC(VCONTACT_CONFIRM_DELAY_MS));
|
||||
@@ -2169,6 +2176,11 @@ bool CompanionMesh::handleProtocolFrame(const uint8_t *data, size_t len)
|
||||
_contact_iter_active = false;
|
||||
cancelSyncPending();
|
||||
cleanupSignState();
|
||||
/* Drop a deferred v-contact confirm from the previous session: its ack
|
||||
* matches an outbound message the new session never sent, so delivering
|
||||
* it here would push an unmatched SEND_CONFIRMED into a fresh app. */
|
||||
_vcontact_confirm_ack = 0;
|
||||
k_work_cancel_delayable(&_vcontact_confirm_work.work);
|
||||
|
||||
/* New session: suppress v-contact notice MSG_WAITING until the initial
|
||||
* sync (contacts + messages) completes at PACKET_NO_MORE_MSGS. */
|
||||
|
||||
@@ -7,9 +7,15 @@
|
||||
#
|
||||
# Include order: prj.conf → zephcore_common.conf → esp32_common.conf → board.conf
|
||||
|
||||
# Board identification
|
||||
CONFIG_ZEPHCORE_BOARD_NAME="TTGO LoRa32"
|
||||
CONFIG_BT_DIS_MODEL_NUMBER_STR="TTGO LoRa32"
|
||||
# Board identification.
|
||||
# "ttgo_lora32" is the upstream Zephyr board name and stays as-is, but the name
|
||||
# the node reports follows Arduino MeshCore: LilyGoTLoraBoard::getManufacturerName()
|
||||
# in variants/lilygo_tlora_v2_1 returns exactly this string, so a ZephCore node
|
||||
# and a stock MeshCore node identify themselves identically on the mesh.
|
||||
# (MeshCore's web flasher lists the same hardware under a third spelling,
|
||||
# "LilyGo LoRa32 V2.1_1.6" — irrelevant here, we publish no firmware for it.)
|
||||
CONFIG_ZEPHCORE_BOARD_NAME="LILYGO T-LoRa V2.1-1.6"
|
||||
CONFIG_BT_DIS_MODEL_NUMBER_STR="LILYGO T-LoRa V2.1-1.6"
|
||||
|
||||
# Radio: SX1276 via Zephyr loramac-node driver (not the native SX126x driver)
|
||||
# zephcore_common.conf sets CONFIG_LORA_MODULE_BACKEND_NATIVE=y for SX126x boards.
|
||||
@@ -26,14 +32,21 @@ CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=17
|
||||
# lora_recv_async, but disable it to avoid the spurious attempt)
|
||||
CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE=n
|
||||
|
||||
# Companion RAM budget — same constraint as the T-Beam: the classic ESP32's
|
||||
# contiguous DRAM segment can't fit the default contact/queue arrays (350/256)
|
||||
# alongside the BT controller heap. The T-Beam sizes (160/8/128) still
|
||||
# overflow here by ~1.2 KB — the SX1276 loramac-node backend carries more
|
||||
# static DRAM than the native SX126x driver — so contacts go to 150.
|
||||
CONFIG_ZEPHCORE_MAX_CONTACTS=150
|
||||
# Companion RAM budget — the binding constraint on this board, and tighter than
|
||||
# the T-Beam's. The classic ESP32's contiguous DRAM segment cannot fit the
|
||||
# default contact/queue arrays (350/256) alongside the BT controller heap, and
|
||||
# this board pays for an SSD1306 display and the SX1276 loramac-node backend on
|
||||
# top, neither of which the T-Beam carries.
|
||||
#
|
||||
# Measured, not guessed (ttgo_lora32/esp32/procpu companion, dram0_0_seg is
|
||||
# 140452 B): 150/128 overflows by 32016 B. A contact costs ~188 B and an
|
||||
# offline-queue slot 173 B (QueuedFrame: 1 + 172), so the two arrays are the
|
||||
# whole lever. 50/32 lands at ~97% with a few KB to spare; raising either one
|
||||
# spends that margin at those rates. Do not restore 150/128 — it has never
|
||||
# linked on this board.
|
||||
CONFIG_ZEPHCORE_MAX_CONTACTS=50
|
||||
CONFIG_ZEPHCORE_MAX_CHANNELS=8
|
||||
CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE=128
|
||||
CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE=32
|
||||
|
||||
# Flash mode: ESP32-PICO-D4 (rev 1.0) — use DIO, not QIO.
|
||||
# esp32_common.conf enables QIO for the ESP32-S3/C3/C6 boards in this repo.
|
||||
|
||||
@@ -98,7 +98,9 @@ for `0x1000`, since they use simple-boot in the release build too.
|
||||
|
||||
### SX127x Boards (loramac-node backend)
|
||||
|
||||
ZephCore supports SX1272/SX1276/SX1278 via the loramac-node backend — a separate radio path from the native SX126x driver used by all other boards. The TTGO LoRa32 is the reference implementation:
|
||||
ZephCore can drive SX1272/SX1276/SX1278 via the loramac-node backend — a separate radio path from the native SX126x driver used by all other boards. The TTGO LoRa32 is the only board exercising it.
|
||||
|
||||
**This path is source-only and unsupported.** No release publishes firmware for it, it is not in `build.sh` or the Mesh America catalog, and it has no RX duty cycle and no RX gain boost. Treat it as a starting point to maintain yourself, not as a supported target.
|
||||
|
||||
| Board | Build string | Flash |
|
||||
|----------------|------------------------------------------------|--------------|
|
||||
|
||||
@@ -49,7 +49,7 @@ heltec_wireless_tracker/esp32s3/procpu
|
||||
heltec_wireless_tracker_v2/esp32s3/procpu
|
||||
thinknode_m9/esp32s3/procpu
|
||||
ttgo_tbeam/esp32/procpu
|
||||
ttgo_lora32/esp32/procpu
|
||||
ttgo_lora32/esp32/procpu # source-only, no published firmware
|
||||
```
|
||||
|
||||
> ESP32 boards require `west blobs fetch hal_espressif` before first build.
|
||||
@@ -76,8 +76,21 @@ ttgo_lora32/esp32/procpu
|
||||
> SX1262 in `board.overlay`. Console/CLI are on `uart0` (onboard USB-UART).
|
||||
>
|
||||
> **TTGO LoRa32** (`ttgo_lora32/esp32/procpu`): classic ESP32 (PICO-D4) with
|
||||
> **SX1276** — the SX127x (loramac-node backend) reference board. Console/CLI
|
||||
> on `uart0`.
|
||||
> **SX1276**. Console/CLI on `uart0`.
|
||||
>
|
||||
> **Source-only — no published firmware, and not a supported configuration.**
|
||||
> It is deliberately absent from `build.sh`, the release workflow and the Mesh
|
||||
> America catalog, so no release ever carries a binary for it. It is kept in the
|
||||
> tree as the one exercise of the SX127x loramac-node radio path, which is old,
|
||||
> lacks RX duty cycle and RX gain boost, and gets none of the work the native
|
||||
> SX126x/LR11xx/LR20xx drivers do. Build it yourself if you want it; expect to
|
||||
> maintain it yourself too.
|
||||
>
|
||||
> If you do build the companion, note it is DRAM-bound (~97% of `dram0_0_seg`)
|
||||
> and capped at **50 contacts / 32 queued offline messages** — lower than any
|
||||
> other companion board. The repeater has no such limit. At the 150/128 this
|
||||
> board's config used to declare, it did not link at all. See `board.conf` for
|
||||
> the measured budget before raising either number.
|
||||
|
||||
## STM32WL
|
||||
|
||||
|
||||
@@ -550,10 +550,14 @@ static void action_deep_sleep(void)
|
||||
* because the multi-tap filter passes an unhandled raw KEY_A through per tap
|
||||
* before it resolves; swallowing that would leave a double tap still acting.
|
||||
*
|
||||
* No expiry is needed: on every board using this UI each switch case below is
|
||||
* fed by the longpress or multi-tap filter, so a wake is always followed by an
|
||||
* action code that clears the flag. (The two boards with direct joystick codes
|
||||
* select ZEPHCORE_UI_JOYSTICK and never reach this callback.)
|
||||
* No expiry is needed, but the flag must only be armed when the wake press was
|
||||
* a raw code that still has an action code coming. ZEPHCORE_UI_DESIGN_JOYSTICK
|
||||
* depends on ZEPHCORE_ROLE_COMPANION, so the repeater build of a joystick board
|
||||
* (wio_tracker_l1, gat562_30s — both shipped as repeater artifacts) falls back
|
||||
* to ZEPHCORE_UI_DESIGN_BUTTON and *does* reach this callback, with its
|
||||
* joystick GPIOs wired straight to INPUT_KEY_UP/DOWN/LEFT/RIGHT/ENTER and no
|
||||
* filter in between. There the wake press is already an action code and is
|
||||
* consumed at the wake, so the flag stays clear and the next press acts.
|
||||
*/
|
||||
static bool display_woken_pending;
|
||||
|
||||
@@ -636,10 +640,17 @@ static void ui_input_cb(struct input_event *evt, void *user_data)
|
||||
#ifdef CONFIG_ZEPHCORE_UI_DISPLAY
|
||||
/* If display is off, wake it and consume the event. The action this
|
||||
* press resolves to arrives later from the longpress / multi-tap filter;
|
||||
* display_woken_pending makes the switch below swallow it. */
|
||||
* display_woken_pending makes the switch below swallow it.
|
||||
*
|
||||
* Only arm that flag when this press really does resolve into a *later*
|
||||
* action code, i.e. when the code we just consumed is a raw one. Boards
|
||||
* that wire an action code straight to the GPIO (joystick lines on
|
||||
* wio_tracker_l1 / gat562_30s in repeater builds) already consumed their
|
||||
* action here — arming the flag would make the next, genuinely separate
|
||||
* press get swallowed instead. */
|
||||
if (!mc_display_is_on()) {
|
||||
mc_display_on();
|
||||
display_woken_pending = true;
|
||||
display_woken_pending = !is_ui_action_code(evt->code);
|
||||
schedule_render();
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user