LoRa Lab Simulator

event-driven scripts, sources on air, sinks listening — no hardware

Script

Sources — nodes transmitting on their own schedule

onidSFperiod msfirst TX ms payloadairduty
a source's id arrives in your script as f.anchor

Sinks — radios parked on one SF, listening to everything

SFlatest payload receivedfromat rxlost
a sink hears sources and your node alike

Node

sim time—
VM state—
frames heard—
receiver on—
duty cycle—
collisions—
events dropped—
callbacks run—
last transmitted—

Timeline — scroll to zoom, drag to pan, hover for detail

frame received by the node frame the node missed collision node RX window (colour = SF) node TX timer release callback running

Log


  
Language reference and what the simulator models
def name(a, b) { ... return expr }    # top-level code is setup: it runs once
attach(RX, fn)   attach(TX_DONE, fn)  attach(BUTTON, fn)
t = timer(fn)    timer_start(t, delay, period)   timer_stop(t)   # period 0 = one-shot
radio_rx(sf)     radio_off()   transmit(bytes [, sf])   halt()
frame fields: f.anchor  f.time  f.sf  f.rssi  f.payload
timer fields: t.late  t.missed  t.period
vote(list)  push(list, v)  len(list)  get(list, i)  byte(b, i)  bytes(b0..b7)
now()  rx_ms()  dropped()  log(...)  show(...)     # nothing blocks: no wait, no listen

Setup runs once and must return; after that the VM only runs your callbacks, one at a time, and a callback that never returns is killed by a watchdog. transmit() is asynchronous: it takes the receiver off air until TX_DONE, and then the receiver comes back on by itself. The transmit and receive spreading factors are chosen independently: transmit(payload, sf) sends on sf and leaves the receiver's own spreading factor untouched, while transmit(payload) sends on whichever one the receiver is using.

Air time is real: EU868, BW 125 kHz, CR 4/8, 8-symbol preamble, 9 bytes on air — 54 ms at SF7, 91 at SF8, 181 at SF9, 297 at SF10, 594 at SF11, 1188 at SF12. A frame is only received if the radio is tuned to its SF for the whole transmission, so waking up late loses it, and two transmissions overlapping on one SF destroy each other. The loop period and jitter in the control bar stand in for everything else the board has to do, which is what makes t.late move.