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:
- BGL-RTL-VUHF, the APRS receiver (a BeagleBone Black + RTL dongle)
- RPi-UTILITY, the MQTT Broker (a Rasberry Pi 2 that runs a few things around my house)
- 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!