Platform Architecture

Heads up. This page describes how the firmware is put together internally — composition root, module layering, runtime task model. It is written for people who want to read or modify the source. If your goal is to use PacketRF rather than to hack on it, you will not need this page; start at PacketRF and the operator-facing chapters from there.

PacketRF is structured as a firmware platform, not as one large board-specific application. That distinction shows up in startup, in how runtime modules are wired together, in how the network stack is shared between local and radio interfaces, and in how the management interface stays decoupled from any specific protocol.

This page is the operator's-eye view of the architecture. It is intentionally short. If you want the deep dive, every box on the diagram below has its own per-module page under Module documentation.

The shape of the firmware

app/ is the composition root: it picks a firmware profile at compile time, wires the runtime services together, and starts the fixed set of FreeRTOS tasks. It is small on purpose. The bulk of the firmware lives in src/, where each subdirectory owns one responsibility — and only that responsibility.

The current src/ tree is, briefly:

  • src/npr — NPR runtime, MAC state machines and slot scheduling, PHY execution and SI4463 driving, frame encode/decode, IPv4 segmentation/reassembly, neighbor cache and diagnostics.
  • src/net — lwIP integration, Ethernet-style netifs (USB, W5500), PPP, DHCP server, the IPv4 pool manager, proxy ARP integration via lwIP route hooks, and the network task orchestration that is reused by future radio modules.
  • src/control — the transport-agnostic management command model. Commands are registered with a single dispatcher; the same dispatcher is exposed over CoAP and over the local USB serial framing.
  • src/coap — a small embedded CoAP server and router. Parses one datagram, picks one endpoint, calls one callback, encodes one response. It is deliberately not RFC-complete.
  • src/cose — the COSE_Sign1 signed-message envelope used for authenticated management traffic, with a deliberately narrow parser profile.
  • src/crypto — Ed25519 device identity, keyring with trust flags, signature/verification API used by cose and by the control layer.
  • src/config — typed persistent configuration, one CBOR file per section, backed by LittleFS on real hardware and by plain files on host tests.
  • src/system — runtime telemetry, watchdog, reboot scheduling, device serial number reporting.
  • src/hal — the RP2350-specific building blocks: GPIO, SPI, USB glue, flash partition placement.

The dependencies between these modules go in one direction. NPR depends on net, net depends on config and on the pool manager, control depends on cose and crypto, and so on. There are no cycles, and there is no module-of-everything that knows about all the others.

Management is one model with two transports

The same inner command model is exposed by two transports:

  • POST /mgmt over CoAP, normally on the UDP port 5683 of any IP interface the device exposes (USB, Ethernet, NPR),
  • the local USB serial framing, intended for first-boot bootstrap and for benchwork without a network in the loop.

The outer transport is different in those two cases, but the inner request — a CBOR map carrying a path and optional args — is the same. So is the response shape, the auth metadata in the COSE protected headers, the error codes, and the schema discovery endpoint at /schema. A handler does not know whether it was reached over UDP or over USB.

A few representative paths from the current command tree:

  • /system/status — overall health, schema id, serial number,
  • /system/mem — allocator-level memory telemetry (FreeRTOS heap, newlib heap, lwIP memory) reported separately, not as one aggregated number,
  • /system/nonce — the rotating freshness token used for signed writes,
  • /interface/list — all live netifs,
  • /interface/np2/status — NPR-side state, neighbors, current slot assignment,
  • /interface/us0/config — read or set the USB service interface configuration,
  • /crypto/device — device public key and key-id,
  • /crypto/bootstrap/install — the special path used exactly once during first boot to install the first admin key.

The full list is not in this document on purpose. The control layer publishes the active schema through /schema, and that is the authoritative answer for the running firmware.

Dual-core, FreeRTOS, lwIP

The RP2350 has two cores. PacketRF currently uses both: timing-critical PHY execution runs on one core, the rest of the runtime — networking, management, NPR MAC state — runs on the other under FreeRTOS. lwIP is the IP stack; it sees np2, us0, et1 and PPP as netifs and routes between them through the standard lwIP routing path with PacketRF's PARP route hook layered on top.

Memory is not abundant. Configuration choices and per-module RAM budgets matter, and the /system/mem endpoint is reporting them separately so a memory issue can be diagnosed instead of guessed.

Where to read next