Chasing AI: single-handedly programming a radio

We’re staying in Cape Canaveral for a month, right before GPSL, so as is predictable of me I brought a box full of radio gear to track radiosondes, capture AIS data, and anything else that piques my interest while we’re here.  I realized after texting a buddy this story that I should expand it for more to read.

I realized early on that the local Cape Canaveral Space Force Base launches old school LMS6-type radiosondes which are not as common as RS41 and DFM sondes. There I was the other day, watching a sonde come in for landing 2km from me, listening to the signal on my HT, without a decoder.  My RTL-SDR was broken and couldn’t track it; I was wishing that rdz_ttgo_sonde could decode LMS6 sondes.  The sonde likely landed out at sea but I was unable to track its last few km of descent.

This is the story of how I added LMS6 sthoring demoduupport to the TTGO radio, using Claude Code heavily.

I originally wrote this as a story to a friend, “I have gotten so lazy about things I used to do. I am literally programming a radio right now which is connected to my laptop on USB, across the room since I’m holding the baby, using remote control, flashing firmware, which has an experimental protocol on it that CC wrote”

It’s not laziness, it’s friction

When I say I’ve gotten lazy, what I actually mean is that the activation energy for a whole class of tasks has collapsed to near zero.

Think about what flashing firmware on an oddball radio involves. You find the right tool. You discover it needs a USB driver of some sort. You Google an error, land in a forum thread from 2014, try three things that don’t work, get annoyed, and put the radio back in the drawer. Tomorrow you’ll have the energy. Except tomorrow you don’t, and now it’s a “someday” project living in a box for a year.

The work was never the hours. The work was the walls. You’d hit one, lose momentum, and set the whole thing down. The reason these projects “took days” is that most of those days were spent not doing them.

What agentic AI actually does, for me anyway, is absorb the walls.

Predicting landfall

Side track from the programming nerdery: I did ask Cowork to build an ocean-water movement model to see where it might wash up.  Ultimately the odds of me ever finding it were incredibly low and thus I did not find it, but it was fun to try and produced a beautiful artifact (click on it!)

click for the interactive analysis version

Adding the LMS6 decoder to rdz_ttgo_sonde

After the sonde landed I sat there thinking “this can’t be too hard to add” – but authoring demodulators like this is a field of its own, one in which I don’t have much experience.  Reference code exists in the rs1729/RS project, upon which radiosonde_auto_rx uses to decode the data.  The RS project is GPL-3 and rdz_ttgo_sonde is GPL-2+ so the code can be incorporated into rdz_ttgo_sonde!  I asked Claude – it said the project would take days.

It was done 15 minutes later.

Code reviews, and my own skepticism, took an hour.  Was this worthy of an upstream pull request? This code would be added to a binary installed on countless devices when most users will never even use it. But after some quizzing of Claude and a bit of verification myself, I’m reasonably satisfied thus far.  Let’s test it!

Uploading to a new board

I plugged in a brand new TTGO Lora32 to my (also new) Mac laptop only to find I didn’t have the requisite CH34x serial driver.  In 2026, serial port drivers are not yet upstream, something Linux figured out a long time ago!

Claude led me down a rabbit hole of which driver to install, but in the end, I got there (it still takes an unfortunate amount of experience to recognize when an agent hasn’t done the research for the latest information by doing a web search – it was quoting me >5 year old advice for Intel-CPU Macs).

At this point, I ran /remote-control to operate from my phone while holding a sleeping/fussy baby.  One handed!

It then set up a virtualenv, installed the firmware upload utility esptool, identified the board, backed up the stock firmware for me as a convenience, and uploaded our new build of rdz_ttgo_sonde with the LMS6 patch.

I’ve done all of this before on other boards/laptops.  It would’ve taken me hours (during which fussy baby would have derailed me and spread the work across a day or two).  This took me less than an hour.  I sat there, holding the baby, configuring my newly minted radio to join the local wifi, the frequencies and decoders to try, and more.  What’s more – it connected straight to rdzSonde Go which is gratifying!

Real-world testing

Belt and braces: having just brought online a new RTL-SDR auto_rx station sitting next to the TTGO, I had an excellent local data set to test against.  I don’t yet want to start uploading to Sondehub for fear of streaming errant data, so for now my goal is local logging.  rdz_ttgo_sonde has AXUDP output which Claude happily wrote a logger daemon for, all to find that the AXUDP/AX25 encoding truncates the lat/lon precision that we’re receiving.  There is also MQTT support which I’ve settled on as the best reference data set.  Running up a Mosquitto broker and subscriber took minutes and was done mid-flight of the first or second LMS6 sonde I saw.

Initial reports showed one glaring bug: all my frames received were dated 1970-01-01.  This was easy enough for Claude to spot and fix.  Otherwise: frame by frame, everything I’m receiving appears identical to what I’m receiving with auto_rx.

Claude Code Monitors

This is an excellent use case for Monitors: I have it checking in on the MQTT logs to see if we’re tracking any Sondes.  Once it sees that we have – and that the flight is no longer logging on either rdz_ttgo_sonde (via MQTT) or my local auto_rx station, it runs a comparison, showing which stations received how many packets and compares frame-by-frame data.

It surprised me this evening: the Space Force launched an RS41 sonde!  (Please tell me they didn’t just run out of LMS6 sondes…)

Many hats

I was motivated to add this decoder only briefly while I was short an SDR, but I was able to make excellent progress in a matter of hours across just a couple of otherwise-busy days.  AI enabled me to wear many hats – ones which I’m accustomed to, but many are dusty/rusty and I would not have found the time or energy to wear:

  1. C++ programmer and tester not to mention modem aficionado
  2. ESP32 compiling guru
  3. MacOS sysadmin
  4. Data comparison and summarization

All stuff I can do – although #1 would have stopped me on investment of time.  I’d rather be writing this blog, taking my kids to Kennedy Space Center, and photographing rockets!

So yes, I have gotten lazy.  Lazy enough to finally get stuff done.

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Tagged | Leave a comment

Chasing AI: Who needs software anyway?

A friend said to me the other day, “In the future, there won’t even be software. It’ll all be AI.”

Sounds crazy right?  Or is it?

How AI replaced software (gpsd) for me today

It clicked for me today when I impulsively plugged an old USB GPS receiver into my laptop just to see if it worked.  I opened up minicom to see if it would get a fix, and quickly glazed over watching the stream of familiar but uninteresting log messages:

$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGLL,,,,,,V,N*64
$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGLL,,,,,,V,N*64
$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGSV,1,1,01,24,,,28*74
$GPGLL,,,,,,V,N*64
$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30
$GPGLL,,,,,,V,N*64
$GPRMC,,V,,,,,,,,,,N*53
$GPVTG,,,,,,,,,N*30
$GPGGA,,,,,,0,00,99.99,,,,,,*48
$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30

Those familiar with the GPS NMEA protocol can squint their eyes at this and can infer:

  1. It doesn’t have a GPS fix yet ($GPGGA,,,,,,0,00,99.99,,,,,,*48 shows no latitude/longitude, just a bunch of empty comma-delimited fields)
  2. It can see one satellite (the odd $GPGSV,1,1,01,24,,,28*74)

We have gpsd for this.  I don’t need to squint at this telemetry stream.  However a thought occurred to me: while I don’t have gpsd installed and ready to fire up on this GPS receiver, I do have a flexible AI agent I can throw at it.  It’s also quite possible that this GPS unit will never get a fix, so the event log of it finding specific satellites is interesting…it might be all that ever happens!

Claude wrote a bit of code to stream the contents of /dev/ttyACM0 to a log file on disk, and a Monitor process to provide updates back to the main agent loop.  It even detected the model of the GPS:

It then went from a stream of noisy events to something more useful.  It started to get events as it recognized more satellites:

This quickly was just noise.  I started asking it questions.  Will it ever get signal or is the placement just too bad?  How long do I need to wait on a cold start in a sub-optimal deployment environment like on my desk, below a monitor, indoors like I am trying right now?

While it monitored, I asked questions and it taught me things: GPS satellites move slowly across the sky, so flickering number of satellites in view is unlikely to be movement across the sky – it’s just weak signals.  This isn’t Starlink.

Then my friend’s point clicked.  I wanted less noise and I wanted to wait, so I let it keep trying and I asked it to rewrite it’s “software”:

I reprogrammed my “GPS status application” – Claude Code – on the fly.

The GPS eventually found a third satellite and after a few more minutes it got a fix as I had hoped for:

Could I write my own GPSd?  Sure I could.  But that’s not the point – the point is that using an AI agent in-situ means you don’t have to write the software up-front.  You don’t always need requirements and a plan like you did before the AI era.  Software can even be ephemeral like today’s exercise – none of the code it used even hit disk.

Here’s the full transcript if you’re curious to spelunk!

Further afield with OpenClaw

One of my OpenClaw experiments is to use it to track grocery spending and prices over time.  However I’m refusing to write any code right now.  All I’m doing right now is capturing data (photos of receipts) over time.  I know I’ll want to ask questions like “has the cost per pound of beef gone up over the past three months?”

My strategy for this is not to just optimize for the answers, but to see what questions the data set I’m reasonably able to collect can even answer.  Receipts aren’t perfect – will they prove useful?  I’m not going to code myself into a corner – instead I’m going to collect data and when we have enough, get the AI agent to build some analysis tools to try and answer my question.  Time will tell.

Challenging your own assumptions

Imagine the world of computing today had speedy NVRAM technologies ever competed with the volatile RAM + non-volatile “slow” disks, where applications have a distinction between running and not running.  Completely different mind set.  We need to apply that kind of thinking to AI!

We all know AI agents can write great code, but we’re still stuck in the old way of thinking that the software is the hard part.

Next time you think you need software, ask yourself: can an AI agent just do this for me?

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Comments Off on Chasing AI: Who needs software anyway?

The Impossible Job: Recovering a failing HFS+ external disk

A month or so back I overheard a conversation in my F3 Wheaton exercise group about a failed portable USB hard drive. The owner is professor Matthew Millner who has traveled as much as I have, has kids like me with priceless photos on that disk, and has ten years of PhD work and life on the disk.  Irreplaceable data.   I had to sit down and ask if I could help! If that was my data, I’d do anything to get it back!

Here’s how, over the span of 59 days, I was able to help.

I know, I know, people should make backups and/or use cloud storage.  But sometimes you don’t, and you just have to make the best of it.  He’s been hearing the lecture on backups for the past year since the disk died.  But as my grandfather-in-law would say, “If wishes were fishes, we’d all have a fry.”  Life is not a Douglas Adams novel: you can’t go back in time.  But you can try to do something about it.  The disk spins and shows a directory listing but none of the files are readable.  Some of it must be readable, right?

with all that padding around two spinning platters, how could it ever fail?

This is the story of how I helped get it back! I am not a hard drive recovery professional (and no, I did not stay at a Holiday Inn Express last night.) I have been taking computer hard drives apart since I was about ten years old.  In high school & college I went through a spree of dodgy 60GB hard drives which were fortunately in a RAID5 and I learned about freezer tricks, heat sinks+ice cubes, and the power of patient persistence. I’ve learned a few tricks since then that help: a sprinkle of microelectronics, and a dash of AI. Years of proximity to system administration taught me some tricks too; volume snapshots and copy-on-write filesystems and a sixth sense for failure modes.

The Impossible Job: a decade’s worth of PhD life on a failing 2TB drive

The disk is a LaCie 2TB portable USB hard drive.  Inside the package is a USB-SATA adapter with a standard 2.5″ SATA disk, a Seagate ST2000LM007 which has two spinning platters inside of it.

I plugged the USB enclosure in, got a directory listing, and the minute I tried to read anything it stopped.  That’s not a good sign!  I half expected to hear the traditional clicking noises of a drive which can’t seek at all and continually tries to re-seek, but I did not hear any so I was encouraged.  I moaned when I saw it was HFS+, but in retrospect, that was not a problem.  Every time I tried something, I had to unplug and re-plug the disk to get it to respond again.  Using the USB enclosure vs SATA directly didn’t change the behavior either.

I tried some fans to cool the disk to no effect, to the point of attaching some DS18B20 temperature sensors to each side of the disk to see if it was getting dangerously hot – it was not. But the graphs (in Home Assistant of course) proved useful later on.

First Contact: Why did dd keep dying at 40GB?

I suppose a hard drive expert can answer that question better than me!

My next step was to try cloning the disk with dd on linux.  Uncle Mintel left me a small cache of big 3.5″ old 2TB disks, and after some quick smartctl verification tests, I picked a couple that looked fit for the job.  I set up the disks on my workbench in the basement in an old PC that I use for GPU experiments, and started to try to clone the disk with dd.  Every time: it hit 40GB, the disk stopped, and the SATA link reset.  It needed a power cycle.  I clearly couldn’t afford to spend time re-reading the same blocks on the disk, so I needed a new plan.

ddrescue to the rescue!

ddrescue is dd but for recovery from a dying disk.  I’d never used it before, but it is exactly what I needed.

You point it at a source and destination disk, it tries to read, and keeps a map of every block it’s tried, what was copied successfully to the target disk, and what produced read errors.  You can run it iteratively, as many times as you want.

My disk was special: it never actually returned read errors; the disk simply performed so slowly that the host never saw a series of read errors – it just saw the disk time out and go away.  The useful part: you can restart ddrescue ad nauseum, at any arbitrary point on the disk, and it will focus on that area of the disk.  I quickly found that jumping ahead to 60GB that I could read the next 20-30GB cleanly.  However I’d eventually run into one of two problems: the disk would timeout and reset and need a power cycle, or a second failure mode emerged: the disk would read at 131kB/sec.  Mysterious?  Probably not – 131kB/sec is exactly 1000x slower than the “normal” read rate of the disk.  Presumably the disk was moving slowly because it was having to retry reads 1000x and using conservative error correction (which should be a pretty good indication of a physical failure, but I persisted!)

The problem with power cycling the disk all the time

I mean sure it’s annoying.  But what was really annoying to me was that it was in my basement; on top of which we have a newly-walking 1-year old baby.  I had to open the gate, go down; she’d want to come with so I’d grab her.  Pulling a cable out of a disk is a two handed operation, so I’d set her down on the basement floor, do the cable dance, pull out my SSH client on my phone, and tell the OS to refresh the SATA host.  Then grab the baby, go back upstairs, close the basement gate, set the baby down.  This got tiring quickly.

I’d learned a few years back that SATA host ports on Linux can be listed:

for host in /sys/class/scsi_host/host*; do
 h=$(basename $host)
 ata=$(readlink $host | grep -oP 'ata\d+')
 if [ -e $host/device/target* ]; then
  blk=$(ls $host/device/target*/*/block/)
  model=$(cat $host/device/target*/*/model)
 else
  blk="<none>"
  model="<none>"
 fi
 echo "$ata -> $h -> $blk -> $model"
done

For example, my file server:

root@minor:~# ./list-sata-devices 
ata1 -> host0 -> sda -> Samsung SSD 850 
ata2 -> host1 -> sdb -> Samsung SSD 850 
ata3 -> host2 -> <none> -> <none>
ata4 -> host3 -> <none> -> <none>
ata5 -> host4 -> sdc -> Samsung SSD 850 
ata6 -> host5 -> sdd -> Samsung SSD 850

You can then tell the scsi host to refresh a port that you just hotplugged:

echo "- - -" > /sys/class/scsi_host/host4/scan

I’ve yet to find a consumer grade SATA host which doesn’t tolerate hotplugging.

I had that – but I needed an automated way to power cycle the disk and save both me and the 1-year old the trips to the basement.

ESPHome ESP8266 + relay on the 5V Rail

Because hey, why not?  I have north of 30 esp8266 MCUs that I’ve built stuff on, all plugged into Home Assistant.  I borrowed a relay node that I use to power cycle DFM radiosondes when on the bench and wired it up to the 5V rail on a SATA power adapter (I left 12V unplugged given that little disks don’t even use the 12V rail from what I’ve read.)  A HASS auth token and a bit of curl later, and I had a script that did an end-to-end power cycle of the disk:

  • Power off the disk and wait a few seconds.
  • Refresh the SCSI (SATA) port to make sure the kernel sees the disk is gone
  • Power on the disk and wait a few seconds
  • Refresh the SCSI port again.
  • If the disk didn’t come back, sleep for 60 seconds and try once more, and if it still doesn’t work, print a big error on the screen and wait for user input.

This setup was the key.  I left the basement one day, and didn’t touch the host for an entire month afterwards as I slowly worked on it.

Timeouts as a Strategy: Skipping the Tar Pits

F3 AO pun not intended

Now that I had a working power cycle procedure, I could try reading the entire disk in segments.  I wrote a simple wrapper which ran ddrescue at 10GB increments, wrapped in a bash “timeout 120” which meant that if I hit one of those 131kB/sec slow spots, or the disk timed out and needed a power cycle, that ddrescue would exit out.  The loop turned into:

  • For x in (10GB segments up to 2000GB)
    • timeout 120 ddrescue read as much as you can
    • Test the disk. Is it offline?  If so, power cycle it with the procedure above.

This was gold.  In a couple of days, I had recovered 724GB of the disk’s total 2000GB area.

Making it Visible: Writing ddrescue log visualizers & HA graphs

The log file is a long machine-readable text file not meant for humans.  It has one block range per line, so on every ddrescue run it got longer.  The ddrescuelog CLI tool didn’t work for me for some reason.  I threw this one at Claude Code which wrote a CLI and HTML visualizer:

I already had the relay switch visible on Home Assistant, which yielded a history graph showing if the disk was powered on or off and the corresponding temperature sensors below it, so you can glance at what was happening autonomously while you were sleeping:

I went further – as regular readers may know, I am obsessed with getting every bit of my data into Home Assistant…read on!

Zooming In: From 10G to 1G to 100M resolution passes

I revised my wrapper script to then do 1G segment passes, with a correspondingly shorter timeout, and eventually I went on to 100M segments.  Smaller segments took much longer; a lot of the remaining data surface area was reading at the slow 131kB/sec rate.  Every now and again, my power cycle controller would hang – I’ve found that a Home Assistant server hang has weird knock-on effects for my esp8266 nodes.  OneWire temperature sensors start returning 80C like parasitic power problems and my relay GPIO would stop toggling, and I’d have to power cycle the node! Still unresolved.

At the completion of the 100M chunk pass, I had a lot more of the data, but I estimated that if I let it run on every block, it would take another 45 days to read it all at 131kB/sec – and that’s not counting power cycles (which became so numerous I stopped paying attention and just kept hoping that the next power cycle would not be the disk’s last.)

Tangent: proof of life (and an excuse to use another hard drive)

I needed to see if I was getting anywhere. I took a few hours’ pause from ddrescue and cloned my recovery target disk over to a second spare online 2TB disk. Pro tip: hotplug disk names like /dev/sdd are not stable across reboots! You knew that. Check your serial numbers or disk device IDs; fortunately in this entire process I never rebooted once (nor did the mains so much as flicker) until the process was complete.

Once my dd to the spare disk was complete, I wanted to mount the volume and run fsck_hfs but I also wanted to snapshot and work with a copy-on-write (CoW) image of the disk. The underlying partition was 100% the size of the physical disk, so the CoW store needed to live elsewhere. My OS disk is a mostly empty 1TB lvm volume which unhelpfully is 100% allocated and I didn’t want to go offline just to lvresize.

I asked Claude.  It’s a pretty good sysadmin!  I created my CoW store as a file on the OS disk, made a loopback device, and used the dm-snapshot method.  Worked perfectly.

I ran fsck and mounted the volume.  I clicked around.  My jaw hit the floor. Even with many GB still to recover, I had tons of photos and sent the owner some proof-of-life photos!  Needless to say, he was excited!

The Breakthrough: Reading the HFS+ allocation file (aka bitmap)

Big thank you to Matthijs Kooijman for their article Recovering data from a failing hard disk with HFS+

This article described a lot of the same challenges I had, and coincidentally also the same filesystem.  The power cycling tricks described there never worked for me, but the HFS+ pointers absolutely did: HFS+ maintains an allocation file which gives a map of which blocks on the disk were in use.  Cross-reference this with the ddrescue log file, and I was in business. I knew exactly which blocks to focus on recovering!

I prompted Claude Code to write a hfsplus_priority.py script to generate 16,000 arbitrarily-sized ddrescue commands for all the remaining blocks, and also publish this to MQTT.  The generated bash script would run for days; the MQTT publish I ran hourly to make me my pretty graph.  This was fun – I could post to the F3 Slack on occasion for people to cheer for the little hard disk that could:

This took another week or so.  At which point my graph levelled out at 40G to go.  Only a few segments in the ~16,000 had failed to read and weighed mere megabytes each – what did I miss?  The ddrecover master process said it was complete!

Trust, but Verify: The day Claude admitted to “helpfully” skipping blocks under 1MB

I teach classes on Claude Code.  I teach people to find ways to verify what it comes up with.  I’m well beyond Spicy Autocomplete; I’m often the FSD driver asleep at the wheel except instead of sleeping, I am parenting.  I should have expected this. In the hfsplus_priority.py script, Claude Code helpfully added a --min-size flag which defaulted to skipping blocks smaller than 1MB.  Claude’s context at this point in the project was huge, and full of references to iteratively trying to recover the largest ranges of data I could assuming that the disk would eventually crash.  It didn’t take long to find the problem (I asked Claude!) and I was on my way. This last recovery sweep added another few days and thousands more segments to cover, most of which were tiny, but added up to 40GB.

Almost there…

The “bytes to recover” graph in Home Assistant marched on to nearly zero.  A few megabytes were truly unreadable no matter how many times I tried, and every time I tried the disk needed a power cycle.  I was done.  I’d recovered all I needed to.

Integrity audit & reporting

I did the same dm-snapshot CoW setup, this time for the primary recovery disk.  Browsing around the volume was like living in a dream: it all worked!  But did it?  There are 600,000 files on the volume.  Let’s be systematic.

I prompted Claude Code to work up an auditor script which attempted to open every file and see if its file type and detected mime type (using the file command mime-info magic database) matched, and then I extended it to actually attempt to open every single file that it could using a Python library to see if the input data was sensible.  Reality hit: loads of files were unreadable, about 1/6 of the total count of files.  A bit of heuristic analysis showed that most of the unreadable files were auto-generated iPhoto thumbnails.

That said: a darn good result given that the data owner had already written it all off.

I took counts of file types, broken down by recovered vs lost, and had Claude Code write a little user-friendly report summary.  I packaged the 800G of data (now some of it gibberish, left in-place) and copied it to the user’s new scratch disk, a solid-state disk, which he said was bound for iCloud.  A good move for important data!  (I have since confirmed the data has made it to iCloud! Long live the data!)

Fin. Done.  I shut down the recovery computer.  Uptime: 59 days elapsed from start to finish.

Lessons from an Impossible Project

“No problem can withstand the assault of sustained thinking.” – Voltaire
“Nothing is impossible, except skiing through a revolving door.” – Woody Allen

the “rig” with the hard drive under examination in the upper-right on a piece of wood

A bit of AI and experience sure helps.

This is one of the joys of being self-employed: the idea that my effort, my drive, is all mine.  I volunteered for a seemingly impossible job, one which I would not normally pick up.  I said yes, expecting to lose and get nothing in return. When the going got tough, I still said “what else” and used a lot of tools.  AI was helpful, not just for technical coding, but also as a consult.  How many platters does this hard drive have?  Why is it pulling data so slowly?  Why is it timing out from the SATA bus?  How do I power cycle a disk on Linux?  Is there anything  I can do about the speed?  Can I tell the firmware to stop retrying so hard?  What’s the syntax for smartctl?  I can’t count on two hands the number of disparate chat threads I’ve made across Claude and ChatGPT and Gemini about this.  I’ve made so many on Claude that it now has a memory of the model of disk, and every now and again guesses that I’m talking about this disk when I ask an unrelated linux block device question!

Next World Backup Day, I’ve got a story to tell.

Posted in Uncategorized | Comments Off on The Impossible Job: Recovering a failing HFS+ external disk

Chasing AI – Claude Code as a Sysadmin

Others have said it already – Claude Code is great at a lot more than just code.  It’s great at using your computer.  Everyone needs a sysadmin from time to time, right?  I’ve used it for all sorts of bizarre things and wanted to share some of my stories to give others ideas beyond just coding.

Toshiba Flashair SD-Card configuration

I have this nifty little SD card for cameras which broadcasts a WiFi signal and lets you download photos remotely. It works while you’re taking pictures – handy for my old Canon Rebel T5i from the olden days of DSLRs. Toshiba stopped supporting their smartphone app so I stopped using it, but I wanted to revive it after years of disuse. Upon inspection I found that the card wasn’t broadcasting its AP. It uses a text file onboard which configures the firmware for what SSID to use and more; maybe I left this thing in a bad configuration? There are many online references for the config options and many permutations to try.

One of the biggest lessons I’ve learned using Claude Code is that you need to give it a way to validate its results – in code this is usually done with tests. Can we get it to iterate on this problem? Absolutely! In Linux, you can scan for WiFi networks at the CLI and just grep for it.

Claude tried a few permutations before it got it. The only thing I had to do to help it iterate once I gave it the right privileges was unplug and plug the card back in after each config change and watch Claude try stuff.

Check out the transcript. This was a fun one!

Docker Compose fun

I run a small (ish) stack of containers on a home server – things like Home Assistant, the fabulous solaredge2mqtt, my beautiful Metra Timetable, and a lot of experiments and hacks that I play with. I was running the old CLI docker-compose which has been deprecated for some time. The day finally came when something really broke: the underlying Python library dropped support for the http+docker URL scheme that old docker-compose was using.  Its day finally came after an apt update as docker-compose was throwing an exception every time I ran it.

I thought this would be a good experiment to ask Claude Code about; it’s great at bizarre error messages and can also digest the specifics of my setup when suggesting solutions. The long and the short of it was switching to docker compose like everyone else probably did years ago. Check out the transcript here.

Simple challenge: why is this package installed?

I happened to notice during an apt update some familiar packages from Ceph – but why does my laptop have Ceph packages installed?!

Normally I’d roll my eyes at the increasing complexity of modern Linux distributions, but I figured I’d toss the question to Claude Code since it can inspect my system. Sure enough, it’s because I am hacky enough to have libvirt/QEMU installed on my system.

See how Claude Code not only figures it out, but also comes up with a helpful, human-readable explanation.

Forensic analysis of a user’s Windows user profile

I’ve been helping some family with a tech challenge: a senior whose Windows 11 laptop crashed, never to boot again, but the Users directory was intact.  We need to recover data they might not even remember exists, and in particular, figure out their Microsoft Online / OneDrive account username.  Simple, right?

I spent too much time back and forth with AI chat bots trying to identify the user’s Microsoft Online account ID which was embedded somewhere in the registry and/or text files – but nothing they suggested yielded a result.  I’d all but given up, but finally threw the entire Users directory at Claude Code and said “figure it out.”

The beautiful thing about an agent is that it can try the dozens of permutations of places to look, really quickly, to find that proverbial needle.  I expected it to produce a negative result.  Sure enough, ~15 minutes of supervising the agent’s trawling of the hard drive and it yielded the simple,  definitive result.  I about fell off my chair.

Transcript if you’re curious.

TL;DR

Claude Code’s capabilities beyond code are generally underestimated – it is clearly capable of work well beyond the realm of coding.  See what you can get it to do!

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Tagged | Comments Off on Chasing AI – Claude Code as a Sysadmin

Chasing AI: Check engine light diagnostics and AI? Really?

Years ago I bought one of these little OBD-II wireless diagnostic plugs because I wanted to datalog my car…a hobby project which naturally, like so many of my hobbies, I never finished.  The plug has sat in the console of the car, waiting for a day when I needed it.  Today was that day.  The check engine light came on during a road trip.  I haven’t driven a car with a check engine light since I was a kid!

Home late at night, over-caffeinated from the drive, I flashed back to what I’d learned while driving: my plug uses Bluetooth Serial Port Profile (SPP) which doesn’t work with iOS, so I can only use it with Android or Linux.  I figured I’d pull out pyOBD which I’d used years ago for my datalogging project as it had some level of GUI, and Claude was confident that it’d get me the diagnostic trouble code that my car wished it could tell me.

I’ve been using AI coding tools so much I decided to roll up my sleeves and get my hands messy.  I spent 20 minutes in Python dependency hell all to realize that pyOBD requires Pythons from a more civilized age.  I threw my hands up, and wrote a desperate prompt to Claude Code, my own little “Help me Obi Wan Kenobi, you’re my only hope” type prompt that you write when you’re tired.  It didn’t bother with pyOBD; it went to python-obd, and just drafted its own little app.  I wasn’t looking forward to Bluetooth pairing on Linux and the rfcomm binding dance, but it did some of that work for me.

It’s nothing to write home about, but it’s functional! I had to try it right away.  Laptop in the car, ignition on, I dealt with the bluetooth pairing and result:

FOUND 1 TROUBLE CODE(S):
============================================================

CODE: P0128
OBD Description: Coolant Thermostat (Coolant Temperature Below Thermostat Regulating Temperature)

I then excitedly pasted the transcript from the application back into Claude Code, which asked me for the make/model of the car, and went on to suggest that I may need a new coolant thermostat (a common problem) and to also check that I’m not low on coolant.  It offered to research videos on how to replace, estimated time to repair for someone handy, and estimated shop costs for the job – all the things you’d expect an AI assistant to do.

Makes me wonder – is there a market for a Check Engine AI Assistant App?  Buy a plug, skip the stressful “free” trip to the auto shop, get a diagnostic code and an AI recommendation on next steps?

I had this dusty $20 plug in my console for years and AI made it useful in 20 minutes.

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Tagged | Comments Off on Chasing AI: Check engine light diagnostics and AI? Really?

hey.com email, a nice experiment, but I’m back to Google Workspace

A couple of years back I got fed up with email (as I perpetually do in waves throughout my life) and decided to try something other than Google Workspace / paid GMail. 37signals had just released hey.com for domains so I could bring my trick@vanstaveren.us email name over there, so I gave it a go.

At first I was stoked!  In fact I was, for about the first year.  Main advantages:

  1. New senders didn’t make it to your inbox; they made it to basically a quarantine box for new senders which you had to whitelist.  This is great!  At first.  Something hitting your inbox is a momentous occasion – a real person with something you want to read!
  2. Automatic filing boxes for “The Feed” (newsletter-type things that are optional to read) and “Paper Trail” (receipts, etc).  These are great.  Probably about 80% of email by count is one of these two; and having these as baked-in concepts keeps email simple.
  3. It’s on my domain!
  4. It’s different!

The not so nice:

  1. Offline never really worked.  I don’t use it much, but I learned that if I wanted to use it offline, it wasn’t worth trying.
  2. Spam filtering is there but isn’t great.  1/3 of new senders were spam.  While you can filter them out as spam, I found myself skimming a lot of spam to realize I was about to hit the “junk it” button.
  3. Plus addresses don’t work as you’d expect; if I want trick+blah@vanstaveren.us in my inbox, I have to go add it as an alias.  Most of these ended up in the Catch All box, which I never looked at, because it’s full of junk.
  4. The calendar.  There’s nothing wrong with taking a stab at your own calendar app, but manually copy/pasting Google Meet links from my free @gmail.com account into hey calendar made me crazy.  The calendar also was not sharable with my spouse nor my DakBoard display screen in my kitchen, nor any general view on my mobile phone, so events in there lived and died in there.  I kinda liked hey.com more without the calendar, because I would just forward invitations to my free @gmail.com account and deal with the confusion from there.
  5. What is this notes cover thing for? To stop me from getting distracted by all the email I’ve read, I guess?  I literally wrote “ahoy” on a note on day 1 and never used it and was only ever annoyed to find the feature was still there and I’d accidentally clicked on it.

 

My inbox never actually looked this clean.

What really drove me away:

  1. The search.  Keyword searching was awful because I get a lot of newsletters that go into The Feed and are full of every word ever.  Don’t search for “QuickBooks” if you expect to find that email from a real human last week about QuickBooks because Money Stuff mentioned Intuit and QuickBooks half a dozen times in the last week, and that real human email is a page down or more.  Mistakenly hit enter on the quick search pullout?  You’re now reading the first, and wrong, email – not looking at a longer list of search results.  Best bet? Search for the person who sent you the email and scan through it.  This is faster.
  2. The iOS app supports notifications, but no red dot / bubble to show me an unread count.  I’ve come to rely on this in my move to iOS.  If I have something unread, it needs to show a red bubble.  The app does not.  (Why?!)
  3. Don’t try to reclassify a sender to go to The Feed if they start sending you junk – you’ll get lost in the maze of settings for what a sender is classified as.  Did you know you can also classify an entire domain name?  Of course you can!  But you’ll forever confuse yourself with layers of settings for where stuff should land.  It’s not intuitive.  The end result? Often when junk landed in my inbox, I started to feel it was faster just to mark it as read and forget about the seconds I just lost, rather than losing minutes to figure out how to reclassify something.  I knew I was going to move away from this email so I lost faith in the system that was supposed to save me time.
  4. Mailing list classification does not exist.  I’m on a few Google Group-type mailing lists and writing a filter for these in any modern email client is a sinch; hey.com does not support them.  You have to Yes/No the individual people you want to hear from.  You can’t send them all to The Feed where they probably belong.

The end result?  Email is annoying – it has been annoying for decades, but after a year on hey.com, it became annoying again.  I just couldn’t get over some of this stuff.  It took me another year to convince myself to migrate away.

This month I’ve done it – exported all my data, gone and re-started my Google Workspace subscription, and I’m finishing up the import now.  My inbox has never been cleaner (it helps that OpenClaw is monitoring my inbox for me daily and suggesting junk filters I should add!)

</hey.com>

Thanks for all the fish.

Posted in Uncategorized | Tagged , , , | Comments Off on hey.com email, a nice experiment, but I’m back to Google Workspace

Home Assistant integration: Claude Usage

If you’re like me and constantly running up your Claude subscription usage to its limits, you want a graph. Anthropic has two rate limit windows; one which is five hours long and one which is a week. Go beyond either and you need to put in a credit card for extra usage, upgrade to Max, or – wait! What kind of AI charged engineer wants to wait?

I do more than half of my usage on my mobile phone, so I wanted the best and easiest graph platform a mobile user can get: Home Assistant.

Check out the open sourced code: https://github.com/trickv/hass-claude-usage

Installation with HACS is easy:

    1. Add the repository as a custom repository in HACS
    2. Restart Home Assistant
    3. Install “Claude Usage”
    4. Restart Home Assistant
    5. Go to Settings → Devices & Services → Add Integration → “Claude Usage”

Once it starts collecting data, make a History Graph with the data points and the window you find this useful for; I usually look at the 24 hour window. See the README for an example dashboard.

Let me know if you find this useful or are managing your usage in other ways?

How I built it – iteratively

Building this little tool was certainly different to how I would have approached this in the pre-AI past.  I would have defined not just the end goal but also the methods clearly and worked straight towards it, optimizing to spend as little time coding as possible.  Given that this is a hobby project, I probably never would have finished it!  The API was elusive at first and I couldn’t find examples for how to use it.  But the AI-enabled engineer approach lends us to an iterative approach, where code is cheap, and making something that works is important to justify spending more time on.  My iterations looked something like this:

  1. First version which used the HA API to create sensors, and used Claude Code CLI in a tmux session to get usage data. It would run Claude Code, wait a few seconds, type /usage, and grep the output.  It worked, albeit running claude code periodically contributed to usage!  But it gave me a graph, and it was useful.
  2. Refactored to run Claude Code and keep it open in a persistent tmux
    1. Somewhere in here I learned about Anthropic’s rate limits on the usage endpoints which block you for 24 hours when you hit them too much!
  3. Learned how to use the APIs directly to fetch usage status, but this approach had to steal Claude Code’s session token (which expires every few hours).
  4. Moved from HA API to MQTT integration to get better data into Home Assistant. This was a surprisingly easy refactor for Sonnet 4.5!
  5. Refactored to standalone OAuth (first time I used Opus 4.5 on this code)
  6. Now refactored into this Home Assistant custom integration, installable by HACS

How are you managing usage?

I’m curious to hear how others on low usage plans like mine (Pro) are managing their usage.  Let me know if  you find this useful!

Posted in Uncategorized | Comments Off on Home Assistant integration: Claude Usage

Chasing AI: Vibe coding a New Years Resolution tracker app

I follow the AI Daily Brief podcast and decided to take on their New Year’s challenge to complete an AI challenge/exercise each week as a way to learn different AI tools. For week one, “build a new years resolution tracker”, I went to my good friend Claude Code, but got ambitious – to code an iOS app.

Prior Work

I’ve been working on a few apps in the past year:

  • Porting rdzSonde, a radiosonde on-the-go tracking app which talks to hardware, from Android to iOS
  • A baby monitor app (coming – soon!)
  • Now this!

Having done a bit of Android development over the years and being more of a Linux head, I’ve found the iOS learning curve to be a challenge, but I’m finally getting to grips with it. So while this is the second app I’ve side loaded to my iPhone, it’s a decent size jump for me.

Approach

My initial goal was to prompt Claude Code to take some of what I’ve spent tokens on before (a GitHub Actions workflow which builds an unsigned IPA package file, and a corresponding AltStore repository definition json to make it convenient for me to sideload and test).  Get a “hello world” app working end to end, and then start building features.  It went well!  I wrote the original prompt for project framework on my laptop during a brief bit of free time between wrangling kids; almost all of the rest of this project continued with Claude Code in a SSH/Mosh session on my phone, nibbling away at it when I had time.

Step 1 – Initial Prompt

Here’s my initial prompt to build an iOS app, to use my baby monitor project Actions as examples, to build no features other than a hello world, and to produce an AltStore source json as a GitHub artifact.

This worked pretty well.  This was my first time building specifications for AltStore so this required some trial and error which is not surprising.  I steered the agent a bit with a decision to use React Native and played some back and forth with errors coming from AltStore.  It’s fun to be able to push a git tag, wait for it to build, and refresh AltStore, walk by my Mac and lift the lid (so the signing AltServer is awake) and install the latest version.

It worked!

Interesting stuff I learned along the way:

  1. Keep hitting Esc whenever you have something to say.  Don’t just queue your message and wait for the agent to finish what it’s doing; this is Claude Code – it might work for minutes only for you to redirect what it just did.  If you hit Esc on accident, just say “continue”, it’ll happily move on.
  2. Tailing build logs with Opus 4.5 on repeatedly failing builds with the Pro $20/month subscription is a great way to waste most of your tokens.  Switch to Haiku (hit Esc and interrupt!) or better yet, ask the agent to build a quick script which tails the logs, waits for completion, and only returns when there’s something interesting to show.
  3. You can, in fact, never open a Mac to build apps for a Mac!  This is not entirely true; I did have to lift the lid 30° to awake it so AltServer would run, but I never opened Xcode once.

Step 2 – the features prompt

I’m not kidding when I say I did this part from my phone, over SSH, with no autocorrect:

1) title and deacription; target date(s) for milestones if applicable…a resolution might have a single milestone. a simple checklist for each milestone and if all are conplete, confetti animation and the resolution gets crissed off. (2) progress: a journal entry log format. no expectation about how often; no promptijg the user, i may never use it. a single line question below the Resolution where you enter “Whats Next?” to ebcourage baby step thinking. user journey – list view of all resolutions, basic indicatioj of done/not done/ some milestones done, tap the resolution for details, show all milestones, recent journal entries, and the ability to edit. yes separate screen for creating a new resolution. (4) data – local. i dint know what AsyncStorage is but if thats your go-to its fine by me. use a data format that lets me make wild changes and keep some amount of data preserved as ling as the features for them are preserved. (5) screens: home screen eith list, resolution detail page which shiws Whats Next, list of milestones with checkboxes by them, and then journal entries for each; finally a resolution Edit page which shows a lot of the same stuff as in view mode but its all editable. vision; i expect to use this myself and for no one else, and jusr dir 2026. borrow some theme from my 42 project since im 42 this year (/home/trick/src/github.com/trickv/42) give it a HHGTG-inspired icon, maybe a pixelated bowl of petunias.

The actual transcript is here, but this is my one-shot explanation of features.

I’ve been using SSH from mobile devices for years and miss the days of my HTC Dream with it’s slide-out keyboard for accurate text input on SSH, but LLMs are incredible at understanding my typos so much that you can see I don’t bother to correct all kinds of ridiculous typos.

This yielded an…interesting result:

I got distracted working on the launcher icon and didn’t notice me running up against the five-hour session limit which I hit right as I was figuring out how to get a screenshot copied onto my dev host to show Claude Code.  It’s terrible at handling the session limit; since it was half way through a task list, it spammed pages upon pages of /rate-limit-options before I hit Esc and went to bed.

Step 3 – the fix

The model was quick to find the issue, which was a font size issue, and et voila!

I also went over to Nano Banana Pro and followed on my HHGTG theme and came up with a bowl of petunias icon for the launcher:

Result

Check out the live demo:

I should be blown away – but actually, this all makes sense to me.  It’s exciting, but it’s not surprising.  Based on my experience in the past half year, that I can expect Claude Code to be this good.  The only question is – what on earth to do with this capability?

Bugs I’ve Found

  • The giant “D” font thing.  Fixed.
  • The “What’s Next” text input box had a bug where it was storing input immediately and causing the UI to glitch when I typed too fast.  Fixed.
  • There was no scrolling when the keyboard popped up.  Easy enough to explain.  Fixed.

What’s Next

This gives me more fuel on the fire to run with other mobile app development ideas.  While this app isn’t a product worth much itself, the experience, and the open-sourced examples here are great for the next idea.  Much like I borrowed from the Baby Monitor project repository for Github Actions configurations, I can now borrow from this project (which I did earlier today on rdzSonde – I borrowed a feature from this app!

Something Interesting

Writing down my New Years Resolutions in this app has made me think and set some personal goals for myself that I otherwise would not have.  If nothing else, this little experience has helped me focus on some self improvement.

Got questions?  Reach out – I love talking about this stuff.  I work part-time as a freelance contractor and dad of three which means I love talking to adults.

Until next time – may your models be grounded, and your prompts be precise!

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Tagged | Comments Off on Chasing AI: Vibe coding a New Years Resolution tracker app

42

The answer to life, the universe, and everything.

(But what, you say, is the question?!)

I’ve done a lot in the past 41 years, but for my next lap around the sun, I have added a special goal: become more whimsical. If that’s even possible.

Douglas Adams left us a gold mine of silly stories, many of which I’ve read and this coming year I will re-read. I might even turn it into a weekly newsletter for 42-year olds. Let me know if that idea appeals to you.

In the meantime –

Everything will be fine. I’m just a year older. In fact, I’m just a day older than I was yesterday.

~ Trick, 2025-12-13

Posted in Uncategorized | Comments Off on 42

Chasing AI: Claude Code for…mobile!

I’ve been using Claude Code both in real work and for some wild “vibe coding” type experiments in the past few months — including but not limited to vibing an MCP server which lets you run Claude Code from Claude Desktop (with the goal to later port it to web). This MCP server experiment was motivated by the fact that Claude Code in a terminal is not a great experience:

I don’t mind the tiny text size – I mind the lack of autocorrect and the loss of scrollback due to using mosh.  Yet – it’s so dang useful I still use it almost daily while on the go; anything from hacking on the Metra Timetable to porting an Android app to iOS to editing Home Assistant automations.

It’s good that I never spent much time on the MCP server because the day before I published my last post on Claude Code, Anthropic released what they call ‘Claude Code for web’ but I may call it Claude Code for Mobile – because that’s where I think it really shines.

Code Code for web runs on Claude’s infrastructure inside a lightly configurable sandbox – so you don’t have to run a server in your basement like I do.  Repositories get checked out from GitHub into your environment, and changes get pushed back as PRs.

Demo

It’s all about show and tell here, so here’s a demo of me running Claude Code on my mobile phone alongside implementing the same feature using CLI Claude Code:

Claude Code CLI yielded a very similar result – not bad!

Fun observations:

  • Note the picker is in the middle on Claude Code CLI instead of the left – to be fair, I did not specify where it should land.
  • They both produced the same “bug” – misinterpreting the difference between Saturday and Sunday schedule, so the resulting Saturday schedule shows just a single train.  Turns out Engineers do need to review PRs from AI coding agents!

Actually on the web

I later learned that when you run Claude Code in a desktop browser, as perhaps they intended, it shows full diffs unlike the mobile experience.

Teleport

Super cool – not sure when I’ll use this feature in anger – the “Open in CLI” button – I figured it’d just take me to a help doc to tell me how to install Claude Code for CLI, but it “teleports” your session to Claude Code CLI – importing all that beautiful, productive context into the CLI interface.  It requires you to have a local working copy of the repository and then you just run the command like claude --teleport session_nBaAuyuUfjltLdfz6qr5 and off it goes.

Nifty!

Got questions?  Reach out – I love talking about this stuff.

Until next time – may your models be grounded, and your prompts be precise!

📧 Chasing AI: Get notified of new posts

Enter your email to be notified when I publish something new:


I’ll only email you about new blog posts. No spam.

Posted in Uncategorized | Tagged | Comments Off on Chasing AI: Claude Code for…mobile!