This post is about how I set up remote operation on my ham radio through a Wifi network, over a VPN, and around the world using a Raspberry Pi, like this:

This could be useful for people who travel a lot and want to be able to use their rig while away. Or for people who want to set up their radio and antenna far away from where they actually live (e.g. apartment dwellers who have friends or parents who live in the boonies and wouldn’t mind you erecting an antenna). If you want to skip the VPN and just control your ham shack from your kitchen, that’s even easier.

I did this all with an ICOM-7100 all-mode radio, a Raspberry Pi B+, a Buffalo DD-WRT router/VPN server, and a laptop running Ubuntu 15.04. This post focuses on the radio control and networked audio but not on setting up the VPN.

Prerequisites

Setting up the Raspberry Pi

I want the RPi to be a “headless” remote server (no screen, no keyboard), so I need to be able to SSH into it from my client. This is pretty easy. I set up public/private keys so I don’t have to type a password every time I log in. Follow the link to set this up.

If you want your Raspberry Pi to work on wifi, you’ll have to set that up too.

Building PulseAudio 6.0 on the Raspberry Pi

We need to use the PulseAudio linux sound system to pipe audio through the network. PulseAudio 2.0 exists in the RPi repositories, but I had much better luck with a more recent version (PulseAudio 6.0), which I built from source. Following this guide, I got it to work (I skipped the bluetooth stuff). In summary, first install some build dependencies:

sudo apt-get install -y libltdl-dev libsamplerate0-dev libsndfile1-dev libglib2.0-dev libasound2-dev libavahi-client-dev libspeexdsp-dev liborc-0.4-dev libbluetooth-dev intltool libtdb-dev libssl-dev libudev-dev libjson0-dev bluez-firmware bluez-utils libbluetooth-dev bluez-alsa libsbc-dev libcap-dev checkinstall 1 sudo apt-get install -y libltdl-dev libsamplerate0-dev libsndfile1-dev libglib2.0-dev libasound2-dev libavahi-client-dev libspeexdsp-dev liborc-0.4-dev libbluetooth-dev intltool libtdb-dev libssl-dev libudev-dev libjson0-dev bluez-firmware bluez-utils libbluetooth-dev bluez-alsa libsbc-dev libcap-dev checkinstall

There’s a libjson-c dependency that I had to build myself:

git clone git://github.com/json-c/json-c.git cd json-c ./autogen.sh ./configure make sudo make install 1 2 3 4 5 6 git clone git://github.com/json-c/json-c.git cd json-c ./autogen.sh ./configure make sudo make install

Clean pulse files that may be installed (note: this may break other installed packages like pygame)

sudo apt-get remove libpulse0 1 sudo apt-get remove libpulse0

Now get the PulseAudio source code and compile. I used checkinstall so I could remove the built package easily later on.

git clone git://anongit.freedesktop.org/pulseaudio/pulseaudio cd pulseaudio ./bootstrap.sh ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-bluez4 --disable-rpath --with-module-dir=/usr/lib/pulse/modules make sudo checkinstall --pkgversion 6.0 --fstrans=no sudo addgroup --system pulse sudo adduser --system --ingroup pulse --home /var/run/pulse pulse sudo addgroup --system pulse-access sudo adduser pulse audio sudo adduser root pulse-access 1 2 3 4 5 6 7 8 9 10 11 git clone git://anongit.freedesktop.org/pulseaudio/pulseaudio cd pulseaudio ./bootstrap.sh ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --disable-bluez4 --disable-rpath --with-module-dir=/usr/lib/pulse/modules make sudo checkinstall --pkgversion 6.0 --fstrans=no sudo addgroup --system pulse sudo adduser --system --ingroup pulse --home /var/run/pulse pulse sudo addgroup --system pulse-access sudo adduser pulse audio sudo adduser root pulse-access

Then I built the init.d script as in the link above to get pulseaudio to auto-start in system mode (yes, it says this isn’t recommended, but it makes sense on a Raspberry Pi). Start PulseAudio by rebooting or just using

sudo service pulseaudio start 1 sudo service pulseaudio start

Configuring PulseAudio to pass sound from the Raspberry Pi through the network

On the Raspberry Pi, edit the /etc/pulse/system.pa file and uncomment or add a line to turn on module-native-protocol-tcp, authorizing the local network to communicate with it.

### Enable networked audio load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/16 1 2 ### Enable networked audio load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/16

I also found it useful to comment out the module-suspend-on-idle line in there as it caused some 10 second lags at first.

Plug the radio into the Raspberry Pi and restart PulseAudio with:

sudo service pulseaudio restart 1 sudo service pulseaudio restart

Configuring PulseAudio on the laptop/client

On Ubuntu, PulseAudio runs on a per-user basis, not in system mode. As such, we can turn on and off modules with scripts, which is just what we’ll do. Make a script and put commands like this in it:

#!/bin/sh pactl load-module module-tunnel-source server=raspberrypi source_name=icom_source pactl load-module module-tunnel-sink server=raspberrypi sink_name=icom_sink # radio -> laptop speakers pactl load-module module-loopback source=icom_source # laptop microphone -> radio pactl load-module module-loopback sink=icom_sink source=alsa_input.usb-0d8c_C-Media_USB_Audio_Device-00-Device.analog-mono 1 2 3 4 5 6 7 8 9 10 11 #!/bin/sh pactl load-module module-tunnel-source server=raspberrypi source_name=icom_source pactl load-module module-tunnel-sink server=raspberrypi sink_name=icom_sink # radio -> laptop speakers pactl load-module module-loopback source=icom_source # laptop microphone -> radio pactl load-module module-loopback sink=icom_sink source=alsa_input.usb-0d8c_C-Media_USB_Audio_Device-00-Device.analog-mono

You will have to adjust the hostname of your Raspberry Pi (or just type in its IP address directly). You will also have to adjust the source of the second loopback module. I have it set to my USB microphone, but your laptop microphone will be different. Use the pacmd list-sources command to get a list of sources on your laptop and just take the name of the one you want. Make the script executable like this:

chmod +x radio_pulse.sh 1 chmod +x radio_pulse.sh

You could also enable networking in this script, but I find it more convenient to use the paprefs program and just select “Make discoverable PulseAudio network sound devices available locally”.

Finally, you have to set up authentication. PulseAudio uses a shared secret called a cookie to authenticate. Copy the cookie from the RPi at /run/pulse/.config/pulse/cookie to ~/.pulse-cookie on your client computer (in the home directory). Now it will authenticate.

Restart PulseAudio on your client with

pulseaudio -k && sudo alsa force-reload 1 pulseaudio -k && sudo alsa force-reload

Now, run the script created above. If all goes well, you should now hear the sound from your radio on your laptop. You may have to make some adjustments in the pavucontrol program. Turn down the radio squelch so there’s something coming through and then, on the playback tab, select Show: All Streams and make sure the loopback from the Raspberry Pi is playing on the device you want it to be playing on. It should look like this:

You can also run pavucontrol for the RPi from your client like this:

PULSE_SERVER=raspberrypi pavucontrol 1 PULSE_SERVER=raspberrypi pavucontrol

And then you can adjust the connections on the RPi side of things if necessary. I don’t think you’ll have to do this.

If you’re using a IC-7100 too, make sure you have it set either to DATA mode (FM-D) or in such a way that the USB audio will make it into the transmitter. Now you can do voice and digital modes through your network! Cool. Here’s a screenshot of running fldigi on my laptop, receiving audio in this kind of a setup.

Recall, at this point, we’re on a local network. We’d have to set up the VPN to go outside of our home through the internet.

Controlling the Radio from afar

If you just want to do digital modes through fldigi, you’re probably good to go at this point. You can have flrig running on the Raspberry pi and fldigi running on the client, and they will be able to communicate and key up the radio and stuff. But I wanted to do voice as well. So what to do?

Well, being a programming nerd, I thought it’d be fun to write my own little controller for the IC-7100. I did this in Python. It’s hosted on github. So far, it’s exceedingly simple, but it allows me to turn the radio on and off, go to different memory channels, turn DATA mode on and off, and key up the radio. This is a good start, but I hope to make it much more sophisticated later. I especially need to be able to type in the frequency I want or scan up and down the bands for HF. This should be easy to add. If you want to use it, feel free. But don’t expect much. If you want to improve it, please do!

There are also other programs out there that offer the ability to control a radio through the network.

Troubleshooting

I gone through many iterations and come across many problems while figuring all this out. At first, I did the PulseAudio configurations on the RPi but I eventually decided doing it on the laptop was more flexible. I tried very hard to get downsampling to work to minimize the required bandwidth. This is important for slow wifi networks but especially important when going over the VPN to the internet. I played a lot with resampling methods and took it down to 11025 sample rate. It works and sounds fine for a while, but it kept fading out every few minutes. I haven’t quite solved this issue.

To downsample, change your tunnel source to be:

pactl load-module module-tunnel-source server=tau.partofthething source_name=icom_source rate=11025 pactl load-module module-tunnel-sink server=tau.partofthething sink_name=icom_sink rate=11025 1 2 pactl load-module module-tunnel-source server=tau.partofthething source_name=icom_source rate=11025 pactl load-module module-tunnel-sink server=tau.partofthething sink_name=icom_sink rate=11025

This uses about 60 KiB/s over the network, as opposed to 200 KiB/s with the defaults.

To adjust the sampling methods, edit /etc/pulse/daemon.conf on the Raspberry Pi and change the resample-method = speex-float-1 setting.

In certain resampling conditions, PulseAudio uses too much of the RPi CPU.

The biggest problem is that over a VPN, there are a few seconds of delay as the audio travels through the internet.

If you’re experimenting with different settings, you can unload all the PA modules on your laptop with commands like

pacmd unload-module 32 1 pacmd unload-module 32

Where 32 is the index of the module to unload. Use pacmd list-modules to list all loaded modules. I do this a bunch as I tweak my script and rerun it.

Securely connecting to the LAN from afar with a VPN

A VPN will let you securely connect to your local network through the internet as if you were sitting in your kitchen, even though you may be in Paris. This unlocks the potential of the remote radio-through-RPi thing, as it allows you to do it all from everywhere.

Conveniently, I already had a “road-warrior” OpenVPN set up on my WRT-based router. This means I can connect to my home LAN at anytime from anywhere securely. I do this to print stuff from afar, talk to my internal devices, and avoid surveillance while on public or other untrusted networks. This is a non-trivial step that involves getting a WRT-compatible router, flashing it with a ROM that has an OpenVPN server (like OpenWRT or DDWRT), building some public/private keys, and configuring the server and clients. It’s not THAT hard, but definitely took me a while to get it working due to the large number of slightly different instructions available online. The GUI-based config in recent DDWRT builds makes it pretty easy. Get started with this guide (slightly outdated) and build some RSA keys and flash your router. Then go to this other guide that’s very close to what I actually did. Go buy some wifi security cameras while you’re at it because you can now securely connect to them and watch your home while you’re away.

I hope this helps someone. It was a lot of fun for me to figure out.

73!