As 1,763,954 other makers have done, I decided to do a 'retro-radio -> internet-radio' build. My wife enjoys listening to UK radio channels so my plan was to build her a radio she could use in the house to listen to the channels she enjoys (we don't live in the UK).
My requirements for the build were as follows:
- Housed in a nice 'retro' radio box
- All original knobs must work
- This includes the volume control, on/off switch and the tuner knob
- 'Tuning' should be performed as if locally situated in London (meaning that if the tuner display shows 95.8, the radio should be playing Capital London)
- Ideally built with components I currently have on my bench.
I obtained a nice 'Amstrad' AM/FM radio and began the gutting. I removed the power supply, AM-FM IC chip and chopped off the passive components on the PCB. I left the knobs attached to the PCB as-is.
As you can see in the picture above, the radio is controlled via an OFF-FM-AM 3-way switch and 2 variable knobs. There is also a 'display' showing frequency currently tuned.
In order to actually power the logic, I chose to use a Next Thing Co. CHIP computer. These are little Debian-based Linux embedded computers ala rPI, however what's cool here is that this guy is only $9 and comes with wireless connectivity, storage and audio output built in!
The plan was to somehow interface all the analog controls with the CHIP and write a python script responsible for polling the inputs and controlling MPlayer accordingly.
I started off by looking at the volume knob. This is a standard 3-pin potentiometer (variable resistor) which I use to drive an analog voltage change. Unfortunately, the CHIP doesn't have any usable ADC inputs. Realising that I'd probably need to make use of ADCs in this project, I decided to use a discrete IC, the MCP3008, which provides 8 12-bit ADCs and interfaces over SPI (which the C.H.I.P supports with a custom kernel). I simply connected the volume pot to the MCP3008.
The 'tuner' knob turned out to be a little more complicated. I was hoping that this would be a pot as well. However much I tried to get some sort of voltage swing from this guy, it simply ended in failure. I had no idea how to connect this 'pot' up. It had 6 pins (4 connected to the AM-FM IC and 2 GND); nothing like any pot I had ever seen before.
Turns out it wasn't a potentiometer at all; it was a variable capacitor - a 'varicap'! This is a component I had never actually played with before. They are very popular in superheterodyne radios. The 4 screws you can see through the PCB are for fine tuning the capacitors.
Mine has4 pins connected to capacitors, in 'dual' ganged configuration; meaning that there are 2 pairs of 2 caps each, each cap connected to its partner in parallel. Turning the knob varies the capacitance of all 4 caps.
So the question now is what could I do with this? I didn't want to modify the radio mechanically in any way (e.g. to use a pot instead of the varcicap for example). So what I did was connect the 2 gangs of capacitors together (to make 4). I did this due to the capacitance of caps connected in parallel being the sum of capacitances of all the capacitors. As varicaps are apparently in the 10s-100s of pF range (i.e. little capacitance) I wanted to maximize the capacitance I could get out of this guy.
In order to make use of the varicap, I needed some way of converting a variable capacitance to a signal that the CHIP could sample. I decided to build an astable multivibrator circuit out of a 555 timer I had lying around (pro tip: always keep a stock of 555s lying around).
The output of an astable multivibrator is a square wave. By varying the capacitance of C1, the frequency of the wave changes. The frequency is also determined by the resistor values, R1 and R2 according to the following formula:
f = 1.44/(R1 + 2R2)C1
As mentioned above, the varicap capacitance is extremely low. This would naturally lend itself to a higher astable multivibrator frequency output which is no good for my purposes. In order to lower the frequency as much as possible, I would need to chose suitably large resistors to compensate.
The largest resistor I had lying around was 1M. I intended to chain a bunch of these together for R2 but I landed up forgetting about it and soldering only a single one on to my PCB (the value of R1 becomes irrelevant when R2 >> R1; I used R1 = 2.2k). This results in a frequency swing of around 1.8-8.2KHz for my resultant square wave. This means that the capacitance of my varicap swings between 90pF and 400pF.
Unfortunately these frequencies are still too high to be sampled reliably from a python script running on the CHIP. In order to get it down to the frequency range I'd need, I either had to use an obscenely huge R2 value (which I couldn't do), or 'divide' the signal. This can be done with a frequency counter.
Unfortunately I didn't have any discrete frequency counter ICs lying around, so I used a TI TevaC MCU (I originally intended to use a MSP430 which is nice and cheap but I couldn't get interrupts working on the chip I had for some unknown reason). The MCU code simply fires an interrupt on the rising edge of the square wave pulse and increments a counter. I then use this counter to generate a PWM signal with a period proportional to the frequency of the astable multivibrator output.
At this point I may have been able to sample the PWM from the python script. However, not wanting to bother adding logic for this and needing to perhaps mess around with slowing things down even further, I just pipe the PWM through a simple RC filter which turns it into an analog voltage signal that varies based on the PWM period. This is connected to an ADC on the MCP3008.
For the ON-FM-AM switch, I simply worked out which traces on the board would be affected by changing its state. These I used as another input to a MCP3008 ADC. I could have simply connected it directly to a CHIP GPIO, however I didn't want to mess around with buffering or anything. A on-off signal connected to an ADC simply produces the maximum-minimum possible values.
The CHIP software is pretty straightforward. The python script is launched by systemd on boot and runs with a nice level of -19. The script continually polls the MCP3008 for the three inputs (on-off, volume, tuning). A station list consisting of station names and audio stream addresses is used as a lookup for the currently tuned frequency. Upon station changes ('tuning'), the script will first play a .wav of the station name. This serves two purposes: Firstly, station changes are not immediate (due to mplayer needing to access the new stream), so by first playing the name, the user at least has some feedback when changing channels. Secondly, it allows the user to know exactly which channel they are currently tuned too (some of the FM frequencies for channels are fairly close together). I tried to use TTS for this, but it turned out glitchy so I have just created .wavs for now.
Anyway, even though this build is a little hacky it seems to work well so I'm pretty happy about how it has turned out!
As always, the source code is available on GitHub.
Here's the prerequisite video of it in action: