APRS notes

I don’t do a lot with APRS but I normally leave my RTL dongle/ BeagleBone Black receivers running 24/7 so I figured I may as well set the V/UHF receiver up as a receive-only iGate and fill in the black hole for APRS reception around my home location.  This was surprisingly easy to do (since I already had the RTL dongle and the BeagleBone Black connected up and running as an rtl_tcp server), in fact I think it took about 10 minutes and I configured it all from my Android phone!  A full explanation for a Raspberry Pi-based setup is on G6NHU’s excellent page and it only took minor adaptation for the BeagleBone Black.

Watching the APRS channel for a given text string

I thought it might be fun to write a script to watch the APRS frequency and trigger something when a given bit of text is received… as an example, turn an outside light on when I send a particular text comment in an APRS transmission local to my house, you can use your imagination for other more interesting things you could drive from APRS.

The key to this is a bit of Python code to watch the output of the Direwolf software modem.  It took a few goes to get this right so I thought I’d post it up here:

# watch_aprs.py
# A simple Python script by G7UHN to do stuff when given text is received by a RTL/BeagleBone APRS receiver

import sys from subprocess
import Popen, PIPE

# Guidance from https://stackoverflow.com/questions/2804543/read-subprocess-stdout-line-by-line

proc = Popen('rtl_fm -f 144.80M - | direwolf -c sdr.conf -r 24000 -t 0 -D 1 -', stdout=PIPE, shell=True)

while True:
    line = proc.stdout.readline()
    if line != '':
        print "Decoded:", line.rstrip()
        if "G7UHN" in line: print "Look it's my call!! :-) "
    else: break

This Python script launches rtl_fm and direwolf as a subprocess and reads the output line by line.  For each line read it prints the line (preceded by “Decoded:” so I can see what was actually processed by the script and didn’t just appear on stdout from somewhere else).  Then, if the line contains some given text (G7UHN in this example), it does something (prints a frivolous message to the screen in this case!).

OK, let’s take this a bit further and use this to trigger some home automation stuff.  I have a spare Raspberry Pi equipped with an Energenie PiMote transmitter for remote-control mains switches.  Two different ways of getting networked devices to talk to each other around the home are MQTT and Node-RED (both so-called Internet of Things or IoT protocols) and I’ve been meaning to have a play with them for a while.  Let’s do it…

Remember – there’s no security here, no authentication, so this is just a bit of fun and shouldn’t be used for anything really important!  😀

APRS receiver to MQTT

Note in the examples below I’m using three devices:

  1. BGL-RTL-VUHF, the APRS receiver (a BeagleBone Black + RTL dongle)
  2. RPi-UTILITY, the MQTT Broker (a Rasberry Pi 2 that runs a few things around my house)
  3. rasperrypi, the MQTT subscriber (a spare Rasperry Pi equipped with the Energenie PiMote)

You could just as easily run the MQTT Broker on either the BeagleBone Black or the RPi and cut this down to two devices, I just happen to have spread it across three.  I followed this guide for setting up MQTT, pretty simple stuff.

Modifying the script above to publish an MQTT message:

# APRS_to_MQTT.py
# A simple Python script by G7UHN to publish an MQTT message when
# given text is received by a RTL/BeagleBone APRS receiver

import sys
from subprocess import Popen, PIPE
import paho.mqtt.client as mqtt
import datetime

client = mqtt.Client("RTL-BGL-VUHF")
client.connect("RPi-UTILITY")
client.loop_start()

# General guidance from
# https://stackoverflow.com/questions/2804543/read-subprocess-stdout-line-by-line
# http://www.steves-internet-guide.com/into-mqtt-python-client/

proc = Popen('rtl_fm -f 144.80M - | direwolf -c sdr.conf -r 24000 -t 0 -D 1 -', stdout=PIPE, shell=True)

while True:
    line = proc.stdout.readline()
    if line != '':
        # Print each line received by Python to stdout
        print "Decoded:", line.rstrip()

        # Look for a given text string...
        if "MQTT switch on" in line:
            # Send an MQTT message to the APRS topic
            client.publish("APRS","Turn the switch on")
        if "MQTT switch off" in line:
            # Send an MQTT message to the APRS topic
            client.publish("APRS","Turn the switch off")
else: break

Now some python to trigger the PiMote when the MQTT message is received:

# MQTT_to_PiMote.py
# A simple script by G7UHN to trigger the Energenie PiMote upon 
# reception of MQTT messages containing given text strings...
# Based upon Energenie's example code, ENER002.py

import RPi.GPIO as GPIO
import time
import datetime
import paho.mqtt.client as mqtt

broker_address="RPi-UTILITY"

client = mqtt.Client("raspberrypi")
client.connect(broker_address)
client.subscribe("APRS")

# set the pins numbering mode
GPIO.setmode(GPIO.BOARD)

# Select the GPIO pins used for the encoder K0-K3 data inputs
GPIO.setup(11, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)
GPIO.setup(16, GPIO.OUT)
GPIO.setup(13, GPIO.OUT)

# Select the signal used to select ASK/FSK
GPIO.setup(18, GPIO.OUT)

# Select the signal used to enable/disable the modulator
GPIO.setup(22, GPIO.OUT)

# Disable the modulator by setting CE pin lo
GPIO.output (22, False)

# Set the modulator to ASK for On Off Keying
# by setting MODSEL pin lo
GPIO.output (18, False)

# Initialise K0-K3 inputs of the encoder to 0000
GPIO.output (11, False)
GPIO.output (15, False)
GPIO.output (16, False)
GPIO.output (13, False)

# The On/Off code pairs correspond to the hand controller codes.
# True = '1', False ='0'

def on_message(client, userdata, message):
    message_text = str(message.payload.decode("utf-8"))
    print "message received ", message_text
    print "message topic=",message.topic
    print "message qos=",message.qos

    if "Turn the switch on" in message_text:
        time.sleep(0.2)
        print "sending code 1111 Socket 1 on"
        GPIO.output (11, True)
        GPIO.output (15, True)
        GPIO.output (16, True)
        GPIO.output (13, True)
        # let it settle, encoder requires this
        time.sleep(0.2)
        # Enable the modulator
        GPIO.output (22, True)
        # keep enabled for a period
        time.sleep(0.5)
        # Disable the modulator
        GPIO.output (22, False)
        print "The switch is ON"
        client.publish("APRS","The switch is ON")

    if "Turn the switch off" in message_text:
        time.sleep(0.2)
        print "sending code 0111 Socket 1 off"
        GPIO.output (11, True)
        GPIO.output (15, True)
        GPIO.output (16, True)
        GPIO.output (13, False)
        # let it settle, encoder requires this
        time.sleep(0.2)
        # Enable the modulator
        GPIO.output (22, True)
        # keep enabled for a period
        time.sleep(0.5)
        # Disable the modulator
        GPIO.output (22, False)
        print "The switch is OFF"
        client.publish("APRS","The switch is OFF")

print "This script controls Energenie socket No.1"

try:
    client.on_message=on_message #attach function to callback

    print "connecting to broker"
    client.connect(broker_address) #connect to broker
    print "Subscribing to topic","APRS"
    client.subscribe("APRS")

    #Loop the MQTT client continuously
    client.loop_forever()

# Clean up the GPIOs for next time
except KeyboardInterrupt:
    GPIO.cleanup()

Note, I’m seeing the “turn the switch off” behave a little unreliably, sometimes takes a few goes to trigger, maybe extending the sleep periods will help.  This is also a reason why I’m interested to try Node-RED as that may be a more robust method that some Python I’ve just cobbled together.

Now, if any APRS messages are decoded by my receiver that include the text “MQTT switch on” or “MQTT switch off”, the light will be switched on or off respectively.  Obviously this is just an example and I post it online so that it might be useful to someone creating their own project.

Repeating the warning above – there’s no security here, no authentication, so this is just a bit of fun and shouldn’t be used for anything really important!  😀

APRS to Node-RED

Just beginning to play with this but first impressions are it’s remarkably easy to set up Node-RED on the Raspberry Pi, somebody’s already made a Node for the Energenie Pi-mote and I’ve got my light turning on and off within a few minutes using the slick web browser interface… cool!

Node-RED running on the Raspberry Pi