The Propagation Monitor I wrote about last time has been sitting on my desk running in daily use ever since, and it’s held up well enough that I went looking for the next question worth answering the same way. PropMon tells me whether I can make the contact. What it doesn’t tell me is what’s actually happening in my local operating area right now — who’s mobile nearby, what the weather’s doing a few miles out, whether anything needs my attention. That’s a different question, and it turned out to need a genuinely different instrument.

So this is APRS Monitor (APRSMon), the second instrument in what I’m now calling the N4MI Desktop Instrument Series — same LilyGO T-Encoder Pro hardware, same round display and knob, same one-glance philosophy. Enough of a pattern now that I’ve settled on a naming convention for the whole series: n4mi-[instrument]-monitor. This one lives at n4mi-aprs-monitor, and unlike PropMon — where the firmware and its backend service live in separate repos — this one keeps firmware and backend together in one place. Small structural decision, but worth mentioning if you go looking for the code.
A wrong assumption, caught early
My original plan was straightforward: reuse PropMon’s whole approach and pull local APRS data from aprs.fi’s API, the same way PropMon pulls solar data from HamQSL. Query a station, get JSON back, done.
That assumption turned out to be wrong in an important way. aprs.fi’s API is explicitly built for querying specific, known stations — it intentionally doesn’t support “show me everything within X miles.” Which is fine for a screen like Weather, where I already know which two stations I care about. It’s not fine for a screen that’s supposed to answer “what’s moving nearby,” where the whole point is that I don’t know in advance who that’s going to be.
The actual answer was APRS-IS itself — the raw network the amateur radio community runs this whole system on top of. It supports real radius filtering, but it’s a persistent TCP connection streaming raw packets, not a REST API. That’s not something to run directly on the ESP32, so Mobile Activity ended up needing its own small always-on backend service, structurally identical to PropMon: a persistent connection, a radius filter, an in-memory summary, a flat JSON endpoint the firmware polls. Same pattern, applied a second time, once I understood what it actually needed to do.
Four screens, two backends
Weather queries two known local stations directly through aprs.fi’s API — a quick, simple periodic poll, no different in shape from PropMon’s own HamQSL fetch. Mobile Activity is the one running against the direct APRS-IS connection, filtered to a 20-mile radius around home, showing how many stations have been active in the last hour and the most recently heard one. Overview is the front door, a condensed glance at both. And Alerts pulls it together — currently watching wind gust and rain-rate thresholds on both weather stations, plus whether my own home weather station has gone quiet for longer than it should.



Rotating the knob cycles through all four; a long press pulls up a Config screen showing Wi-Fi status and whether each backend is actually alive, the same “is my stuff working” screen PropMon has. Short press does something different depending on which screen you’re on — on Weather it swaps which of the two stations is shown large, and on Mobile Activity it switches to a Recent Stations list, the last three heard instead of just the latest one. PropMon’s short press has one global meaning everywhere; here it didn’t map as cleanly, so each screen got whatever made sense for it instead.
I also just finished an ambient version of the Alerts screen — a small badge and a brief on-screen banner that show up on whatever screen you’re already looking at when something changes, so you don’t have to be on the Alerts screen specifically to notice. It’s built and deployed, but as of this writing I haven’t actually seen it fire against a real condition yet — that’s still on the list to confirm properly rather than just assume.
A real bug worth telling
My own home weather station showing up as a source of local APRS activity was a nice surprise I hadn’t planned for. A separate project on my NAS relays it to APRS-IS, and once I noticed, adding a liveness check to Config took almost no work — that same station’s packets were already flowing through Mobile Activity’s existing connection, just quietly ignored because a stationary station never registers as moving.
Except once, briefly, it did. My own home station showed up in the mobile count as “active,” reporting itself half a mile away from its own fixed location. It never happened again, and I couldn’t fully pin down why — most likely a moment of GPS jitter in the relay, or a stray value in one packet.
The interesting part wasn’t chasing down the exact cause. It’s that I didn’t need to. I already know this specific station doesn’t move, by definition — that’s the entire reason it’s useful as a liveness check in the first place. So instead of trying to fully diagnose one weird packet, I just excluded that callsign from movement detection entirely, permanently, regardless of what any future packet ever claims. Sometimes the right fix isn’t explaining the anomaly, it’s recognizing you already know something the code didn’t.

Wi-Fi setup, the easy way this time
PropMon’s Wi-Fi setup — the phone-based captive portal, join a temporary network, pick from a scanned list, type a password — took real work to get right the first time, including two genuine bugs I only found by deliberately trying to break it. This time I built the same thing for APRSMon by porting that proven code directly rather than starting over, bugs and all the lessons from finding them included from day one.
It worked correctly the first time I flashed it. Every case I’d had to hunt down by hand on PropMon — a wrong password getting rejected, the scan and the hotspot not fighting each other — just worked. That’s the whole value of building a series instead of one-off projects: the second instrument gets to start from what the first one already learned the hard way.
Where it stands
APRSMon is at v1.0 now — every screen built and running on real hardware, the same real Wi-Fi setup flow as PropMon, and the ambient alert system described above. I’m treating that ambient piece as built-but-not-yet-proven until I actually see it trigger against a real condition, which is next on my list — probably by deliberately taking my own home station offline long enough to force its own silence alert.
My two weather stations both happen to sit roughly east of home, which got me thinking about the gaps. There’s a station further out to the west I’m planning to add as an early-warning sentinel, since weather here typically approaches from that direction. North and south are open questions for later — no candidate stations picked yet, just an idea worth not losing.

Three of these on the desk now. Whatever the next one ends up answering, I’ll write about it here.
If you want to build this one yourself, the code is public: N4MI APRS Monitor. The README covers the build, both backend services, and the Wi-Fi setup flow. Same caveat as last time — you’ll want to already be comfortable running a Docker/Portainer stack before you dive in.

