What happens under RF load?
Aspen kept running through 52.8 minutes of mixed LoRa traffic. Delivery fell during bursts and overload, even though neither radio rebooted or reported a failed transmission. Size a deployment around received messages, not just transmit counters.
The useful distinction: sent versus received
Cedar is the lab companion radio used for these measurements, not a shipped project setup. These results describe the listed boards and workload, not every board port or host implementation.
The run offered 4,933 requests: bot commands and ordinary DMs, channel traffic, room posts, repeater status and RF administration. Of 1,639 calculation requests, the companion received 926 correct replies. A separate room subscriber received 91 of 104 posts.
| Measurement | Aspen node | Cedar companion radio |
|---|---|---|
| Confirmed radio transmissions | 6,091 | 6,357 |
| Rejected / failed / uncertain TX | 0 / 0 / 0 | 0 / 0 / 0 |
| RX errors | 132 | 122 |
| Observed restarts | 0 | 0 |
| Minimum sampled internal free heap | 43.2 KiB | 60.2 KiB |
| Maximum sampled transmit queue | 12 packets | 12 packets |
A confirmed transmission means the radio sent the packet. It does not mean the other radio received it. Bot admission can also decline work before a packet reaches the transmit queue.
Nine channel queries produced a utility permission error after a local relay copy consumed the request. The dispatcher now ignores local channel command copies before admission. A 14.2-minute RF retest with 2,304 requests recorded no such errors: 36 of 107 channel calculations and 173 of 639 DM calculations received correct replies. Neither radio rebooted or reported failed or uncertain TX.
The retest's minimum sampled Aspen heap was 39.3 KiB, lower than the longer run; free heap recovered to 57,816 bytes after temporary test connections closed. This shorter workload spent proportionally more time in burst and overload, so its delivery percentages are not a before/after performance comparison. The graphs below retain the original 52.8-minute run, including its errors.
Bursts cost delivery
At the moderate offered rate, 42 of 47 calculation requests received correct replies. During overload, that fell to 40 of 270. The table uses actual submitted rates: serializing sends, fetching the inbox and running other roles prevented the generator from reaching every planned rate.
| Phase | Bot requests / s | Extra channel packets / s | Correct calculation replies | Room deliveries |
|---|---|---|---|---|
| Low | 0.10 | 0.10 | 13 / 16 | 5 / 6 |
| Moderate | 0.30 | 0.50 | 42 / 47 | 6 / 6 |
| High | 0.79 | 0.97 | 79 / 122 | 5 / 6 |
| Burst | 1.67 | 2.45 | 36 / 172 | 2 / 4 |
| Mixed soak | 0.49 | 0.72 | 303 / 375 | 25 / 30 |
| Overload | 1.75 | 3.38 | 40 / 270 | 5 / 6 |
| Sustained | 0.75 | 0.94 | 361 / 580 | 27 / 30 |
| Recovery | 0.10 | 0 | 25 / 27 | 10 / 10 |
Bot requests include calculations, unknown commands and ACK-only plain DMs. Extra channel packets are non-command traffic; auxiliary room, administration, keepalive and relay work adds further airtime. There were 1,385 receipt-ACK observations for 1,961 requests expecting an ACK, 86 relay status responses for 104 polls, and 81 management RF replies for 105 requests.
Reply timing and correlation
Successful results use exact calculation values or unique request tokens. Channel replies additionally carry the native request digest marker. Channel members share a key, so that marker associates a response with a request; it does not authenticate an individual bot. Each received response is assigned to at most one request.
Internal RAM remained available
Aspen's minimum sampled internal free heap was 44,260 bytes. Minimum DMA-capable free memory was 36,520 bytes, with a minimum sampled largest DMA block of 32,756 bytes. PSRAM does not replace the internal RAM needed by WiFi, TLS, task stacks and radio buffers.
Queue occupancy and administrative sampling
The collector received 914 authenticated diagnostic samples and recorded four diagnostic errors: two HTTP 429 responses, one HTTP 504 response and one timeout. Those misses are retained separately from the radio counters.
Airtime is the shared limit
On this SF7, 250 kHz profile, a 38-byte request takes about 53.38 ms of RF time; a 22-byte reply takes 40.58 ms, and a six-byte ACK takes 30.34 ms. A short direct DM exchange with a request, reply and two ACKs therefore has a packet-only ceiling of 6.47 exchanges per second before channel access, reply delays, collisions, receive time or relay copies.
The configured bot budget was 3,600 ms per minute. That permits at most about 1.48 of those short replies per second on average, before its other transmissions. Larger replies cost more. The shared modem also applies an aggregate 50% airtime budget over a one-hour window; it is not a 50% cap on each two-second sample.
Measured transmission rate and RF time
Packet-size model
Start with a low request rate and watch recipient ACKs, replies and queues on your mesh. Allow room for adverts, forwarding, administration and other users. This run measures one mixed workload; it does not establish an absolute maximum sustained rate for every role.
Heavy work, bad packets and outbound recovery
Follow-up checks on the fixed field build exercised heavier Wasm commands, RF message handlers, replay and malformed packets, and a real interruption of Aspen's MQTT and HTTPS connections. The companion received the command results over LoRa. Radio settings, role identities, original Lua source, empty Wasm selection and grants were restored and read back afterward.
| Check | Received result and recovery |
|---|---|
| Heavy Wasm alongside other roles | 43 of 90 checksum replies in 151.6 seconds: 30/67 by DM and 13/23 on the channel. The routed room subscriber received 5/8 posts; relay status returned 7/8 times. Lua and Wasm controls answered after the workload. |
| Wasm limits and RF handlers | 400-round command checksums and 250-round packet-handler checksums matched. Both DM and channel handlers reported RF reception. Deliberately excessive work returned instruction-budget or wall-deadline errors; a later command worked. |
| Outbound transport outage | A 135.1-second block affected only Aspen's broker and HTTPS traffic. All 16 competing RF calculations arrived. HTTPS returned a transport error over RF, then a successful echo after unblocking; MQTT packet publication resumed. Shared services were not stopped. |
| Invalid and repeated requests | Five cases arrived physically at Aspen: exact replay, native retry flags, bad MAC, unsupported version and invalid packed path. None added an execution to the test counter. Corrected fresh controls executed, and the counter was cleared. |
| Reminders and TRACE | A reminder arrived during competing DM/channel traffic, with 12/12 calculation controls received. Its terminal status was sent; cancellation was also checked. TRACE widths 1, 2, 4 and 8 returned results on both DM and channel. |
Resource use and failure behaviour
Minimum sampled Aspen internal heap was 53,436 bytes during heavy Wasm work and 48,220 bytes during the outbound outage. Maximum sampled queue occupancy was four and two packets respectively; neither radio showed a restart. The MQTT observer became not-ready while its transport was blocked, while RF roles remained available, and recovered afterward.
Message-handler admission is bounded: a busy node does not promise to run a handler for every received packet. The original load reminder was later read back as overdue. A fresh authenticated return PATH allowed the follow-up reminder to arrive under load; the original route-age state was not recorded, so its specific cause remains unknown. Ordinary DMs alone do not refresh that path's age.
Invalid channel TRACE syntax was received and rejected before VM execution. Channel syntax errors do not have an authenticated private sender for an error DM, so this check used physical reception and parser counters rather than expecting a public reply. Valid channel TRACE and calculation controls worked afterward.
Setup and scope
- Cedar companion radioNative companion protocol, DM and #testpark commands
- Local LoRa meshDirect receptions and repeater copies, including Birch
- Aspen standalone nodeBot, repeater, room, Base, management, observer and KISS service
The radios used 912.525 MHz, BW 250 kHz, SF7, CR5, 2 dBm and three-byte paths. Aspen was a XIAO ESP32-S3R8 with SX1262. Native DM and channel responses were received over RF; WiFi was used for private administrative sampling. The source build was development revision b3e752b, corresponding to clean-source revision ef94a47, with MeshCore upstream d92964352441.
These measurements use the revisions listed here. The current GitHub releases record their own source revision. Configure frequency and power for your location.
Earlier negative submissions and remaining scope
The run submitted 24 bounded negative cases: replay, invalid MAC, unsupported version and reserved path width. Capture confirmed 23 arriving at Aspen over RF, excluding local reflections. One burst-phase reserved-width submission was not confirmed. The retest confirmed 14 of 20 submissions; six remain unconfirmed. Reception alone does not establish rejection or replay behavior.
The later execution-counter checks above establish outcomes for their five identifiable cases; they do not turn the earlier reception-only corpus into rejection results. Grant revocation in the original run tested permission changes; the follow-up separately interrupted actual transport.
The external KISS bot endpoint had no separate host-bot workload. These measurements cover standalone Aspen, not an absolute capacity limit for every variant. GPS hardware and newer GPS/region source changes were not flashed for this run.
Use the measurements
- Aggregate results (JSON)Conditions, radio counters, phase outcomes and correlation counts.
- Phase measurements (CSV)Planned and actual offered rates, delivery denominators and reply percentiles.
- PHY airtime model (CSV)Complete wire length, airtime and packet-only rate bounds.
- Fixed-build retest (JSON)14.2-minute run on development revision 7b141fc, clean-source revision 1daed13.
- Fixed-build phases (CSV)Fresh native DM preflight, then control, high, burst, overload, sustained and recovery.
- Follow-up acceptance (JSON)Heavy Wasm, actual outbound outage, rejection/replay counters, reminders, TRACE and retained-state checks.
- Follow-up resources (CSV)Heap, DMA memory, largest DMA block and queue samples from the two bounded phases.
- Monitor your own nodeKISS, dashboard, MQTT observer and Grafana options.