fix build and update readmes

This commit is contained in:
liquidraver
2026-06-17 22:31:47 +02:00
parent 906ef08b05
commit 28f1c3af05
5 changed files with 87 additions and 20 deletions
+5 -1
View File
@@ -46,6 +46,8 @@ Other benefits:
| **Heltec V3** | ESP32-S3 | SX1262 | OLED (SSD1306), 8MB flash |
| **Heltec V4.2** | ESP32-S3 | SX1262 + GC1109 PA | OLED (SSD1306), 16MB flash, 2MB PSRAM |
| **Heltec V4.3** | ESP32-S3 | SX1262 + KCT8103L PA | OLED (SSD1306), 16MB flash, 2MB PSRAM |
| **Heltec Wireless Tracker** | ESP32-S3 | SX1262 | ST7735R 160×80 TFT, UC6580 GPS |
| **LilyGo T-Beam v1.2** | ESP32 (PICO-D4) | SX1262 | AXP2101 PMU, GNSS, USB-UART CLI |
### Other
@@ -204,7 +206,9 @@ Key Kconfig options (set in board configs or via `-D` flags):
| `CONFIG_ZEPHCORE_MAX_CONTACTS` | 350 | Contact storage slots (companion) |
| `CONFIG_ZEPHCORE_MAX_CHANNELS` | 40 | Channel slots (companion) |
| `CONFIG_ZEPHCORE_BLE_PASSKEY` | 123456 | BLE pairing PIN |
| `CONFIG_ZEPHCORE_GPS_POLL_INTERVAL_SEC` | 300 | GPS fix interval (seconds) |
| `CONFIG_ZEPHCORE_GPS_POLL_INTERVAL_SEC` | 300 | Companion GPS duty interval between fixes (seconds); 0 = always-on |
| `CONFIG_ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` | 300 | Cold-start window for the very first fix (longer to allow almanac download) |
| `CONFIG_ZEPHCORE_REPEATER_GPS_INTERVAL_SEC` | 172800 | Repeater/room-server GPS duty interval boot default (48 h); 0 = always-on |
| `CONFIG_ZEPHCORE_WIFI_OTA` | n | WiFi AP + HTTP OTA updates (ESP32 repeaters, requires `--sysbuild`) |
| `CONFIG_ZEPHCORE_REPEATER_UPLINK` | n | Repeater WiFi+MQTT uplink (ESP32) |
| `CONFIG_ZEPHCORE_PACKET_LOGGING` | n | Arduino-compatible mesh packet logging |
+37 -5
View File
@@ -429,8 +429,9 @@ Power: `powersaving on/off`
- Nordic UART Service (NUS) with AUTHEN permissions on CCC + RX (forces pairing)
- Passkey-based MITM pairing (SC + MITM + Bonding), runtime configurable PIN via `app_passkey` callback
- DisplayOnly IO capability — phone enters passkey displayed on device / known to user
- Advertising with public identity address (no RPA) — required for Android Flutter BLE app compatibility
- `CONFIG_BT_PRIVACY` disabled: Android's Flutter BLE plugin fails to `connectGatt()` to RPA-advertised devices from app context; iOS and Android system BT settings handle RPA fine but the MeshCore app doesn't. Arduino MeshCore also uses public addresses.
- Advertising always uses `BT_LE_ADV_OPT_USE_IDENTITY` — exposes the stable identity address even when privacy is enabled, preserving Android connect-from-app
- `CONFIG_BT_PRIVACY` **disabled** on nRF52840 / MG24: identity address is advertised directly; both iOS and Android work without RPA. Android's Flutter BLE plugin fails `connectGatt()` to RPA-advertised devices from app context.
- `CONFIG_BT_PRIVACY` **enabled** on ESP32-S3 (`boards/common/esp32_common.conf`): the Espressif controller's privacy-OFF Secure-Connections path produces a MIC failure against iOS (HCI disconnect `0x3d` at encryption start). Privacy ON keeps the controller on its working SC path. `USE_IDENTITY` advertising preserves Android compatibility. Do **not** remove `USE_IDENTITY` while ESP32 privacy is on.
- Pairing triggered reactively: phone hits ATT error 0x05 on secured attribute → initiates SMP pairing (Apple Accessory Design Guidelines §55 compliant — no proactive Security Request)
- TX congestion control: queue (12 frames) + overflow buffer + retry + timeout watchdog
- Fast/slow advertising switching with post-disconnect flap prevention
@@ -442,21 +443,50 @@ Power: `powersaving on/off`
- **Internal**: LittleFS on flash (`/lfs`), 256-byte cache for reduced flash I/O
- **External**: Optional LittleFS on QSPI (`/ext`) with auto-migration
- **BLE bonds**: File-based settings on LittleFS (`/lfs/settings/`) — all platforms (no NVS)
- **BLE bonds**: NVS (`storage_partition`, 0xD0000 on nRF52) via Zephyr settings backend (≥1.16.2)
- **Prefs**: 292-byte binary format, Arduino-compatible, field-by-field I/O (see §13)
- **Contacts**: 152-byte records, stored on external flash if available
- **Channels**: 68-byte records (4 pad + 32 name + 32 secret)
- **Blobs**: Fixed-size records with LRU eviction by timestamp
**First-boot migration (3-way FS self-heal)**
A marker file `/lfs/_zc_init` is written after the first clean ZephCore boot. On every subsequent boot it is present and the logic below is skipped. On first boot (marker absent), `main_companion.cpp` picks one of three paths before `bt_enable()` runs:
1. **No prefs, or Arduino MeshCore prefs** → full LFS + NVS format. Arduino's `new_prefs` omits `node_lat`/`node_lon`, shifting `freq`/`sf`/`bw` by 16 bytes; `prefsLookLikeArduino()` detects this by range-checking those fields. Covers fresh installs and Arduino → ZephCore migrations.
2. **Valid ZephCore prefs + `/lfs/settings` present** → NVS-only erase (`formatNVSOnly()`). ZephCore ≤1.16.1 stored BLE bonds in `/lfs/settings` (file backend); ≤1.16.1 used 0xD0000 as app code, so bytes there may pass NVS sector validation and hang `settings_load()`. Identity/prefs/contacts are preserved; re-pairing is required.
3. **Valid ZephCore prefs + no `/lfs/settings`** → skip format entirely. NVS was already initialised by ZephCore ≥1.16.2; bonds survive the upgrade.
`loadPrefs()` also range-checks `freq`/`sf`/`bw` after deserialisation and reverts to compile-time defaults on out-of-range values, so a misread Arduino prefs file never corrupts the radio config.
### 7.3 GPS (`adapters/gps/`)
- State machine: OFF → ACQUIRING → STANDBY (with warm standby on supported hardware)
- 3 consecutive good fixes (≥4 satellites) required before reporting
- Multi-constellation: GPS+GLONASS+Galileo+BeiDou with fallback
- T1000-E: Complex 6-GPIO power sequencing with VRTC preservation
- Repeater mode: 48-hour wake interval for time sync only
- GPS time blocks phone time sync for 2 hours after last fix
**Duty cycle vs always-on**
`gps_wake_interval_ms` (initialised from `prefs.gps_interval`) controls the mode:
- **Duty cycling** (`gps_wake_interval_ms > 0`): after acquiring 3 good fixes the GPS powers down; the state machine wakes it again after the configured standby interval. The fix callback fires and then the GPS sleeps.
- **Always-on** (`gps_wake_interval_ms == 0`): the GPS never powers down. `consecutive_good_fixes` is reset after each promotion so the 3-fix gate cycles continuously, streaming fresh positions. Flash writes and fix callbacks are rate-limited to once per `gps_acquire_timeout_ms` to avoid hammering storage.
`gps_set_poll_interval_sec(0)` switches to always-on live; persisted via `prefs.gps_interval` (set by `set gps duty 0`).
**Timeout split**
Two separate timeouts apply to acquisition:
- `CONFIG_ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC` (default 300s): the cold-start window used for the very first acquisition after `gps_enable()`. Longer to allow almanac download.
- `CONFIG_ZEPHCORE_GPS_FIX_TIMEOUT_SEC` (default 120s): the normal per-wake timeout for all subsequent acquisitions (warm start).
**Repeater mode**
Repeaters and room servers default to `CONFIG_ZEPHCORE_REPEATER_GPS_INTERVAL_SEC` (48 h) for GPS duty — GPS wakes only for a periodic time-sync fix (5-minute acquire window). The interval is now unified with companion via `prefs.gps_interval` and is configurable at runtime via `set gps duty <sec>`; persists across reboots.
### 7.4 USB (`adapters/usb/`)
- **CompanionUSB**: V3-framed CDC (little-endian 16-bit length prefix + payload)
@@ -569,6 +599,8 @@ prj.conf (base: console; production defaults — LOG=n, ASSERT=n)
| XIAO ESP32-C6 | ESP32-C6 | SX1262 | - | - | - | - | - | 300 |
| LilyGo TLoRa C6 | ESP32-C6 | SX1262 | - | - | - | - | - | 300 |
| Station G2 | ESP32-S3 | SX1262+PA | UART1 | OLED 128x64 | - | 1 button | - | 350 |
| Heltec Wireless Tracker | ESP32-S3 | SX1262 | UC6580 | TFT 160x80 | - | - | - | 350 |
| LilyGo T-Beam v1.2 | ESP32 | SX1262 | gnss-nmea | - | - | - | - | 300 |
| XIAO MG24 | EFR32MG24 | SX1262 | - | - | - | - | - | 350 |
---
@@ -667,7 +699,7 @@ Over USB CDC: V3 framing: `[2B LE length] [1B opcode] [payload...]`
| `/lfs/adv_blobs` or `/ext/adv_blobs` | Advert cache | Fixed-size blob records |
| `/lfs/repeater/acl` | Client ACL | 136B × N records |
| `/lfs/repeater/regions2` | Region map | Header + 164B × N entries |
| `/lfs/settings/` | BLE bonds + Zephyr settings | File-based settings (all platforms) |
| `storage_partition` (NVS, 0xD0000 nRF52) | BLE bonds + Zephyr settings | NVS settings backend (≥1.16.2; old `/lfs/settings` file detected by self-heal) |
### Preferences Binary Layout (292 bytes)
@@ -134,7 +134,7 @@ void ZephyrDataStore::begin()
checkAdvBlobFile();
}
bool ZephyrDataStore::exists(const char *path)
bool ZephyrDataStore::exists(const char *path) const
{
struct fs_dirent ent;
return fs_stat(path, &ent) == 0;
@@ -145,7 +145,7 @@ bool ZephyrDataStore::removeFile(const char *path)
return fs_unlink(path) == 0;
}
bool ZephyrDataStore::openRead(const char *path, uint8_t *buf, size_t buf_sz, size_t &out_len)
bool ZephyrDataStore::openRead(const char *path, uint8_t *buf, size_t buf_sz, size_t &out_len) const
{
struct fs_file_t file;
fs_file_t_init(&file);
@@ -90,9 +90,9 @@ private:
void checkAdvBlobFile();
void migrateToExternalFS();
bool openRead(const char *path, uint8_t *buf, size_t buf_sz, size_t &out_len);
bool openRead(const char *path, uint8_t *buf, size_t buf_sz, size_t &out_len) const;
bool atomicReplaceFile(const char *path, const uint8_t *buf, size_t len);
bool exists(const char *path);
bool exists(const char *path) const;
bool removeFile(const char *path);
bool copyFile(const char *src, const char *dst);
};
+41 -10
View File
@@ -43,6 +43,7 @@ SWD flash: `west flash` (requires J-Link, pyocd, or nrfjprog connected).
| Heltec V4.2 (GC1109 PA) | `west build -b heltec_wifi_lora32_v4/esp32s3/procpu zephcore` | `west flash` |
| Heltec V4.3 (KCT8103L PA) | `west build -b heltec_wifi_lora32_v43/esp32s3/procpu zephcore` | `west flash` |
| Heltec Wireless Tracker | `west build -b heltec_wireless_tracker/esp32s3/procpu zephcore` | `west flash` |
| LilyGo T-Beam v1.2 | `west build -b ttgo_tbeam/esp32/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.
@@ -51,6 +52,13 @@ unclear, check GPIO2's default pull: the V4.2 GC1109 PA has an internal pull-dow
(GPIO2 reads LOW at boot), while the V4.3 KCT8103L PA has an internal pull-up
(GPIO2 reads HIGH). Only difference in firmware: TX control pin GPIO46→GPIO5.
**LilyGo T-Beam v1.2:** Classic ESP32 (PICO-D4) board — several caveats apply:
- Upstream Zephyr DTS models the SX1276 variant; `board.overlay` overrides the radio node to SX1262 on the same SPI3 wiring.
- PICO-D4 rev 1.0 bootloops when the bootloader tries to enable QIO flash mode. `board.conf` forces DIO (`CONFIG_ESPTOOLPY_FLASHMODE_DIO=y`). Any new classic ESP32 board with PICO-D4 needs this.
- Classic ESP32 DRAM is much smaller than ESP32-S3. `board.conf` shrinks contacts/channels/queue (`MAX_CONTACTS=160`, `MAX_CHANNELS=8`, `OFFLINE_QUEUE_SIZE=128`) to fit.
- AXP2101 PMU manages LoRa and GPS power rails via Zephyr regulator + fuel-gauge drivers (auto-enabled from the upstream DTS PMU nodes). Add `CONFIG_FUEL_GAUGE=y` to boards that expose battery state-of-charge over I2C.
- Console/CLI are on `uart0` (onboard USB-UART). No native USB on classic ESP32.
ESP32 flash uses esptool over USB via `west flash`. Hold BOOT button if the device
doesn't enter download mode automatically.
@@ -67,6 +75,26 @@ west build -b <board>/esp32s3/procpu zephcore --pristine --sysbuild -- \
west flash --esp-device COMX
```
### 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:
| Board | Build string | Flash |
|----------------|------------------------------------------------|--------------|
| TTGO LoRa32 | `west build -b ttgo_lora32/esp32/procpu zephcore` | `west flash` |
SX127x boards require these `board.conf` overrides (the `zephcore_common.conf` default enables the native SX126x path):
```kconfig
CONFIG_LORA_MODULE_BACKEND_NATIVE=n
CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE=y
CONFIG_ZEPHCORE_RADIO_SX127X=y
CONFIG_ZEPHCORE_LORA_RX_DUTY_CYCLE=n # lora_recv_duty_cycle not implemented for SX127x
CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM=17 # PA_BOOST max without external PA
```
Classic ESP32 + PICO-D4 also need the DIO flash mode fix (see T-Beam note above).
**One-time setup required (all ESP32 boards):**
```
@@ -248,6 +276,9 @@ should ONLY contain settings that can't be inferred from hardware:
CONFIG_HEAP_MEM_POOL_SIZE Override for large displays (>128x64)
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP Shrink RTT on RAM-tight boards
CONFIG_ESPTOOLPY_FLASHSIZE_16MB ESP32 boards with 16MB flash
CONFIG_ESPTOOLPY_FLASHMODE_DIO=y Classic ESP32 PICO-D4 boards (bootloops otherwise)
CONFIG_ESPTOOLPY_FLASHMODE_QIO=n Companion to the DIO override above
CONFIG_FUEL_GAUGE=y Boards with AXP2101 or other I2C fuel gauge
CONFIG_ZEPHCORE_DEFAULT_TX_POWER_DBM Boards with external PA
CONFIG_ZEPHCORE_MAX_TX_POWER_DBM Boards with external PA
CONFIG_ZEPHCORE_APC Adaptive Power Control — OFF by default.
@@ -331,16 +362,16 @@ Quick Reference: Wio-SX1262 XIAO Pin Mapping
All XIAO boards use the same D-pin assignment for Wio-SX1262:
Signal | XIAO Pin | nRF52840 | nRF54L15 | MG24 | ESP32-C3
-------|----------|-----------|-----------|-----------|--------
DIO1 | D1 | P0.03 | P1.05 | PC01 | GPIO3
RESET | D2 | P0.28 | P1.06 | PC02 | GPIO4
BUSY | D3 | P0.05 | P1.07 | PC03 | GPIO5
NSS | D4 | P0.04 | P1.10 | PC04 | GPIO6
RXEN | D5 | P0.29 | P1.11 | PC05 | GPIO7
SCK | D8 | P1.13 | P2.01 | PA03 | GPIO8
MISO | D9 | P1.14 | P2.04 | PA04 | GPIO9
MOSI | D10 | P1.15 | P2.02 | PA05 | GPIO10
Signal | XIAO Pin | nRF52840 | nRF54L15 | MG24 | ESP32-C3 | ESP32-C6 | ESP32-S3
-------|----------|-----------|-----------|-----------|-----------|-----------|----------
DIO1 | D1 | P0.03 | P1.05 | PC01 | GPIO3 | GPIO1 | GPIO39
RESET | D2 | P0.28 | P1.06 | PC02 | GPIO4 | GPIO2 | GPIO42
BUSY | D3 | P0.05 | P1.07 | PC03 | GPIO5 | GPIO21 | GPIO40
NSS | D4 | P0.04 | P1.10 | PC04 | GPIO6 | GPIO22 | GPIO41
RXEN | D5 | P0.29 | P1.11 | PC05 | GPIO7 | GPIO23 | GPIO38
SCK | D8 | P1.13 | P2.01 | PA03 | GPIO8 | GPIO19 | GPIO7
MISO | D9 | P1.14 | P2.04 | PA04 | GPIO9 | GPIO20 | GPIO8
MOSI | D10 | P1.15 | P2.02 | PA05 | GPIO10 | GPIO18 | GPIO9
Note: nRF52840 D-pin mapping varies by board (XIAO nRF52840 shown).
RAK4631 has SX1262 integrated — different pinout entirely.