Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ indent_size = unset
# changes, each fenced `// ZEPHCORE:`. Upstream mixes 2-space and tab indentation
# and has trailing whitespace, so none of it is enforced here. Add a file to this
# list when it is converted to upstream shape.
[{zephcore/src/Mesh.cpp,zephcore/src/Dispatcher.cpp,zephcore/src/Packet.cpp,zephcore/src/StaticPoolPacketManager.cpp,zephcore/include/mesh/MeshCore.h,zephcore/include/mesh/Dispatcher.h,zephcore/include/mesh/Mesh.h,zephcore/include/mesh/Packet.h,zephcore/include/mesh/Identity.h,zephcore/include/mesh/Utils.h,zephcore/include/mesh/SimpleMeshTables.h,zephcore/include/mesh/StaticPoolPacketManager.h,zephcore/helpers/BaseChatMesh.cpp,zephcore/helpers/BaseChatMesh.h,zephcore/helpers/ContactInfo.h,zephcore/helpers/ChannelDetails.h,zephcore/helpers/ClientACL.cpp,zephcore/helpers/ClientACL.h,zephcore/helpers/RegionMap.cpp,zephcore/helpers/RegionMap.h,zephcore/helpers/TransportKeyStore.cpp,zephcore/helpers/TransportKeyStore.h,zephcore/helpers/BaseSerialInterface.h,zephcore/helpers/MultiSerialInterface.h,zephcore/helpers/ConfigSerializer.cpp,zephcore/helpers/ConfigSerializer.h,zephcore/helpers/SensorManager.h,zephcore/helpers/sensors/LPPDataHelpers.h}]
[{zephcore/src/Mesh.cpp,zephcore/src/Dispatcher.cpp,zephcore/src/Packet.cpp,zephcore/src/StaticPoolPacketManager.cpp,zephcore/include/mesh/MeshCore.h,zephcore/include/mesh/Dispatcher.h,zephcore/include/mesh/Mesh.h,zephcore/include/mesh/Packet.h,zephcore/include/mesh/Identity.h,zephcore/include/mesh/Utils.h,zephcore/include/mesh/SimpleMeshTables.h,zephcore/include/mesh/StaticPoolPacketManager.h,zephcore/helpers/BaseChatMesh.cpp,zephcore/helpers/BaseChatMesh.h,zephcore/helpers/ContactInfo.h,zephcore/helpers/ChannelDetails.h,zephcore/helpers/ClientACL.cpp,zephcore/helpers/ClientACL.h,zephcore/helpers/RegionMap.cpp,zephcore/helpers/RegionMap.h,zephcore/helpers/TransportKeyStore.cpp,zephcore/helpers/TransportKeyStore.h,zephcore/helpers/BaseSerialInterface.h,zephcore/helpers/MultiSerialInterface.h,zephcore/helpers/ConfigSerializer.cpp,zephcore/helpers/ConfigSerializer.h,zephcore/helpers/SensorManager.h,zephcore/helpers/sensors/LPPDataHelpers.h,zephcore/app/KissModem.cpp,zephcore/app/KissModem.h}]
indent_style = unset
indent_size = unset
tab_width = unset
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ the [supported boards list](docs/supported_boards.md).
| **Repeater** | Relays packets for everyone else | [Text CLI](docs/CLI_commands.md) over USB, or remote admin over the mesh |
| **Room server** | A shared message board that keeps posts for clients who were away | Same CLI as the repeater; users log in from the app |
| **Observer** | Listens only and publishes what it hears to MQTT over WiFi (ESP32) | [Its own small CLI](docs/CLI_commands.md#observer-zephcore-only) over USB |
| **KISS modem** | Raw LoRa packets for a host program (KISS TNC, as upstream's `kiss_modem`) | [KISS protocol](docs/kiss_modem_protocol.md) over USB |

One role per firmware image; you choose it when you flash.

Expand Down Expand Up @@ -140,6 +141,7 @@ Roles and extras are config fragments in `zephcore/boards/common/`, added throug
| `repeater.conf` | Repeater |
| `room_server.conf` | Room server |
| `observer.conf` | Observer (ESP32) |
| `kiss_modem.conf` | KISS modem |
| `debug.conf` | Logging and asserts |
| `packet_logging.conf` | One clean RAW/RX/TX line per packet, nothing else |

Expand Down
35 changes: 28 additions & 7 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@

## 1. Project Overview

ZephCore is a LoRa mesh networking firmware running on Zephyr RTOS. It supports four device roles:
ZephCore is a LoRa mesh networking firmware running on Zephyr RTOS. It supports five device roles:

- **Companion**: BLE-connected device paired with a phone app. Full contact/channel/message management.
- **Repeater**: Autonomous headless relay node. CLI administration via authenticated mesh connections or serial UART.
- **Room Server**: Headless store-and-forward shared message room (BBS). Reuses the repeater's ACL/region/CLI; pushes new posts to logged-in clients (per-client sync cursor + ACK).
- **Observer** (ESP32): Listen-only node that publishes received LoRa packets to MQTT over WiFi.
- **KISS modem**: Raw LoRa packets to and from a host over serial (upstream's `kiss_modem`).

Supported hardware: nRF52840, nRF54L15, nRF54LM20A, ESP32 (classic PICO-D4 and C3/C6/S3), EFR32MG24, and STM32WL (LoRa-E5). Radios: SX126x family (SX1261/62/68, LLCC68, STM32WL sub-GHz), LR1110, SX127x (SX1272/76/78, loramac-node backend), and LR2021 (validated on the MeshTracker X1). A native Linux port runs the full stack on SBCs (Femtofox, Raspberry Pi) via Zephyr `native_sim` — see `LINUX_NATIVE.md`.

Expand Down Expand Up @@ -103,7 +104,8 @@ zephcore/
│ ├── RepeaterDataStore.cpp/h # Repeater-specific persistence paths
│ ├── RoomServerMesh.cpp/h # Store-and-forward room server (BBS)
│ ├── ObserverMesh.cpp/h # Listen-only WiFi+MQTT observer (ESP32)
│ └── main_observer.cpp, observer_creds.cpp/h
│ ├── main_observer.cpp, observer_creds.cpp/h
│ └── KissModem.cpp/h # KISS TNC + SetHardware extensions (upstream verbatim)
│
├── helpers/ # Shared utilities
│ ├── compat/ # Arduino Stream/SHA256/FILESYSTEM over Zephyr, for upstream-verbatim code
Expand Down Expand Up @@ -742,6 +744,24 @@ Headless store-and-forward shared message room (BBS). Clients log in with the ad

Listen-only node (ESP32 only): receives LoRa packets and publishes them to an MQTT broker over WiFi STA (`adapters/mqtt/`, `adapters/wifi/`). Never transmits. Configured at runtime via serial CLI (credentials in `observer_creds.cpp`); build with `boards/common/observer.conf`.

### 6.5.1 KISS modem

Upstream's `examples/kiss_modem` (protocol: [kiss_modem_protocol.md](kiss_modem_protocol.md)). `app/KissModem.cpp/h`
keep upstream's text, with ZephCore changes fenced; `src/main_kiss_modem.cpp` is the composition root. No `Mesh`
or `Dispatcher`: the modem drives `LoRaRadio` directly, and the main loop runs the radio's maintenance (noise floor,
CAD, AGC/drift) as the Dispatcher would. The host serial is the CDC-ACM port, else the console UART, through
`adapters/transport/ZephyrUartStream` (an Arduino `Stream` over interrupt-driven RX/TX rings); the console driver
is off in this role so nothing else writes to the port. Event-driven: serial input, LoRa RX and TX-done, host
output drained, and a one-shot for the sooner of `KissModem::msUntilNextTx()` and the radio's maintenance
deadline. Differences from upstream:
- An LBT refusal from `startSendRaw()` returns to the CSMA slot wait until the wait-clear bound (1.5 × the
airtime of a 255-byte packet from the start of CSMA), then reports TX failed; upstream reports it at once.
- A transmit times out only once the radio has concluded it, as in `Dispatcher::loop()`.
- `SetRadio` outside the `ZC_RADIO_*` ranges (NodePrefs.h) is refused with `HW_ERR_INVALID_PARAM`.
- No periodic AGC reset: the radio's own deaf-aware maintenance replaces it, as in every role.
Radio settings live in RAM only (upstream: the same); the identity is `/lfs/repeater/_main.id`, shared with the
server roles. Build with `boards/common/kiss_modem.conf`.

### 6.6 CommonCLI Commands

System: `ver`, `board`, `reboot`, `start dfu`, `start ota`, `erase`
Expand Down Expand Up @@ -857,7 +877,8 @@ Repeaters and room servers default to `CONFIG_ZEPHCORE_REPEATER_GPS_INTERVAL_SEC
While the bus is suspended (the computer asleep with the port open) the session is kept but is not a connected
client: nothing is queued for it, and it does not hold up the clean-up that runs when the last client leaves. Each
change reaches the main loop as a connect or a disconnect.
- Servers (repeater, room server, observer) use the CDC port as their serial console.
- Servers (repeater, room server, observer) use the CDC port as their serial console; the KISS modem uses it for
its binary stream.

### 7.5 Board (`adapters/board/`)

Expand Down Expand Up @@ -1028,7 +1049,7 @@ prj.conf (base: console; production defaults — LOG=n, ASSERT=n)

### 9.2 Key Kconfig Choices

- **Role**: `ZEPHCORE_ROLE_COMPANION` (default) vs `ZEPHCORE_ROLE_REPEATER` vs `ZEPHCORE_ROLE_ROOM_SERVER` vs `ZEPHCORE_ROLE_OBSERVER` (selected via `repeater.conf` / `room_server.conf` / `observer.conf`)
- **Role**: `ZEPHCORE_ROLE_COMPANION` (default) vs `ZEPHCORE_ROLE_REPEATER` vs `ZEPHCORE_ROLE_ROOM_SERVER` vs `ZEPHCORE_ROLE_OBSERVER` vs `ZEPHCORE_ROLE_KISS_MODEM` (selected via `repeater.conf` / `room_server.conf` / `observer.conf` / `kiss_modem.conf`)
- **Radio**: `ZEPHCORE_RADIO_NATIVE` (SX126x, default) vs `ZEPHCORE_RADIO_LR1110` vs `ZEPHCORE_RADIO_LR2021` vs `ZEPHCORE_RADIO_SX127X`
- **Features**: Display, buzzer, buttons, multi-tap, Doom (auto-enabled from DT); PSRAM auto-enable from DT (`Kconfig.psram`)

Expand Down Expand Up @@ -1226,21 +1247,21 @@ Codes `0x80`–`0x90` (`PUSH_CODE_*` in `app/CompanionMesh.h`). Most used:
| `/lfs/contacts3` or `/ext/contacts3` | Contacts | 152B × N records |
| `/lfs/channels2` or `/ext/channels2` | Channels | 68B × N records |
| `/lfs/adv_blobs` or `/ext/adv_blobs` | Advert cache | Fixed-size blob records |
| `/lfs/repeater/_main.id`, `prefs.json` | Repeater/room-server/observer identity + prefs | `prefs.json` as above; legacy 311B `prefs` kept after migration; atomic-replace writes |
| `/lfs/repeater/_main.id`, `prefs.json` | Repeater/room-server/observer identity + prefs (KISS modem: identity only) | `prefs.json` as above; legacy 311B `prefs` kept after migration; atomic-replace writes |
| `/lfs/repeater/acl` | Client ACL | 136B × N records |
| `/lfs/repeater/regions2` | Region map | Header + 164B × N entries |
| `storage_partition` (NVS, 0xD0000 nRF52) | BLE bonds + Zephyr settings | NVS settings backend (≥1.16.2; old `/lfs/settings` file detected by self-heal) |

> **Roles are not interchangeable.** Each role formats the whole volume on its first boot if the
> volume holds no data for that role: the companion checks `/lfs/prefs.json` and `/lfs/new_prefs`
> (`ZephyrDataStore::hasPrefs()`), the repeater/room-server/observer check `/lfs/repeater/prefs.json`,
> (`ZephyrDataStore::hasPrefs()`), the repeater/room-server/observer/KISS modem check `/lfs/repeater/prefs.json`,
> `/lfs/repeater/prefs` and `/lfs/repeater/_main.id` (`RepeaterDataStore::hasRoleData()`). So flashing a repeater over
> a companion — or the reverse — erases the previous role's identity, prefs and contacts, plus
> `storage_partition` and QSPI. Export your identity before switching roles. The roles' files
> never overlap physically (one LittleFS volume, one allocator); the reason for the wipe is that
> they share 128 KB and the other role's data crowds out writes. Repeater, room server and
> observer share `/lfs/repeater/` and the same prefs layout, so switching among *those three*
> preserves the identity.
> (or to the KISS modem, which keeps only the identity there) preserves the identity.

### Preferences (`prefs.json`)

Expand Down
4 changes: 4 additions & 0 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ west build -b <board> zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/<f
| `repeater.conf` | Repeater | On ESP32-S3/C-series this pulls in `wifi_ota.conf`, so add `--sysbuild` |
| `room_server.conf` | Room server | |
| `observer.conf` | Observer | ESP32 only (WiFi + MQTT) |
| `kiss_modem.conf` | KISS modem | Raw packets over the USB/console serial port; the console is off |
| `debug.conf` | Logging and asserts | Combine with any role |
| `packet_logging.conf` | One RAW/RX/TX line per packet | Logging subsystem stays off, so the console carries only these lines and the CLI |
| `repeater_uplink.conf` | Repeater WiFi + MQTT uplink | ESP32, with `repeater.conf` |
Expand Down Expand Up @@ -72,6 +73,9 @@ west build -b rak4631 zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/ro
# Observer
west build -b xiao_esp32c3 zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/observer.conf"

# KISS modem
west build -b xiao_esp32c3 zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/kiss_modem.conf"

# BLE adapter at debug level (debug.conf turns logging on, the flag raises BLE to DBG)
west build -b rak4631 zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/debug.conf" -DCONFIG_ZEPHCORE_BLE_LOG_LEVEL_DBG=y

Expand Down
10 changes: 6 additions & 4 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ A **role** is a firmware personality chosen at build time. Exactly one role per
| Repeater | Autonomous relay: ACL, regions, neighbours, admin CLI | Operator | `examples/simple_repeater` | `/lfs/repeater/` |
| Room server | Store-and-forward board, per-client sync cursor | Operator + logged-in clients | `examples/simple_room_server` | `/lfs/repeater/` |
| Observer | Listen-only; publishes packets to MQTT (ESP32) | Broker | none (ZephCore-only) | `/lfs/repeater/` |
| KISS modem | Raw packets, radio and identity services for a host | Host program (KISS) | `examples/kiss_modem` | `/lfs/repeater/` (identity) |

Roles share the mesh core; they differ in policy. **Switching between the companion and a server role formats the
volume** (the identity is lost unless exported first); repeater, room server and observer share one layout.
volume** (the identity is lost unless exported first); repeater, room server, observer and KISS modem share one
layout.

---

Expand All @@ -106,7 +108,7 @@ flowchart LR
|---|---|---|
| Platform | nRF52840, nRF54L15, nRF54LM20A, ESP32 classic, ESP32-S3/C3/C6, EFR32MG24, STM32WL, native Linux | board directory |
| Radio | SX126x family (default), LR1110, LR2021, SX127x | devicetree / board conf |
| Role | companion (default), repeater, room server, observer | user `EXTRA_CONF_FILE` |
| Role | companion (default), repeater, room server, observer, KISS modem | user `EXTRA_CONF_FILE` |
| UI | none, button UI, joystick/keypad UI (companion) | board Kconfig select |
| Features | debug, WiFi companion, WiFi OTA, uplink, packet logging, PM | user confs; some auto-added |

Expand All @@ -129,11 +131,11 @@ The board list is one manifest per board (`zephcore.yml`), read by `build.sh`, t

```mermaid
flowchart TB
subgraph L5["L5 Composition roots — src/main_companion.cpp, src/server_main_common.cpp, app/main_observer.cpp"]
subgraph L5["L5 Composition roots — src/main_companion.cpp, src/server_main_common.cpp, app/main_observer.cpp, src/main_kiss_modem.cpp"]
MAIN["construct objects, wire callbacks, run the event loop"]
end
subgraph L4["L4 Roles and app policy — app/"]
ROLES["CompanionMesh · RepeaterMesh · RoomServerMesh · ObserverMesh · PowerPolicy"]
ROLES["CompanionMesh · RepeaterMesh · RoomServerMesh · ObserverMesh · KissModem · PowerPolicy"]
end
subgraph UI["Presentation — helpers/ui*"]
UIX["display, button UI, joystick UI, buzzer, LEDs"]
Expand Down
Loading
Loading