Making · 2026 — ongoing

A LoRa mesh across Phoenix

Deploying MeshCore nodes through the West Valley. Traced a board that received fine but never transmitted down to an antenna-switch bring-up bug in the I/O expander — fixed upstream in a community fork.

LoRa · MeshCore · Firmware · RF

The idea

LoRa gives you kilometres of range at a few hundred bits per second, on unlicensed spectrum, from hardware that costs less than dinner. A mesh of those radios is a text-messaging network that works with no carrier, no internet, and no infrastructure anyone owns. That’s a genuinely interesting property, and the only way to find out whether it holds up is to put nodes on the map and see who you can reach.

I’m building out MeshCore coverage across the Phoenix West Valley, with an eye toward a budget repeater at elevation — the single highest-leverage node in any RF network is the one with the best horizon.

The bug that taught me the most

A Heltec WiFi LoRa 32 V4 that received perfectly and transmitted to nobody.

That’s a maddening failure mode, because everything reports success. The firmware boots, the radio initializes, packets come in, transmit calls return without error. From the software’s point of view the node is healthy. It just isn’t there.

The path chain on that board runs through a PI4IOE5V6408 I/O expander, and the antenna switch — the thing that physically routes the RF path between receive and transmit — hangs off it. If that expander isn’t brought up correctly at init, the switch never moves. Receive works because the default state happens to favour it. Transmit goes into a terminated path and radiates nothing.

Nothing in the software layer can see this. The SPI transaction to the radio succeeds. The radio dutifully generates the signal. It just has nowhere to go.

The fix existed in a community fork — the sosprz branch had the expander bring-up handled correctly. Flashing it turned a dead node into a working one.

What it changed about how I debug

I’d internalized “the error will show up in the logs” as basically axiomatic, and this is the cleanest counterexample I’ve hit. Every layer I could instrument was reporting success, because every layer I could instrument was succeeding. The failure was one layer below the lowest thing that could talk back.

The generalization I took away: when every observable says the system is fine and the system is obviously not fine, the fault is in something that has no way to report. Which means the next move isn’t more logging — it’s finding a different observation channel entirely. In this case, another node listening.

Currently

Working on link asymmetry — pairs where A hears B clearly and B doesn’t hear A at all, which usually comes down to antenna, elevation, or noise floor rather than anything in software. Also poking at the existing Phoenix community network to understand real-world coverage before committing to repeater placement.

← All making

Contact

Let's build something.

I'm looking for data science, machine learning and AI engineering roles in Phoenix or remote — and I'm always happy to talk about models, retrieval, or why yours is overfitting.