posse

arch one firmware, two targets

the same code that ships on the device

posse is a MeshCore application written in C++ against a small hardware abstraction layer: screen, keyboard, gps, compass, radio, board. On hardware those are an ST7789, an STC8H-branded keypad coprocessor, a GNSS receiver, a QMC6309 magnetometer and a Semtech LR1110. In the browser they are eight imported functions.

1 · firmware

C++ UI and mesh logic, drawn into a 240×320 RGB565 canvas with Adafruit GFX.

2 · wasm

Compiled to a freestanding WebAssembly module exporting fw_init, fw_loop, fw_key, fw_radio_rx.

3 · world

TypeScript instantiates one module per device and owns time, position, GPS and the air between them.

Nothing about the UI is reimplemented for the web. When you press right on an emulated device, the same handleKey that runs on the M9 switches the tab, and the pixels you see are the bytes the real display controller would have received.

engine the world

virtual time, and the air

The engine steps a single virtual clock. Every 20 ms each device's fw_loop runs; every second it is handed a GPS fix; whenever a device transmits, the frame is put in flight with an airtime computed from the LoRa symbol formula — the same one the firmware uses to decide when the channel is clear.

When a frame lands, every other device is tested against it. Received power is log-distance path loss with a per-pair shadowing term, compared against a thermal noise floor for the bandwidth plus the demodulation threshold for the spreading factor. Frames overlapping in time collide unless one arrives capture-dB stronger and first. Half-duplex is respected: a device that is transmitting hears nothing.

path loss PL₀ + 10n·log₁₀ d n = 2.0 free space, 2.7 open playa, 3.5+ a dense crowd. Shadowing is a fixed normal draw per pair.
sensitivity −174 + 10log₁₀B + NF + SNRSF Within 2 dB of the threshold a frame is lost probabilistically, which is what the edge of range actually feels like.
speed hundreds of × realtime Fast enough that the emulator runs the world on the main thread and spends its frame budget on drawing.

target the real device

what the emulator cannot tell you

The emulator models the protocol and the link budget, not the physics of a specific afternoon. It does not know about a body standing between two antennas, a metal art car, 40 °C in a pocket, or the way a GNSS fix behaves under a shade structure. It also assumes every device hears the same channel; real radios differ.

What it is good for is everything upstream of that: does the UI make sense at arm's length, how often should a device beacon, how many hops does a crew need, and what happens to a field of twenty radios all beaconing at SF10.

Hardware notes for the ThinkNode M9 — pin map, the USB pad trap that clamps the keyboard bus, the keypad coprocessor's register map and key codes — live with the firmware, assembled from four independent ports because Elecrow publishes no schematic.

try it