Most of this project is, for the most part, discontinued. But more pictures and videos are on it’s GitLab repository.
Back in May of 2019, before that disastrous road-trip; I was installing some ham radio equipment in my car when I found myself in a bit of a pickle. I didn’t feel like spending money on a CB…because I’m classier than that (kidding.) I did however think it might be good option to be able to at least listen to what was going on. While the ham-radio I was installing in the car was not legal to transmit on the CB band; it was perfectly legal to listen to them. But the problem became one of channelization; CB is channelized to something like 40 channels (there’s SSB channels and stuff I’m not getting in to) where as my radio is not. It does have memory presets; but only about 22 of them.
This would be one of the first times I ever took an idea and turned it in to a rats nest of wires, hardware, and code; that actually did the thing I wanted. The fact I did not know Python, anything about interfacing with the GPIO pins, or the iCOM CI-V interface/command-set wasn’t going to stop me. This was after all, Linux; all the things I wanted to do were possible on their own. I just had to figure out how to plug them all together.
The hardware side would consist of two basic units; the data interface between the RPi’s serial port and the physical user interface. The user interface would be simple; 7-segment LED digit displays for a channel read-out, and a rotary encoder. Communication with the radio happens over a serial-data bus; however it is a single-wire open-collector bus that runs at TTL level. Since we need some kind of isolation/conversion to go from UART TxD/RxD to the single-wire; it also provides the level conversion.
Originally my intention was to literally just plug external binaries together if required; it would all depend how I would handle the rotary-encoder input. I wasn’t even sure if I was set on using Python, other than people saying “the Pi in RPi is basically for Python”. So the entire thing started by finding someone’s rotary encoder routine on a forum; and playing around with it while figuring out how to make Python do things.
I would eventually feel my way through the syntax that I figured out how to actually call an executable and pass variables as arguments. So I built the rest of the python code around that. You can look at the actual code on the GitLab repository; I’ll just summarize it’s operation here
- Look at current channel number to see if we need to “roll-over”
- Increment or decrement channel number
- Load frequency from array using channel number as index
- Issue command to binary to load new frequency in to VFO
- Update display with channel number
- Rinse, lather, repeat
When this began working, I was ecstatic. Yes, this was very basic stuff; and I started with only having a vague understanding of Python syntax. Seeing this work gave me goosebumps. Of course it was also the first version, really slow, and I soon got over my excitement. Ok…it kind of worked; not exactly the way I wanted.
I soon began trying to figure out how to improve things. Maybe the call to the external binary was slowing things up; so I figured out how to interface with a library instead. I got it working and it was still slow, but it gave me more feedback than the binary and showed me why it was slow. I finally cracked open the radio to solder the diodes to bump it’s baud rate up to 9600; but the direct library method was still too slow. At this point I thought to myself “why don’t I just bang the bytes directly to the serial port?” It was an idea I’d seen in another project for an entirely different brand of radio; but this person had largely written their own library that directly wrote the binary data.
So, I started studying how CI-V worked. It actually didn’t take me very long to figure out this stuff was literally just serial data moving across the pipe. I had actually never worked much with serial communications. I missed the era of the external modem and serial mice. I came in to things just before USB came out; and PCI and ISA cards were still a thing. Going out and making sure you bought a “real hardware” modem for Linux. My hardware career has all centered around parallel data communications.
I quickly began studying the format of a CI-V data packet and quickly was sending commands manually to the radio. I wrote a test-script that basically ran 10 frequency changes in rapid succession; one using external binary, one using the already developed library, and another that literally just sent the bytes directly to serial. Even before breaking out the smart-phone slow-mo to film the behavior; the sound the radio made made it very clear just how much faster the raw serial code was. The frequency changes were done so the radio had to “change bands” every time, this causes an audible click as it changes filtering networks. It was a pretty rapid clicking on both the binary and library versions; but the raw-serial literally sounded dangerous to run. Relays chattering so fast you can’t hear a single click.
What was actually going on, and the reason my raw serial code was faster; is that I was basically breaking rules. Being a single-wire bus; there’s no detection collision or anything. I think it could be somewhat easy to implement in hardware, the CI-V line itself is high when idle. But even then, there’s always the risk of packets getting interrupted, corrupted, or missed. So the device the command is addressed to will echo the command back; and the device sending it will know if it actually got heard. But in addition to this, the radio sends back a frame to indicate “ok” or “no”. You can’t eliminate the echo, at least not on the radio I was using; but you are able to use a couple of commands that silence the acknowledgement. The problem is the library isn’t set-up to do that; so I was waiting for 2 CI-V data packets to come across before the program would execute the next.
My raw-serial method, doesn’t even have provisions for dealing with serial data input; so it will just bang data to the serial port as fast as it can send the bytes. This is not the proper way of doing it, but to see how the radio responded; I set up a script to start at one frequency, increase the frequency 100Hz every loop, process that for a CI-V data packet, send the packet to the serial port, and loop the process. The only delay was going to be the inherent time to execute each part. I also set it up to display the frequency to the terminal. When it got to the end of the range, it started over. I let it run over-night and the next morning; the radio’s frequency display was still rapidly going up. I stopped the script and compared the VFO reading on the radio with the script’s output. They were only 100Hz off; which could be explained by the execution stopping that exact moment between sending the vfo and writing the output to terminal.
The end result of putting this in to my existing code was having exactly what I wanted. I had basically made a “memory preset” unit, that could flip channels as fast as you could spin the knob and have the RPi process it. So if I wanted to go from 19 to 6, I could spin the knob down there just as fast as a real radio. But as ashamed as I should feel about having made what I briefly dubbed “The GoodBuddy Controller” when I’m supposed to be a classy ham; I would quickly realize the potential for good to come out of it.
For many, many years; when you installed a radio in your car, you had to put the entire thing where you could get to it. But as cars have become smaller, more compact, and less radio-install friendly; technology has allowed that to no longer be the case. Pull the faceplate off the radio, like we used to do with car stereos; except now make a cable that plugs between the radio and the face-plate. This later evolved to the example I showed above; where everything is in a remote box and the radio itself is a small box you stow away. I typically call them “remote heads”, in some context they could be called detachable faceplates; usually requiring you to buy/make a “separation kit” since the radio won’t do it “out of the box”
But a lot of radios, especially the older ones, don’t have this feature. So why not bring it to those radios? Why not expand this to show the status, and control, more things on the radio? In fact, at some point you can argue that the additional head actually expands upon the radio’s functions. We already know it can act as a memory pre-set unit; but you can also program it to make the radio behave in ways it might not normally. We can pick an arbitrary VFO stepping, since we’re doing all the math on the VFO in the PI and sending a pre-calculated frequency. We can change the behavior so that when it gets to the end of one band, it will automatically jump to the beginning of the next one.
I had started working on my design for reference hardware and working out various function calls that would allow someone to easily design a display-driver for something else. In fact at one point I envisioned PiFO just being a memory/engine/logic controller that would interface with different display, input, and radio drivers. Actually, the more I think about it; I was probably developing an API without realizing that’s what I was doing.
What ultimately lead to the demise of development of PiFO was HiFiLOGIX. After getting some Arduino under my belt, I realized I didn’t need a full OS at my disposal; I just needed the right microcontroller. I actually got a lot of stuff done on the Arduino in communicating with the radio and actually converting the frequency data around; but I ran in to some issues with some initialization stuff I was trying out and wound up taking a still-ongoing break from it. I was doing my development on a Mega2560 board; as I was sure I would need more resources than a 328.