In August we handed an AI a firmware plan that a different AI had written and asked it to find everything wrong with it. Not summarize it, not polish it: go through it claim by claim against the actual driver source and the actual chip datasheet, and report what didn’t hold up.

It found real problems, and each one was an evening at the bench we didn’t have to spend: a configuration flag set in the wrong direction, a block of code that could never run, a status value labeled as one thing where the hardware meant another.

Then we ran the corrected plan on real hardware, and two of the corrections turned out to be wrong. The frequency offset had its sign backwards. A live measurement said so, and flipping it turned the worst of our three test points into the first clean decode this project had ever produced. The second was funnier: the carefully corrected data rate was further off than the original number nobody had touched.

So the reviewer was right about the plan and wrong about its own fixes, and what settled the disagreement was a radio, a real meter, and somebody willing to be corrected by a measurement. That’s the end of the story. Getting there took about seven years.

The Meter in the Yard

There’s a small radio inside a lot of American water meters. Ours is a Neptune R900, and every so often it broadcasts a short packet with the current reading, so a utility truck can drive down the street and collect a whole block without anyone lifting a lid. It has been out there announcing our water usage to the neighborhood for longer than we’ve been paying attention to it.

The reading is ours. The signal is in the air over our own house. We wanted the number on a dashboard instead of on a quarterly bill.

In 2019 we did the sensible thing: a Raspberry Pi Zero, a cheap USB TV tuner repurposed as a radio receiver, an off-the-shelf decoder that already spoke the protocol, a small Go daemon shoveling readings into a time-series database, and Grafana on top. It worked. Then it sat. That repository holds a handful of commits spread across four years, most of the middle of it a silence three years long, until somebody opened it one afternoon in 2023, changed a few things, and closed it for good.

The Part We Made Harder on Purpose

The dongle setup works, but it’s a whole Linux box, a USB radio, and a pile of software just to read one slow number. We wanted something smaller: a single board that plugs into an outlet, sits there listening, and streams what it hears straight up to Grafana, or Home Assistant, or whatever else in the house wants to know how much water we’re using. That means a dedicated radio chip instead of a general-purpose receiver, and writing our own firmware to drive it, which is a different problem entirely.

The chip we picked, a CC1101, is cheap and thoroughly documented. The first PCB layouts for a receiver board are dated spring 2025. We also bought an evaluation kit and a debugger, planning to bring the chip up in the manufacturer’s own tools before touching our own code, because that’s the responsible order to do things in. Then we found out the evaluation board doesn’t natively support the chip we’d bought it to evaluate.

The Attempt That Didn’t Work

Spring 2025. The first commit in the scratch repository is titled “Claude version”: a complete, structured firmware file, written in one pass, that didn’t work.

What followed was weeks of going downhill in slow motion. We dropped from a convenient high-level SPI interface down to poking registers directly. One commit message from that stretch reads, in full, “Almost working SPI loop thing.” The file grew several competing register tables with overlapping names. A fake payload appeared where real received data was supposed to be. One branch pivoted midway to a third-party driver library and was left in a state that wouldn’t even build, its build file still pointing at a source file somebody had deleted.

Nothing in that tree ever says “abandoned,” and no commit announces a decision. There’s a last one, and then there is simply nothing.

The one thing in the whole mess that unambiguously worked was the cheap dongle and the off-the-shelf decoder, reading the real meter the entire time. The problem was entirely ours: the signal existed, it was decodable, and someone else’s software could already do it.

That wasn’t the last word from 2025. A second effort that summer came at the meter from the other direction, using software-defined-radio tooling instead of a microcontroller to work out the protocol from first principles and build a decoder against it, checked the whole way against that same reference receiver. That one made real progress. It held together for a couple of months before going quiet too, with a few last informal recordings trailing off in September.

Starting Over

About six months later somebody opened a fresh planning document and started again from an empty directory. The work that followed ran from spring into late summer with long gaps in it, and it was slower on purpose. We tried three receive architectures in sequence and measured them against each other before keeping one. The winner was the least clever option: stop trying to catch individual bits in software, let the radio’s own hardware assemble whole packets, then read them out.

Two side branches on a smaller, cheaper chip went nowhere. One got far enough to decode a recorded file and no further. We left its last commit message where it was: “step 5 live RX remains open.”

Teaching a HackRF to Lie

A Neptune meter transmits on its own schedule, which has nothing to do with when you want to test a receiver. Waiting for the real thing wasn’t a viable way to work, so we built a fake one: record one genuine transmission, then play it back on a HackRF, on command, as many times as we needed.

That mostly worked, except for one thing. HackRF loops a recording by reading it in a circle, and every time it wraps around there’s a gap of roughly 30 milliseconds of silence. Our own receiver logic was watching for silence as the sign that one packet had ended and treated that stitch as a real gap every time, resetting itself mid-decode. We spent longer than we’d like to admit chasing what looked like a flaky radio before realizing the flakiness was coming from our own test rig, not the air. Widening one timeout fixed it outright, and it’s the kind of bug that stays invisible until you go looking at what your tools are doing to the signal, not just at what the meter is doing.

Once that was sorted, the setup earned its keep. We could run the same known-good capture dozens of times in an hour, watch the chip’s own log stream over a serial cable as it tried each one, and check the result against what an independent reference receiver said the same signal should say. That’s also how we learned the meter’s hop cycle isn’t jittery at all: an unattended overnight run held the same interval, to a fraction of a second, well over fifty times in a row, which is what eventually made predicting the next hop possible instead of just chasing it.

The Free Integrity Check

The bench story from the top of this post happened in that stretch. In early August we got the first clean payload decode, offline, out of a recorded capture. The next day the same firmware decoded a live transmission out of the air and matched what the reference decoder on the old dongle reported for the same packet, digit for digit. Two independent implementations agreeing on one reading is about as close to ground truth as a home lab gets.

Decoding still wasn’t perfect. Some packets came through with scattered bit errors, and we had a theory about why, involving runs of identical bits getting stretched by timing drift. We asked for a second independent review, and it told us the theory was a red herring. What was actually going on: the circuit inside the radio that decides whether an incoming wobble counts as a one or a zero was getting it wrong in one direction better than ten to one, and a bias that lopsided is something you can subtract back out.

Then came the good part. Neptune encodes its data so that every legal packet contains exactly the same number of one-bits. That’s an integrity check the protocol has been handing out free since the day it was designed, and we had walked past it for seven years. Whether anybody else has ever used it we honestly don’t know, and we didn’t go looking; what we did do was verify the property bit for bit against every real capture we had, then against a pile of randomized ones, and it never once broke.

Chasing a Meter That Moves

The meter doesn’t stay on one frequency. It hops. Parking on a single channel got us around five packets an hour, fine for a dashboard and useless for anything else, so the firmware learned to guess where the meter would go next and be sitting there when it arrived.

On a live run in late August it landed on 171 of the meter’s next 172 hops, and better than nine times out of ten it could tell on arrival that the transmission was ours and not a neighbor’s. A few days later, hop-following stopped being an experiment and became the default receive mode.

The project isn’t finished. There’s a port to that smaller chip sitting in the tree right now whose README opens, in capital letters, with a warning that it has never been flashed to real hardware. It builds, it passes tests on a development machine, and it has never met a radio.

What We Actually Think About This

Six of the forty-two commits behind the working firmware carry a Claude co-author trailer, and four of those six land within the same two days: the plan, the review, the sign error, the first clean decode. The rest is ordinary human work, most of it spent measuring things.

AI is a multiplier for people who know enough to catch it being wrong. For everyone else it's a very fast way to produce confident nonsense, and our own 2025 folder is the proof. Same kind of assistance, same enthusiasm, no verification step, and a firmware file that read beautifully and never once decoded a packet. What changed a year later wasn't a better model. It was the decision that every claim, the reviewer's own corrections included, had to survive contact with a datasheet or a measurement before anybody believed it.

If You’re Poking at Something Like This

Apicula is a small nearshore team of senior engineers with the founders in delivery. This particular meter is a side project, but the way it got done is how the client work gets done too: slowly, at the bench, with the person making the claim also holding the probe.

So if you’re trying to work out how AI assistance actually fits into your engineering practice, past the demos and into the part where hardware time and production incidents cost real money, we’d like to hear about it. We have the failed branches to back up our opinions.

And if you’ve just got a project like this one, the kind that gets picked up and put down across years until it finally clicks, we’d enjoy hearing about that as well.