loomwave://mesh — status: weaving_ v0.x · hidden-node LoRa mesh · open firmware
◇ Mesh network · woven from scratch

A network you
weave by hand.

A from-scratch LoRa mesh platform built around the hidden-node regime — direct messages and group channels that reach the whole mesh, with graceful degradation from 5 to 500 nodes, no carriers and no required infrastructure. You flash the firmware. The network grows itself.

Flash a device → Read the docs flasher.loomwave.org
// the premise

The loom and the wave.

the loom — handcraft

From scratch. Tagged. Validated.

No protocol decision rests on intuition. Every claim cites a tagged simulation result, a named hardware run, or a captured real-world trace — the project's quantitative-discipline rule. Seven client platforms shipping on one mesh stack; built by one engineer with AI-accelerated R&D in the loop.

the wave — technical

Radio packets. Scheduled access where the hub can see everyone; epoch-hash contention where it can't.

Engineered from first principles around the hidden-node regime that breaks contention MACs — turn the topology liability (one elevated node hears many mutually-hidden terminals) into the asset that makes a collision-free schedule possible.

01

No infrastructure required

No carriers, no SIM. Where an elevated node exists it becomes the time, slot, and modulation authority for a collision-free schedule. Where it doesn't, the network drops to epoch-hash contention with no carrier sense — a floor that never performs worse than the alternatives.

02

Coordinator-liveness failover

Clients detect a dead coordinator by beacon silence and either re-home onto an adjacent cell or drop into the coordinator-free fallback. Authority concentration buys schedule feasibility without becoming a single point of failure.

03

Designed to scale

From-scratch protocol, not bolted onto a legacy stack. Validated three ways — calibrated simulation, hardware bring-up across seven client platforms, and a captured real-world hidden-node hub. Target: graceful operation from 5 to 500 nodes.

// the why

Why another mesh?

Flood-based meshes are simple and need no coordination, but channel load grows as N²·ToA/T — on default long-range LoRa settings the channel saturates at roughly 5–11 nodes at one message per node per minute.

Placing a relay on a hilltop makes things worse: it gathers more mutually-hidden transmitters into one collision domain, and carrier-sensing can't help because a node can't sense a signal it can't hear. These are the failure modes Loomwave is built around.

// the stack

Three layers, chosen against an airtime budget.

MAC

Hybrid CSMA / TDMA

Rate-capped CSMA discovery plus a TDMA data plane. TDMA solves the hidden-node problem by construction — no GPS required. Degrades gracefully to flood behavior without a coordinator.

Routing

Infrastructure-aggregated distance-vector

Flooded control is O(N²) at scale. Aggregating through infrastructure nodes makes it O(N): scheduled unicast registration with link-state only among infrastructure nodes.

Security

Per-packet AEAD

Authenticated encryption on every packet with a derived nonce and truncated tag (~16 bytes overhead). Replay protection with no synchronized clocks required.

// the hardware

Node roles.

Separate optimized firmware per role — not one binary.

RoleHardwareFunction
CLIENT ESP32 / nRF52
Heltec, T-Beam, RAK4631…
User endpoint via BLE/USB. Sleeps aggressively. Relays only if configured.
REPEATER Same hardware class Always-on relay with neighbor table and routing. Optimized for solar/battery field deployment.
INFRA Linux SBC Full routing with link-state. Time and slot authority. Persistent store-and-forward. Optional MQTT/IP bridge.
// firmware

The current build.

2026-08-28 (main @ gfd89004) — 911.5 MHz, SF9, BW125. Builds are labelled by date and commit rather than by a release name; the installer always carries the current one. This is a full refresh of every board image, and it carries two reasons to reflash: Tier 0 Direct, which makes messaging between nodes with no coordinator in range a real mode rather than a survival mode, and a transmit-power fix for the Heltec V4 and the Seeed T1000-E, whose images were built far below full power — both described in the news below. Also in this build: messages that stop making progress now report an honest reason instead of a generic failure, and cross-cell forwarding holds a deeper queue than the single slot it used to, while counting anything it still has to drop. All of it on top of group channels, the coordinator-hosted web chat page (join a WiFi coordinator's network, open /chat in any browser, and you're messaging the mesh with nothing to install), the shared node map, cell-wide position relay, and the offline inbox. All seven platforms: Heltec T114, XiaoGator, RAK WisBlock RAK4631, Seeed T1000-E, Heltec V3, Heltec V4, and Station G2 (client + backbone relay). Flash directly from a Chrome or Edge browser at flasher.loomwave.org — no tools or drivers required.

Not in this build: emergency SOS. The SOS feature described in the news below is merged into the firmware source, but no installable image carries it yet — this build predates it. A node flashed today has no SOS, and the five-tap gesture on it does nothing. It ships once the full alert path is validated on the air, and this section will name the build that carries it.

In this build, on one board: the band survey. The Station G2 image flashed as a coordinator with WiFi carries an on-demand survey of the radio band, reachable from the node's own web page — described in the news below. It is the only image that has it; the other builds have no such page, and the nRF52 boards have no WiFi at all. One caveat if your Station G2 is masted and relaying under a tower: a sweep in this build can cost the node that tower link until the sweep ends. The fix is merged but is not in the image the installer serves today.

Not in this build either: the pairing bug fixed on 2026-09-06. A defect that could leave a never-claimed ESP32 node permanently refusing to pair — described in the news below — was introduced on main after this build was cut, and fixed there before any release carried it. This build predates it and is unaffected: a board flashed from the installer claims normally.

Two known limits of Tier 0 Direct in this build. Coordinator-free messaging works, and both limits appear only in one situation: a node talking directly while a working cell is within earshot on the same channel. First, a node in direct mode transmits on its own schedule without listening first, so it can begin about a second before that cell's beacon and destroy both transmissions. Second, a node gives up on its coordinator after a fixed twelve seconds of beacon silence — which on a slower cell is less than two beacons apart, so a single beacon lost to that collision is enough to send a healthy node back to scanning. Both fixes are merged and described in the news below; neither is in the image the installer serves today. If all your nodes are in one cell, or your direct-mode nodes are out of earshot of a cell, neither applies to you.

BoardRadio / MCUFlash method
Heltec T114 nRF52840 · SX1262 · L76K GPS UF2 drag-and-drop
XiaoGator v0.9 nRF52840 · E22-900M30S (1 W) · L76K GPS UF2 drag-and-drop
RAK WisBlock RAK4631 nRF52840 · SX1262 UF2 drag-and-drop
Seeed SenseCAP T1000-E nRF52840 · LR1110 · AG3335 GPS UF2 drag-and-drop
Heltec WiFi LoRa 32 V3 ESP32-S3 · SX1262 Web installer — one click
Heltec WiFi LoRa 32 V4 ESP32-S3 · SX1262 + FEM Web installer — one click
Station G2 (client) ESP32-S3 · SX1262 Web installer — hold BOOT first
Station G2 (coordinator) ESP32-S3 · SX1262 Web installer — hold BOOT first
Browser-based web installer

Flash Heltec V3, Heltec V4, or Station G2 directly from Chrome or Edge — connect via USB, click Flash, done. For nRF52840 boards (T114, XiaoGator, RAK4631, T1000-E), double-tap reset to enter bootloader, then drag the .uf2 file to the drive that appears. Reinstalling is safe to repeat: from the 2026-08-18 build onward, an install leaves a node's identity, WiFi setup, channel subscriptions and owner binding in place — only an explicit factory reset clears them.

Open flasher →
// join the weave

Bring your own node.

Grab a supported radio board, open the flasher in your browser, and you're a thread in the cloth. No accounts, no gatekeepers.

Open the flasher →
01
Get a supported radio board
ESP32 + LoRa class hardware. Cheap, off-the-shelf.
02
Open the web flasher
Plug in over USB. Firmware writes in seconds.
03
Join the weave
Power on — your node finds the mesh and relays.
// the progress

Roadmap.

// news & updates

A record of milestones.

DateMilestone
2026-09-10 Two ways a healthy node could walk away from a working tower — both fixed on main, neither in a build you can flash yet — who this affects, first: it takes a node running in coordinator-free direct mode and a working cell on the same channel within radio earshot of it. If every node you own is joined to one cell, or your direct-mode nodes are off on their own, nothing here reaches you, and the image the installer serves is unchanged either way. What we found, chasing a cell that kept losing beacons on the bench. A node talking directly has no coordinator to take its turns from, so it transmits on a schedule of its own and does not listen before it does — and in every one of the seven collisions we measured, it was the direct-mode node that started first — around half a second to a bit over a second before the beacon — wrecking both transmissions. Six of nine beacons went missing in thirty-three minutes that way. Behind it sat a second fault that turned each lost beacon into something worse: a node abandoned its coordinator after a fixed twelve seconds without hearing one, a threshold set when cells ran fast frames. On a slower cell — the kind a tower-synchronised network uses — twelve seconds is not even two beacons apart, so one collision was enough to make a node at point-blank range give up, rescan and re-register. Four such abandons in fourteen minutes, each of them exactly twelve seconds after the last beacon it heard. Both fixes are merged. The first has the direct-mode node learn the rhythm of the beacons it can hear, predict when the next one is due, and keep quiet across that window and the busy stretch after it — with a floor that guarantees a fifth of every cycle stays open, so nothing it wanted to send can be starved out, and with emergency alerts deliberately exempt: an SOS never yields to a beacon. The second reads the cell's own pace out of the beacon and waits proportionally — at least twelve seconds, and at least two and a half beacons — and re-arms the receiver first, something only one of the seven boards used to do. In a thirty-minute acceptance run the fixed build rode out silences of 13.8 and 15.8 seconds and stayed with its coordinator five separate times; the old build could not have occupied any of those states. Status, plainly: the beacon-timing fix has passed that run, the collision fix is merged and live on a single bench node but not yet proven at scale, and neither is in the image at flasher.loomwave.org. They ride out with the next build, and this page will say when that is.
2026-09-10 A message with no tower behind it can now travel further than one radio hop — when there is no coordinator in range, Loomwave nodes talk to each other directly. Until now that was strictly what you could hear: if a node was not in earshot of the sender, the message did not reach it, and the phrase we used on this page for coordinator-free mode was "no tower to relay". That changes. Nodes now pass a coordinator-free channel message along for each other — each node repeating what it heard, unchanged, exactly once, with a hop budget that runs out so nothing can circle forever, and only after checking the sender's signature, so a node never repeats something it cannot verify. To keep a crowd from all shouting the same thing, a node that hears two of its neighbours already repeat a message stays quiet — with one deliberate exception that turned out to matter: a node that heard the message only faintly repeats it regardless. That node is usually the one at the edge, the only link to whoever is further out, and staying quiet is exactly the wrong instinct for it. In the network model we test these designs against, that single exception is the difference between half the nodes receiving a message and all of them, and it costs nothing extra among nodes that could already hear each other. Two honest limits. This is direct messages' quieter sibling — it carries channel and broadcast traffic, not one-to-one messages, which still travel only as far as the sender's own radio. And it is merged source with simulation and test-bench results behind it, not a shipped feature: it has not been proven on the air, no installable image carries it, and the nodes you can flash today still relay nothing when they are on their own. It goes out with the same build as the fixes above.
2026-09-06 A fresh ESP32 node could refuse to be paired, forever — fixed on main, and never in a build you can flash — the reassuring half first, because it decides whether this is your problem: the image the web installer serves is the 2026-08-28 build, it predates this bug entirely, and it is unaffected. If you flash from flasher.loomwave.org, you were never exposed and there is nothing to do. What broke, and for whom: a node keeps a small record of who owns it, written the first time an owner claims it. On the three ESP32 boards — Heltec V3, Heltec V4, Station G2 — the firmware opened that record read-only before it existed, and the chip's answer for "no such record yet" came back indistinguishable from "the record is damaged". The node then did the careful thing for the wrong reason: it declared its owner store unreadable and refused every pairing claim rather than risk overwriting a real owner. Nothing was actually wrong with the node, and no amount of retrying, rebooting or resetting helped — a board that had never been claimed could never be claimed. The fix probes for the record before reading it, so absent now means new and claimable, while a genuinely faulty store still fails closed and refuses the claim. Verified on hardware both directions: a fresh board becomes claimable, an already-paired board stays paired. The exposure window is narrow and it is developers-only: every build from main between 2026-08-28 and the fix carried it, and no released image ever did. The four nRF52840 boards — T114, XiaoGator, RAK4631, T1000-E — were never affected; they store that record as a file, and a missing file already read as new. When the next installer build ships, it will carry the fix from the start.
2026-09-04 The band survey is already on a build you can flash — on exactly one board, and not on a masted relay yet — this one has been shipping quietly since the 2026-08-28 image, and we had it listed as still-in-development. Correcting that, with the limits attached. A Station G2 flashed as a coordinator with WiFi can survey the radio band it lives in: open the node's own web page and it steps across fourteen channels spanning 902–928 MHz, sitting about three seconds on each — half a second measuring the noise floor, then a listening pass — and reports what it found per channel: the floor, the loudest thing it saw, and what fraction of the time that channel was busy. A couple of minutes end to end. It measures energy, not traffic: it can tell you a channel is loud, never who is talking on it. It is a diagnostic and it came out of one — we built it while chasing a real reception problem, because guessing at what the air looked like was not working. Two limits are worth more than the feature. First, one target only: the Station G2 coordinator-with-WiFi image. The other installer builds do not carry the page, and the UF2 boards — T114, XiaoGator, RAK4631, T1000-E — have no WiFi to serve it from. Second, and this is the one to read twice: on a node relaying under a tower, a sweep in the currently published build can knock it off that tower. Going off-channel to listen collides with the tower's beacon, and partway through the sweep the node gives up on the tower and falls back to standing on its own. The repair — the survey now waits for the tower's beacon to pass before it listens, and refuses outright rather than return a survey it had to cut short — is merged, but it is not in the image the installer serves, and it has not yet been confirmed on the air. So on a masted relay, treat a sweep as something that will interrupt that node's mesh duty; run it on a bench node, or on one you can afford to have step out for a couple of minutes. On a node already claimed by an owner, starting a sweep takes that node's OTA password. One sweep per five minutes.
2026-08-31 A drive test caught the cross-cell delivery bug red-handed — and this one is still open — messages sent between two mesh cells were received and decrypted at the backbone relay, and then went nowhere: seven forwards in, zero out, and no error recorded anywhere. We now have the mechanism rather than the symptom. The relay held no current route to the destination cell — that cell's coordinator had stopped announcing its roster after a restart — and a message with no route simply vanishes instead of reporting a failure. Behind it sits a second finding that explains the first: a coordinator can boot into a broken state at random. The same board, power-cycled twice fifteen minutes apart, once came up announcing its cell normally and once came up silent — never announcing, sealing frames the backbone could not read. That boot-time coin flip is the precondition for the silent drop, and it reproduces on the bench with nothing but a power cycle. Neither is fixed yet, and we would rather say so plainly: until the fixes land, a message crossing between cells can fail without telling you. The hop between cells is where it is lost. The first thing to land is the instrument — a stay-at-home cross-cell smoke test (#330) that puts this entire scenario on the bench, no truck required.
2026-08-30 Emergency SOS has landed in the firmware — and it is not on a build you can flash yet — read the second half of that first, because it is the half that matters today: this is merged source code, not a release. Nodes in the field do not have SOS, and five taps on one of them do nothing. The installer still serves the 2026-08-28 build, which predates the feature by hours. What is written, reviewed and merged: five deliberate taps on the node's own button — or the app, or the serial console — raise an alert that rides airtime the node already owns, so it outranks that node's own chat traffic without taking anything extra from the channel. It carries the sender's position, and it repeats until it is stood down, or until it lapses on its own after thirty minutes. Nodes that hear an alert re-broadcast it under a strict rate bound, so a stranger's distress call gets relayed while nobody can turn the mesh into their personal transmitter. One deliberate exception is worth knowing before you would ever need it: raising an SOS sends your position even if your standing “share my location” setting is off — an alert without a position is close to useless, so triggering one is an explicit decision to be found, and the app says exactly that on the screen where you trigger it. The raise-and-cancel gesture was verified on real hardware; an adversarial review of the result then found three ways an armed alert could fail silently, and all three fixes are merged (#314, #316, #317), alongside a reworked on-device screen that makes a node claim only what is true at each stage. A fourth came later and from a different direction — bench testing found it and the source confirmed it: on a node that already had an owner, an SOS raised over Bluetooth by a phone that was not that owner never reached the alert code at all; a list of what an unclaimed connection is allowed to ask for had simply never had the SOS request added to it. That silence was the opposite of the intent. A stranger being able to raise an alert through a node they do not own — rate-limited, and with none of the other privileges the owner has — is deliberate, and it is much of what makes an SOS worth carrying in the first place. Fixed (#396), with that list now pinned by tests, so it can neither quietly narrow again nor quietly widen. Next is validating the whole alert path on the air, and then a flasher build that actually puts SOS on nodes. This page will say so when that build exists.
2026-08-28 If you run a Heltec V4 or a Seeed T1000-E, reflash it — those two images were built far below full transmit power — a single build setting fixes a node's transmit power at compile time, and every board image sets it explicitly. Two did not: the Heltec V4 and the Seeed T1000-E fell through to a bench default of −9 dBm instead of the 22 dBm the rest of the fleet uses. Nothing about it was visible from the outside — no warning, no log line, no setting you could check on the node — which is exactly why it survived. That 31 dB gap is measured at the radio chip, not at the antenna: the V4 has an amplifier in front of its antenna that absorbs some of the shortfall, and we have not yet measured how much, so we are not going to attach a range figure to it. What is certain is that a V4 or a T1000-E flashed from the installer before 2026-08-28 has been transmitting far more quietly than it was meant to, and that the 2026-08-28 build fixes it. Reflash from flasher.loomwave.org — it keeps the node's identity, channels and owner binding. The other five platforms were always correct and need nothing. The setting can no longer go missing: a check now fails the build if any board image leaves it unset, and it runs on the path that actually publishes the installer, not only on the one that doesn't.
2026-08-28 Messaging with no tower at all — coordinator-free operation is now a first-class tier — Loomwave has always said it needs no infrastructure, and the coordinator-free mode behind that promise has always existed. It was a survival mode, though, and it behaved like one: a direct message from a peer nearby could vanish into a 45-second gap while the node was still working out that its coordinator had gone; channel traffic heard before joining a cell was dropped outright; retries kept the slow pace built for a schedule that was no longer running; and a stalled message blamed a network registration that, with no coordinator anywhere, could not have been the problem. All four are fixed in this build. Nodes out of reach of any coordinator now carry direct messages and channel traffic between themselves, advertise their names to each other so peers show up as names rather than addresses, and retry at a pace suited to talking directly — about 9 seconds rather than 45. The node-side behaviour is in the firmware you can flash today; the matching app changes — a Direct status with a live count of the neighbours in earshot, and a stalled message that says "no peers nearby" instead of blaming an uplink that isn't there — are merged and ride out with the next app build. It is a smaller network while you are in it: whoever is in radio range, no tower to relay, no delivery across cells. But it is a working one, and it now tells you the truth about which one you are on.
2026-08-28 The next-generation radio left the bench — and the first field result is mostly a measurement of where we put the receiver (#170) — the LR2021 evaluation hardware got its first real-world link test: one board carried on foot along a walked route, the other left stationary as the receiver, running 2.4 GHz LoRa at 2.450 GHz, SF9, BW 500 kHz and +22 dBm with the stock whip antennas on both ends. From fixed stops, every frame arrived out to 218 m — 50 of 50 at each stop but one. Walking, the link was clean to about 150 m, had lost half its frames by 270 m and was effectively dead past 300 m, though a handful still made it in from 436 m. The number that matters most, though, is one that has nothing to do with distance: at the very first stop, 7 m apart, the link was already spending about 26 dB more than free space alone can explain. The receiver was indoors, unelevated, in a recessed spot with no gain antenna, and that placement dominates every row of the results — it is several times larger than the roughly 8.7 dB the 2.4 GHz band itself costs against 900 MHz. So the honest conclusion is that this run measured a poor receive location, and the band's actual reach is still unmeasured; it is not a fair comparison against our 900 MHz drive test, which had a mast and an external antenna behind it. One genuine radio result did come out of it: frames decoded at −15 dB signal-to-noise, roughly 3 dB better than the nominal figure for this spreading factor. This is one run, one route and one receiver placement, with approximate distances — a data point, not a coverage curve. The rerun that answers the real question puts the receiver outdoors and up high. Full write-up, plot and data: loomwave.org/rfc/24ghz-per-test-1-result.html. This remains evaluation, not a shipping feature.
2026-08-18 Reinstalling from the web installer no longer wipes your node (#216) — every install served from the browser flasher used to erase the patch of flash where an ESP32 node keeps its settings, even though the installer declared it would leave that area alone. A node came back from a routine reinstall with a brand-new address that every peer had to learn all over again, and with its WiFi setup, saved position, channel subscriptions and owner binding gone. The installer now writes the firmware as two pieces that step around that area, so reflashing to pick up a fix keeps a node's identity, its channels and its owner. This covers the browser-flashed boards — Heltec V3, Heltec V4, and Station G2 as client or coordinator; the nRF52840 boards you flash by drag-and-drop were never affected. Wiping a node on purpose is still there as an explicit factory reset. Any installer build dated 2026-08-18 or later has the fix — the date is shown next to each board at flasher.loomwave.org.
2026-08-05 The compact tower antenna, simulated whole: two targets clear, one the model cannot resolve — earlier simulations described a board outline the design had since outgrown, so the whole antenna — radiator, printed balun, matching section, ground pour and reflector — was re-simulated as one structure at the as-cut 104 × 200 mm outline, driven at the real connector. Return loss comes out at −11.6 dB or better across the whole 902–928 MHz band and realized gain at 6.9 dBi — both comfortably inside target, and both still inside it when the simulation's own settings are varied. Front-to-back is the one that will not settle: moving the simulated boundary further from the antenna, with the geometry untouched, shifts that number by more than a decibel, and moving it further still shifts it back the other way. There is no setting the result converges on, so the model cannot resolve front-to-back against its target — a statement about the simulation, not a verdict on the antenna. Establishing that cost a run 6.5× the size of the original, and it is why nothing has been sent for fabrication: a real front-to-back figure for this design needs a measurement the simulation cannot stand in for. This remains exploratory design work.
2026-08-02 The published specification now matches what the fleet actually runs — the packet-format reference was brought up to the shipped wire version, including the membership-scaling beacon that lets a single coordinator serve a dense hub and its downlink-pending signaling. A new reliable-messaging chapter documents confirmed delivery end to end: the sealed message envelope the infrastructure can't open, the automatic retransmission that survives a dropped packet, and the short receive-window timing rule that makes downlinks land. The whole specification is self-contained and browsable at loomwave.org/rfc/.
2026-07-31 Next-generation radio evaluation begins — LR2021 on the bench — Semtech's new dual-band transceiver came up on the bench and linked to itself cleanly: 25 of 25 packets at 915 MHz, then a first 2.4 GHz LoRa link (8 of 8 at SF9/BW500) and a 2.6 Mb/s FLRC link at 2.45 GHz. This is evaluation, not a shipping feature — but if it delivers, future towers could carry backbone traffic on a separate band, freeing up airtime where everyday messages live.
2026-07-30 A compact 4-channel tower antenna, designed before anything was fabricated — a printed-circuit radiator with its impedance-matching network etched onto the board, designed end to end in open-source electromagnetic simulation (openEMS) and taken to a fabrication package. The automated fab gate passed that package; a human review then caught fabrication-killing defects the gate had missed — copper printed outside the board outline — which is what drove the new outline and hole-overlap checks the gate now runs. The full-structure simulation of that board followed on 2026-08-05 — see above.
2026-07-25 Firmware release client-fw-2026.07.25 — message the mesh from any web browser (#153) — coordinators with WiFi now host a chat page: join the coordinator's network, open /chat, and you're talking on the mesh with no app to install (iPhones included). Validated over real radio in both directions on the bench. The web page talks on the built-in #Public channel, which is open by design — see below. Also in this build: a fix for nodes that hadn't finished joining a cell silently ignoring channel traffic, battery voltage reported on the air, and a lower-idle-power mode for WiFi coordinators. Flash at flasher.loomwave.org.
2026-07-25 Battery telemetry and early warning — the first piece of fleet energy monitoring (#152) — nodes now report battery voltage as part of their normal telemetry, and infrastructure keeps a solar-aware trend fit over it, so a node heading for a flat battery can be flagged days out instead of simply going quiet. Alongside it, a staged-rollout system for firmware updates landed in dry-run form — canary nodes, automated health checks against live telemetry, and halt-or-rollback rules — groundwork for safe fleet-wide updates.
2026-07-24 Smarter cross-cell routing, live on the production tower (#145) — cross-cell delivery now prefers the cell that actually owns the recipient and forgets stale routes learned from elsewhere, fixing two delivery bugs reported from the field. The routing decision was pulled out into a pure, unit-tested function first, so the behaviour that fixed them is now pinned by tests rather than by hope.
2026-07-24 Infrastructure relays now sip power (#146) — the WiFi-connected coordinator build idles about 30% lower — 0.17 A down to 0.12 A at 5 V, measured at the bench meter on a freshly booted node. It is on by default in the shipped WiFi coordinator firmware. That figure is the combined effect of three settings measured together, not one: a slower CPU clock, deeper WiFi sleep between beacons, and trimmed WiFi transmit power. The deeper sleep is the one that trades against how quickly the node answers an incoming connection — a balance we are still tuning.
2026-07-13 One-click WiFi setup for a masted coordinator (#136, #137) — the phone now hands a coordinator its WiFi credentials in a single tap using Wi-Fi Easy Connect, instead of typing a password into a tiny screen or baking credentials into a build. It is now the default WiFi coordinator firmware for the Station G2. One caveat if you plan to update that node over the air later: a coordinator set up this way generates its own update password, and that password never reaches the phone — so a node provisioned entirely by one tap will refuse over-the-air updates. Until the app can retrieve it for you, set that node up from its built-in setup page instead of using the one tap, where you choose that password yourself. Do it in that order: a node that has already been set up by one tap can't have its update password changed from the setup page afterwards — it has to be factory-reset from the app first, which also gives it a new node address and unpairs it from its owner. Nothing else about the one-tap path changes.
2026-07-12 Your node answers to you — owner binding, read receipts, and a new home screen (#130, #133) — a node can now be claimed by its owner through a challenge-response, with display boards showing a passcode on their own screen, so a stranger in range can't quietly pair with your node; an unauthenticated connection times out rather than squatting the slot, and a node can be unpaired again on the owner's say-so. Messages gained a third receipt state — sent, delivered, and read — and the app moved to a three-tab shell: Chats, Channels, Node.
2026-07-11 Channels in the app — join, create, and catch up (#132) — the phone app can join or create a channel, generate a strong random channel key in one tap, share an invite by QR code or link, and read the channel thread alongside direct messages. History missed while your phone was away syncs when you reconnect, and it survives leaving and re-entering a channel.
2026-07-11 Group channels are live on the mesh — send once, reach everyone (#132) — send to a named channel and every subscriber's node receives it, across cells. A channel message crosses a whole cell in two radio transmissions rather than having every node repeat it, and it travels only toward cells that actually have a subscriber, so a channel costs airtime where it is wanted and nothing where it isn't. Coordinators keep a per-channel history that survives reboots and updates, so a node that was powered off catches up on exactly the messages it missed when it returns — proven by powering a node down, sending messages, and watching them replay on its return. Validated over real RF between two towers. Every channel message carries its sender's signature, so you can tell who really sent it.
2026-07-11 What a channel key does and doesn't hide — worth being precise about, since we ship both. A channel you create carries a key you chose and shared yourself; the infrastructure holds no channel key material at all, so coordinators forward those messages by channel identifier alone and cannot read the contents. The built-in #Public channel is the deliberate exception: its key is compiled into every node's firmware, so every node — and every coordinator — derives the same one. Anything on #Public is readable by anyone in radio range, joined or not. That is not a gap, it's the point: it's what lets a coordinator serve the browser chat page above with no app, no account, and no join. Use a channel of your own for anything you wouldn't say out loud.
2026-07-09 Android Auto, field-validated — the full hands-free loop in a real truck — on an actual head unit, the in-car screen showed the live mesh map, and a message crossed the network to the truck, was read aloud, and the spoken reply traveled back over the mesh to the sender. The complete incoming + reply loop, validated in the field with the Google Play build — no developer mode, no cables.
2026-07-09 Firmware release client-fw-2026.07.09 — road-test bug fixed the same night (#131) — the truck test flushed out a real reliability bug: on a busy cell, a node's peer-key table could fill up and silently block delivery confirmations — messages arrived fine, but senders burned their retries and saw "failed." Root-caused, fixed (larger table, least-recently-used eviction that never evicts active peers, and receipts that wait for keys instead of dropping), bench-validated end-to-end, and shipped to every board that night. Flash at flasher.loomwave.org.
2026-07-09 The app is on Google Play (internal testing) — with an automated release pipeline — build, sign, upload, and release notes in one command via the Play Developer API. The Play build is what unlocks the complete Android Auto experience on real head units, and internal testers now get updates through the store instead of sideloading. Closed testing is next.
2026-07-08 The mesh map is live — every opted-in node on a real map, from any node (#114) — nodes that turn on "share my location" now appear as pins on a street-map view in the app and on the Android Auto car screen. Positions relay cell-wide, so the map is complete no matter which node your phone connects through — including a headless Pi tower. Works offline too: with no internet the map stays pannable and pins render on a plain backdrop. Confirmed on the production tower and in the field.
2026-07-07 Any two nodes can now message each other reliably (#120) — messaging between two client devices could silently fail in one direction — permanently — with a "no key" error, while the reverse direction worked fine. Two separate causes: a power-napping device only listened during part of each cycle and kept missing the moment the coordinator shared a particular peer's identity, and a long-settled device's cached identity quietly expired at the coordinator with nothing to refresh it. Both are fixed — devices now re-share their identity at a low background rate, and the coordinator varies the order it relays them — so every device learns every other device's key regardless of timing. Bench-proven on the pairs that used to fail.
2026-07-07 Seeed T1000-E reception fixed — from ~⅓ of beacons missed to none — the LR1110-radio tracker was dropping 25–37% of the tower's beacons even at close range and bouncing between coordinators. The culprit was its own radio-stall recovery: it wasn't restoring the radio's high-sensitivity power mode after firing, and it was re-initializing the radio every healthy frame instead of only after a genuine miss. With both fixed, a bench T1000-E went from 5-of-8 beacons to 18-of-18 — zero misses. (This also retired the long-standing theory that power-napping caused the drops; the misses happened with napping fully off.)
2026-07-06 On-node inbox — messages wait for your phone instead of getting lost (#127) — a message that arrived while your phone app was disconnected used to vanish: the node handed it to a Bluetooth link nobody was listening on. Now the node keeps a small on-device inbox of undelivered messages and replays them when the phone reconnects — and it survives a node reboot, since the buffer is persisted to the node's flash. The queue is bounded (newest messages win) and replayed gently so a backlog never stalls the radio schedule. A follow-up fix ensured the on-device drive-log can never crowd the inbox out of flash — verified end to end: messages injected, node hard-reset, both replayed on boot.
2026-07-06 Companion app reliability pass — several quality fixes landed together: message threads are now scoped to the node you're connected through (and you can't message yourself); a delivered message (✓✓) can no longer flicker back to a single check when a late radio retransmit arrives; each node can be individually forgotten and display names are sanitized; and a small build-version footer makes it easy for testers to report exactly which build they're on.
2026-07-04 In-car node map and mesh status — Android Auto screens verified; Play Store next (#78) — the companion app's in-car surface grew beyond hands-free messaging: a node map and a live mesh-status screen over Android Auto, validated on Google's head-unit emulator (one crash traced to a missing map-template permission and fixed). With a published privacy policy and upload signing in place, the app is headed to Play Store closed testing — so testers get the in-car screens without developer mode. Also new: a proper Loomwave launcher icon and a clear-node-list control.
2026-07-03 Node positions, phase 1 — "you are here" with privacy by default (#114) — the phone now receives its own node's position over the wire: a compact GPS block (live fix, surveyed location, or none) shown as a "you are here" line in the app. Sharing location with the rest of the mesh is a separate, opt-in switch — default off, persisted on the node itself — decided as the privacy model before any position ever leaves the device.
2026-07-03 Heltec V4 GPS — first confirmed fix on hardware — the original bring-up was driving the GPS module's reset line as "enable," holding it in reset forever, and listening on the wrong UART pin. A one-boot diagnostic pin sweep (kept in the firmware for future boards) measured the truth: enable, reset, and UART orientation all corrected, and the V4 went from GPS-dead to a live fix in under a minute.
2026-07-03 Field usability: power-off, button wake, chirp de-spam, dead-battery guard — hold the button ~4 s for a power-off with an on-screen countdown (release to cancel); button presses now register instantly via interrupt instead of a 1 Hz poll that caught ~1 in 5 taps; join chirps now sound only on the first join after boot or on recovery from a real outage, instead of beeping on every rejoin; and a V3/V4 with a dead battery now sleeps and waits for charge instead of brownout boot-looping under radio-init load — a real field failure.
2026-07-03 Roaming loyalty and eviction patience (#59, #112, #116) — a client between two comparable coordinators no longer ping-pongs: the incumbent gets a 6 dB hysteresis edge, and a transient beacon loss no longer blacklists it — a 10-second re-acquire grace stops the client from greedily grabbing whichever coordinator speaks first, so the incumbent's next beacon wins. This churn turned out to be the real source of tracker battery drain; bench-validated at zero coordinator changes in 150 s where before it flipped roughly every 25 s. On the coordinator side, small cells now require both missed roster turns and a real silence window before evicting, so a collision burst can't knock out a live member.
2026-07-03 "Cell full" is gone — PACT-lite membership on the ESP32 coordinator and tower (#109) — membership is now a table row (up to 32 members) with a rotating per-frame telemetry roster, replacing the three standing slots that had a fleet of live clients playing musical chairs between coordinators all night. Ported byte-identical from the Rust coordinator to the ESP32 and hardware-validated with the live fleet: everyone seated, roster rotating, rows held. Beacon timing now uses the measured time-on-air instead of a stale constant (up to ~370 ms of error at worst case), and formerly silent receive drops now log with names.
2026-07-03 Mesh-clock hardening — the schedule epoch always advances (#110) — on-air debugging traced a serving-cell collapse to a frozen schedule epoch: clients hash their contention-window picks from it, so a frozen value locked them into permanent collision patterns and the member table drained to zero. The epoch now always moves forward, converges to a pure UTC-derived value in steady state on time-anchored coordinators, and caps how long any single value can be re-served (encryption nonce safety). Clients also mix a per-beacon counter into their picks so a held epoch can never freeze them again.
2026-07-03 Node identity from persisted hardware randomness (#94) — node addresses now derive from a persisted hardware TRNG seed instead of the radio's broadcast MAC, so identity survives reflashes and reboots and is no longer tied to a hardware identifier the radio announces to the world. The whole device fleet was rebuilt and published; a node updating from an older build gets a new address once, on first boot, by design.
2026-07-03 Identity pins survive eviction (#112) — a member evicted for radio silence used to lose its pinned public key, so its next join stalled awaiting a fresh identity advert and, worse, its address was briefly open to being claimed under a different key — defeating first-writer-wins exactly when the rightful owner was knocked out. Trust-on-first-use pins, session keys, and replay history now persist through eviction, and the pin table is hard-capped so an attacker can never flood out an existing pin.
2026-07-02 Live network telemetry over MQTT (#89) — infrastructure coordinators can now stream liveness, periodic metrics, and topology events (joins, evictions, backbone adjacency, routing forwards) to an MQTT broker — opt-in via a single environment variable, fully off when unset. The radio frame loop is never blocked: publishing rides a bounded queue in a dedicated thread, and the connection self-heals with backoff when the broker is down or slow. Groundwork for centralized live mesh mapping.
2026-07-02 Coordinator hardening — nonce-reuse fix and hardware watchdog (#95, #104) — coordinators now persist an encryption-epoch high-water mark across reboots, closing a window where a rebooted coordinator re-used (epoch, sequence) pairs — two packets sealed under the same keystream is a serious AEAD break. Separately, a hardware watchdog self-resets a wedged coordinator, and radio-init failure now retries then hard-resets instead of hanging until a manual power cycle. Scoped to the ESP32 coordinator; the nRF52 watchdog stays off deliberately to protect the headless-reflash DFU window.
2026-07-02 Android app — reliability and persistence (#99, #100) — message history, the learned peer directory, and the chosen connection now survive app restarts. Bluetooth auto-reconnects with exponential backoff after an unexpected drop (walking out of range, node reboot) instead of staying down until a manual tap. And a listener leak that made each reconnect show incoming messages an extra time is fixed, with incoming messages now deduplicated by ID.
2026-07-02 Wired INFRA-to-INFRA backbone — hardware-validated over a real LAN (#64/#72) — the coordinator-to-coordinator backbone is now transport-agnostic: two distinct coordinators formed a signed-identity adjacency and exchanged bidirectional AEAD-sealed cross-cell frames over TCP, with none of the RF forwarding cap. A wired trunk can also be pinned to its designated partner tower (a peer-authorization ACL), so an open listener no longer trusts any node with a valid identity.
2026-06-29 Firmware release client-fw-2026.06.29 — the reliable-delivery + automatic-retransmission build is now the published fleet release, and the platform roster grows to seven with the Seeed SenseCAP T1000-E (first LR11x0-radio client, with live GPS) and the Heltec WiFi LoRa 32 V4. Flash at flasher.loomwave.org.
2026-06-29 Frame-integrity CRC16 — ghost-message bug fixed (#83) — first field test from a real masted site surfaced a confusing bug where garbled 'ghost' messages from phantom contacts appeared on the phone. Root cause: a dropped BLE notification desynced the phone↔node frame parser, which locked onto a stray byte and rendered a frame header as a message source. A trailing CRC16-CCITT on every BLE frame now lets both ends detect a false lock and resync cleanly. The whole device fleet plus the Android app were rebuilt to matched builds and published.
2026-06-28 Hands-free in-car messaging — validated in an actual vehicle (Android Auto) — a message sent across the LoRa mesh was read aloud by Android Auto on the receiving phone, and a spoken voice-reply traveled back over the mesh to the sender. The full incoming + reply loop, no hands.
2026-06-28 Opportunistic GPS time-sync — first coordinator adoption (#79) — foundation for network-wide time sync where any node with a GPS fix (client or coordinator) can anchor the whole mesh's clock. Bench-validated: a free-running coordinator adopted a Heltec T114 client's GPS-derived UTC over the air and re-derived its epoch around the better source. The first step toward seamless cross-cell message relay.
2026-06-28 In-cell delivery reliability improvements — hardware-verified — two complementary fixes: the coordinator now TXes pending downlinks ahead of housekeeping traffic at each frame boundary (in-cell delivery jumped from 73% → 90%), and the client adds app-layer ARQ that resends an un-acked message every 45 s (up to four retries) so messages survive transient RF loss. Bench-validated end-to-end: a receiver reset mid-send still received the message at +64 s after rejoin.
2026-06-28 LR1121 receive sensitivity recovered ~38 dB — the Nebra Duo's E80-900M2213S has its RF-switch DIOs wired opposite the LR1121 eval-board convention the driver assumed. Fixing the mapping (rx=DIO6) brought the mid-tower from −89 dBm to −51 dBm on the bench; distant nodes that were buried in the noise floor came back. TX was unaffected (both DIOs are HIGH on transmit, swap-immune), which is why the issue stayed hidden until receiving a distant signal.
2026-06-24 RFC specification paper published — the full Loomwave design document is now online at loomwave.org/rfc/. Covers the complete stack: motivation, protocol, simulation results, and all hardware-verified results through Stage 6.
2026-06-24 Boundary make-before-break handoff (#59) merged to main — when a peer coordinator's LSA confirms a client has roamed to it, the previous coordinator releases the slot immediately instead of waiting the ~50 s silence timeout. Eliminates wasted slots and stale LSAs at cell boundaries. Baked into the current field-test firmware; field verification planned June 27–28.
2026-06-23 Dual-radio hub — two full cells on one node, hardware-verified — a single coordinator (Pi + Duo HAT) ran two independent cells simultaneously: SX1262 on channel A (915 MHz) and LR1121 on channel B (916 MHz). Heltec V3 joined cell A and V4 joined cell B, both streaming telemetry concurrently for 70+ frames, zero errors. Opens the capacity-layer path to N>1 cells per infrastructure node.
2026-06-23 Per-link bandwidth — hardware-verified — each roster member now runs at its own (SF, BW) pair within the same frame. Bench-verified: SF7/BW500 for a strong member alongside SF9/BW125 for a marginal one in a single transmission, 13+18 positions, zero overruns.
2026-06-23 Automatic (SF, BW) modulation selection — hardware-verified — the coordinator auto-selects the fastest bandwidth the cell can support, stepping up in hysteresis-respecting stages: SF9/BW125 → SF9/BW500 → SF7/BW500. Hardware-verified on the bench: member followed all steps (66 positions). SF7/BW500 delivers 4× the capacity of the SF7/BW125 floor that spreading-factor selection alone cannot reach.

Older milestones — the full build record from the start →

Figures on this page were last checked against source at 689c575 (2026-08-06).

// case studies

Production data, real networks.

Captures from real, healthy LoRa mesh networks — analyzed to understand what current protocols do well, where they hit structural limits, and what that means for the Loomwave design.

Meshtastic

North Georgia metro, June 2026

19,049 packets · 77.8 h continuous · 312 nodes · 37 MQTT gateways. What a well-run Meshtastic network looks like on the air — and what no amount of tuning can fix without a protocol change.

Download PDF
MeshCore

Atlanta repeater network, June 2026

19,697 packets · up to 79.8 h · three PYMC repeater vantages. Source-routed unicast and LBT fix several Meshtastic failure modes — and reveal where the next structural wall stands.

Download PDF
// node operators wanted

Meshtastic & MeshCore operators — share what your node hears.

The Stage 1 analysis is grounded in data from a real ~842-node network in North Georgia, pulled from the Meshview and Malla public APIs. Those sources give breadth. A direct connection to a live node gives depth — and some things that public aggregators structurally cannot provide:

Packet loss

Measured, not modeled

Public loggers only record packets that decoded and uplinked — collisions, CRC errors, and dropped packets are invisible (survivorship bias). A node's own counters (num_packets_rx_bad, num_rx_dupe, channel_utilization) are the only source of real loss data.

Propagation

Clean SNR/RSSI samples

Position, antenna, role, and firmware are all known exactly, so every reception is an unambiguous propagation sample with no guesswork about the receiver's location or config — unlike a position-join from a third-party log.

Protocol behavior

Why, not just what

The node exposes the decisions it made: route type, duplicate suppression, drop reasons, per-packet airtime. Aggregated logs show what moved. The node shows why it forwarded, dropped, or deferred — which validates the protocol design directly.

Connecting to a Meshtastic node and a MeshCore node in the same area produces a controlled head-to-head comparison in one RF environment — impossible from separate public tools alone.

How to share — lowest-trust options first

We only ever subscribe to heard-packet metadata and telemetry counters. We never send messages, change configuration, or record message content — only payload byte-length and packet type. Access is read-only, on your terms, revocable anytime.

operator_capture.py

Self-contained Python script. Runs on the Pi hosting meshtasticd. It only ever makes one outbound connection and never listens for one — by default to 127.0.0.1, the Pi's own node, so nothing leaves the machine unless you point it elsewhere with --host. Produces a single .zip archive to share. One short file you can read before running anything.

Run it with python3 operator_capture.py --hours 24 --anonymize. It prints a progress line every 15 minutes, tells you if the node goes quiet, and records how much of the window it actually covered — so you never spend a day on a capture that stopped in hour one.

Download script

To get involved or ask questions: github.com/Loomwave