VE6SLP / Projects

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.

52.8-minute run: radio counters and two-second resource samples
MeasurementAspen nodeCedar companion radio
Confirmed radio transmissions6,0916,357
Rejected / failed / uncertain TX0 / 0 / 00 / 0 / 0
RX errors132122
Observed restarts00
Minimum sampled internal free heap43.2 KiB60.2 KiB
Maximum sampled transmit queue12 packets12 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.

Correct calculation replies and room deliveries as percentages of offered requests across the ten load phases.
Each point summarizes one phase; connecting lines show the phase sequence, not per-second delivery. Missing replies remain missing, rather than being assigned an unrelated later response.
Selected phases; the complete table is in the downloads
PhaseBot requests / sExtra channel packets / sCorrect calculation repliesRoom deliveries
Low0.100.1013 / 165 / 6
Moderate0.300.5042 / 476 / 6
High0.790.9779 / 1225 / 6
Burst1.672.4536 / 1722 / 4
Mixed soak0.490.72303 / 37525 / 30
Overload1.753.3840 / 2705 / 6
Sustained0.750.94361 / 58027 / 30
Recovery0.10025 / 2710 / 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.

Median and 95th percentile correlated bot reply latency by phase.
Latency includes the companion's inbox retrieval and correlated error replies. Requests with no reply are absent from the latency percentiles, but remain in delivery denominators. A low latency among received replies does not imply low loss.

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.

Internal free heap sampled through the 52.8-minute run on Aspen and the companion radio.
The collector took 1,582 Aspen samples and 1,577 companion samples. Sampling can miss a brief allocation trough; these are observed minima rather than guaranteed memory reserves.
Queue occupancy and administrative sampling
Sampled transmit queue occupancy on Aspen and the companion, reaching 12 packets during load.
Both radios reached a sampled queue occupancy of 12. All baseline-ready Aspen roles were ready at final readback; identities, source hashes, grants and radio settings were unchanged.

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
Confirmed radio transmissions per second, averaged over each telemetry interval.
Peak sampled rates were 9.32 transmissions/s on Aspen and 11.58 on the companion. These include short ACKs and other roles' packets, not that many completed bot conversations.
Radio transmit airtime as a percentage of each telemetry interval.
Aspen transmitted for 361.87 seconds across the run; the companion for 362.13 seconds. Peak sampled interval airtime was 57.70% and 76.31%, respectively. Counters account completed transmissions within sampling intervals.
Packet-size model
Packet-only frames per second versus complete over-air packet size at 100% and 50% transmit airtime.
The model uses a 32-symbol preamble, explicit header and 16-bit CRC, matching this SX1262 build. These curves are airtime arithmetic, not achieved application throughput. The nominal coded bit rate is 10,937.5 bit/s.

Download the packet-size and airtime table.

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.

Bounded follow-ups on development revision 7b141fc
CheckReceived result and recovery
Heavy Wasm alongside other roles43 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 handlers400-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 outageA 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 requestsFive 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 TRACEA 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.
Received heavy Wasm, room and relay results, compared with RF calculations during an isolated outbound outage.
These are separate workloads, not a performance comparison. Heavy replies include the request token and computed checksum; missing results remain losses.
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.

Internal free heap on Aspen and the companion in separate heavy Wasm and outbound-outage sampling windows.
Two-second samples cannot capture every allocation trough. Wasm's shared 1 MiB PSRAM pool remains allocated after removing a module; it is separate from internal WiFi and radio memory.

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

  1. Cedar companion radioNative companion protocol, DM and #testpark commands
  2. Local LoRa meshDirect receptions and repeater copies, including Birch
  3. Aspen standalone nodeBot, repeater, room, Base, management, observer and KISS service
A second room subscriber used a routed connection through Birch. Birch also provided an independent packet capture. This was an operating mesh, not an isolated two-radio link.

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