Internet shortwave radio using Max, Hamachi, and Mumble

How to control an amateur radio transceiver over the internet, using Osc (Open Sound Control), VOIP (Voice over Internet Protocol) and VPN (Virtual Private Networks).

What problem does this solve?

Using a shortwave radio receiver in a  live performance without installing a large antenna system.

This method gives low-latency real-time access to audio, and radio control using a laptop computer from anywhere. I suppose it could also remote-control a synthesizer, if you’re into that kind of thing.

CAT

Modern ham radio receivers can be controlled using serial commands using the CAT (Computer Aided Transceiver) protocol. Usually this is done via a USB port. There are hardware solutions for remote controlling radios over the internet, like RemoteRig http://www.remoterig.com/wp/. But there is also a free, or low cost, solution using software.

System diagram

Screen Shot 2015-12-21 at 1.23.10 AM

The ‘base’ computer is connected to the radio/antenna. The ‘remote’ computer is a laptop that could be anywhere connected by WiFi

For this experiment we used a TenTec Eagle transceiver connected to a MacBook USB port. The audio output of the radio connects to the audio input of the MacBook. The MacBook is directly connected to an internet WiFi router using an ethernet cable.

VOIP

A mumble client runs on the base computer, https://en.wikipedia.org/wiki/Mumble_(software)  and also on the remote laptop. Both clients are connected to a Mumble server (Murmur) at Mumble.com http://www.mumble.com/mumble-download.php. You could also run your own server. I set the audio to the best quality and muted the microphone on the remote laptop. We are only using the laptop as a receiver. For transmitting, you could simply open up another channel on the Murmur server. Mumble has very low latency (compared to Skype) and decent audio quality.

Bi-directional commands using VPN and OSC

CAT commands go in both directions – to and from the radio. For example, you would send a command to the radio to change frequency. The radio would send acknowledgements back to the remote laptop.

This is a problem for networks that use NAT (Network Address Translation) because local IP addresses are private, hidden behind routers. The solution that eventually worked was using a VPN called Hamachi https://secure.logmein.com/products/hamachi/download.aspx on both the remote and base computers. Hamachi servers are setup on both computers and connected to each other. This allows the computers to ‘see’ each other as if they were on a local network.

Max and Osc

Max patches are run on both the base and remote computers. The Max patch on the base computer connects to the radio using the serial object and passes commands back and forth over the internet using udpsend and udpreceive (which use Osc).

The Max patch on the remote MacBook sends and receives commands from the base computer using updsend and udpreceive. With the Hamachi VPN, Osc works just like it does on a LAN (local area network).

Automatic reconfiguration of clients

The main advantage of this system is that when you move the remote MacBook to a new location – for example, a coffee shop with public Wifi – both the Mumble and Hamachi clients automatically reconfigure for the location. So you don’t need to know the actual IP address of your computer in the coffee shop. The reconfiguration usually happens within seconds after the Wifi connection is made.

Alternatives

If you are just working across a LAN, you don’t need a VPN. Osc will run on a local network using private IP’s.

You could also try Ross Bencina’s Oscgroups http://www.rossbencina.com/code/oscgroups. Although I was not able to get Oscgroups to work, other than in a LAN.

For uni-directional Osc communication from remote to base, in a WAN (wide area network) you can use a static IP address for the target.

Skype is another (free) solution for transmitting VOIP audio. Set the base computer in auto-answer mode and call it from the remote computer. Skype will process the audio more than mumble, with noise gates and such. And the latency is higher. But its very easy to set up.

Development

The next step is to build a remote interface for the radio that uses Midi/Osc controllers, so for example you can turn a dial on the Midi controller to change frequency or filter settings on a base radio.

to be continued…

Pd signal streaming objects

Use streamout~ and streamin~ to stream audio over a local network

The ‘help file’ for streamout~ has an example. Change ‘localhost’ in the connect message to the local IP address of the target.

xively.com feed with Arduino

[Note: xively.com is gone. This system doesn’t work. Post is here for historical reasons only]

Bi-directional communication from Arduino to a xively.com feed using an ethernet shield.

  • Initializes an internet connection (DHCP)
  • Connects to xively.com servers every minute
  • Stores random value in the feed using HTTP PUT
  • Retrieves current feed value using HTTP GET
  • Lights up LED when transmitting
By the way, xively used to be cosm used to be pachube… 
Arduino circuit
  • Use an ethernet shield.
  • Connect ethernet cable. (I am using a Netgear WNCE2001 ethernet to wiFi adapter)
  • LED is connected to pin 5 and ground. The shorter lead connects to ground.

download

[wpdm_file id=18 title=”true” ]

files
  • xively_test1 (Arduino sketch)
Arduino files and libraries

Copy the xively_test1/ folder to Documents/Arduino. This puts it in the Arduino sketchbook.

Notes on installing xively/cosm/pachube libraries for arduino: https://reactivemusic.net/?p=4900

Instructions

  1. Connect Arduino to Macbook via USB.
  2. Open the Arduino serial monitor to initialize the ethernet connection and display the IP address.
  3. Every minute data gets send to the feed
  4. Monitor feed data here: https://xively.com/feeds/98281/workbench
Arduino sketch


/*
5/20/2014 - Arduino/xively feed interaction
Uses Ethernet Shield and and LED connected between pin D5 and ground
Sends a random value to a xively.com feed every minute
The LED lights up during data transmissions
demonstrates:
HTTP PUT - send data to xiveyly feed and store
HTTP GET - read xively feed value
*/
#include <SPI.h>
#include <Ethernet.h>
#include <HttpClient.h>
#include <Cosm.h>
int ledPin = 5;
int upCount = 0; // counters for number of times going up and down
#define API_KEY "96PqSh4rj7HzNif3WtTpN7GjX96SAKxrWms3SUhwaDFGUT0g" // your Cosm API key
#define FEED_ID 98281 // your Cosm feed ID
// MAC address for your Ethernet shield
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x0B, 0xCE };
// note that pins 0 and 1 are used by the Ethernet shield
unsigned long lastConnectionTime = 0; // last time we connected to Cosm
const unsigned long connectionInterval = 60000; // delay between connecting to Cosm in milliseconds
// Initialize the Cosm library
// Define the string for our datastream ID
char sensorId[] = "count";
CosmDatastream datastreams[] = {
 CosmDatastream(sensorId, strlen(sensorId), DATASTREAM_FLOAT),
};
// Wrap the datastream into a feed
CosmFeed feed(FEED_ID, datastreams, 1 /* number of datastreams */);
EthernetClient client;
CosmClient cosmclient(client);
void setup() {

 // initialize the detector pins 

 pinMode(ledPin, OUTPUT ); // internet transmitting indicator
 // start the Monitor (console) serial port

 Serial.begin(9600);
// display happy messages 

 Serial.println("Xively test");
 Serial.println("==========================");
// Keep trying to initialize the Internet connection
 // Note - we should eventually timeout of this and just run the stairs independently

 Serial.println("Initializing network");
 while (Ethernet.begin(mac) != 1) {
 Serial.println("Error getting IP address via DHCP, trying again...");
 delay(15000);
 }
Serial.println("Network initialized");
 Serial.println();
 // print your local IP address:
 Serial.print("Arduino IP address: ");
 for (byte thisByte = 0; thisByte < 4; thisByte++) {
 // print the value of each byte of the IP address:
 Serial.print(Ethernet.localIP()[thisByte], DEC);
 Serial.print("."); 
 }
 Serial.println();
 Serial.println();

} // end of setup function
//////////////////////////// control loop ///////////////////////////
void loop() {
 // main program loop

 ////////////////////////////// Internet sending/receiving code ////////////////////////////////

 if (millis() - lastConnectionTime > connectionInterval) {

 // uncomment this to just send a random value...
 upCount = random(256);

 digitalWrite(ledPin, HIGH ); // turn on transmitter light
 sendData(upCount);
 // read the datastream back from Cosm - comment out to save time
 getData();
 digitalWrite(ledPin, LOW );
 // update connection time so we wait before connecting again
 lastConnectionTime = millis();

 }

 ///////////////////// end of internet send/receive code /////////////////

} // end of main loop code
/////////////////// additional functions //////////////////////////
////////////////////////////////////////////////////////////////////
// send the supplied value to Cosm, printing some debug information as we go
void sendData(int sensorValue) {
 datastreams[0].setFloat(sensorValue);
Serial.print("Read sensor value ");
 Serial.println(datastreams[0].getFloat());
Serial.println("Uploading to Cosm");
 int ret = cosmclient.put(feed, API_KEY);
 Serial.print("PUT return code: ");
 Serial.println(ret);
Serial.println();
}
// get the value of the datastream from Cosm, printing out the value we received
void getData() {
 Serial.println("Reading data from Cosm");
int ret = cosmclient.get(feed, API_KEY);
 Serial.print("GET return code: ");
 Serial.println(ret);
if (ret > 0) {
 Serial.print("Datastream is: ");
 Serial.println(feed[0]);
Serial.print("Sensor value is: ");
 Serial.println(feed[0].getFloat());
 }
Serial.println();
}