Sending APRS data with Bash

I have APRS set up in the shack, using YAAC and a Raspberry Pi to send weather data. It's also configured so when I want to, I can turn on a radio, and send/receive via RF, while being an iGate and/or digipeater.

I also figured out how to send/receive text messages and email, and installed an FT400 in the truck to do APRS on the go. I used to use an HT and Mobilnkd VNC, with an external antenna. That sortof worked, but was unreliable with the 5W output.

Wanting to round out using APRS for even more things, I wanted to send telemetry information from the ham shack as well. First thought I'd do it via YAAC, but to do that, I needed to write a server or client that would send the data to YAAC. Not a huge deal, but another process that needs to connect through another port, given I had to gather the data from other places, just felt too messy.

All the status for the shack was available via REST calls to my home automation hub (Hubitat), so I figured either a shell script or Python program would be my best option. A search dug up an article from a Ham that wrote a Bash script to send APRS data. I played with that, and figured out how to make it work for my needs. In case it helps anyone, posting my version of the script here. One thing I found, that is not in the script I posted here, was another handy tool: jq. I used that to parse the JSON returned by the Hubitat REST calls, to just get the values I wanted. Was a lot easier than manipulating the JSON in Python!

Given how well this ended up working, I may migrate my weather reporting from YAAC to just adding it to this script. Then I can reserve YAAC for when I want to run a digipeater/iGate.

The current information I send is the temperature in the shack, and the states of two power supplies (a binary on/off).

#original from

#I run this script via cron every 30 minutes.

#Define login info (include your APRS-IS password)

#Define object user info (make 8 characters, so with the 1 space padding you get the required 9 characters)
senduser='KW6V-15 '

#Define APRS-IS server

#Define station location

#original authors location:

#Time between header updates in seconds (12 hours=43200)

#Define data

#Authentication variable
aprsauth="user $user pass $password"

#Original temperature calculations...

#Aprs telemetry protocol accepts 3 whole (int) 
#numbers only so we need
#to convert the result (tempfloat) into 3 whole 
#numbers. So if tempraw=40.3, it will
#convert it to 403. If tempraw=8.5, it will convert 
#it to 85 and we'll add the leading zero
#to create 3 number format 085 later below.

#Read raspberry-pi CPU temperature
#tempraw=/opt/vc/bin/vcgencmd measure_temp #Read pi temperature

#Filter result to numbers only 
#tempfloat="$(echo "$tempraw" | awk -F= '{print $2}' | awk -F\' '{print $1}')"

#temp="$(echo "$tempfloat * 10" | bc | awk -F. '{print $1}')"

# Placeholder temp value

#Other things I measured, leaving the code here for example
#The on/off status of two power supplies, large and small
#The results I got back from my query would be "on" or "off", 
#So the code reflects creating headers and binary data from
#that input


if [ "$pwrL" = "off" ]; then
if [ "$pwrS" = "off" ]; then

#assemble the bits...

#create a data checksum

#echo "'"$pwrL"'" ":" $pwrLB ',' $pwrS ":" $pwrSB

#I didn't want to transmit identical data packets.
#Check the checksum against the stored checksum
#note. If the file doesn't exist, write different 
#data, so it'll run the first time.

#Check if file exist
if [ ! -f "/tmp/aprs_data.txt" ]; then
  echo $aprsData'1' > /tmp/aprs_data.txt

#Read previous data, if it hasn't changed, simply exit. No need to publish duplicate data.
#Unless you really want to. If you want smooth temperature/voltage graphs over time, then
#you would want to comment out this conditional, and run the script as often as you want
#it updated. I just push when the temp, or a switch state is different from the last time.

read oldaprs < /tmp/aprs_data.txt
if [ "$oldaprs" = "$aprsData" ]; then
  echo "data identical, bailing..."
  exit 0
  echo $aprsData > /tmp/aprs_data.txt

#Project comment
projectcomment='Shack status'

#Generate telemetry strings
#Read data and put it into variable
#%s means string
#%03d means prepend up to 3 zeroes, so if the value is 8, 
#you'll get 008, if the value is 80, you'll get 080 etc.
#This needs to be done or APRS will deny the packets as invalid. 
#The other things is, everytime you send the telemetry,
#a sequence number has to change, it's defined as T# in a APRS protocol. 
#We will automate this next.

#Check if file exist
if [ ! -f "/tmp/sequence_number.txt" ]; then
 touch /tmp/sequence_number.txt

#Read sequence number. Everytime the scripts runs, the number will 
#rise by 1 until it comes
#to 1000 and then returns back to 0. Everytime the script
#will run, a sequence number will change.
read num < /tmp/sequence_number.txt
num=$((num + 1))
if (( num == 1000 )); then

#Finally, we can start assembling the data. 
#$senduser goes to %s, $num goes to first %03d and $temp goes to 
#the last %03d in the string. 
#printf -v t1 "%s>APN001,TCPIP*:T#%03d,%03d,000,000,000,000,00000000" "$senduser" "$num" "$temp"
printf -v t1 "%s>APN001,TCPIP*:T#%03d,%03d,000,000,000,000,$bits" "$user" "$num" "$temp"

#Define telemetry parameters
t2="$user>APN001,TCPIP*::$senduser :PARM.Shack Temp,,,,,Big power,Small power"

#Define telemetry units
t3="$user>APN001,TCPIP*::$senduser :UNIT.Deg.F,,,,on,on"

#Original author's temperature/coefficient setting
#Add telemetry coefficient so the APRS protocol can convert your raw values
#into real value.
#We get the value in 3 whole numbers and we need to define coefficient so 
#the APRS protocol
#will know how to display the value. We add 0.1 to the second field, means
#if the value is 452, the temperature will be displayed as 45.2
#t4="$user>APN001,TCPIP*::$senduser :EQNS.0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0"

#I use the actual temperature returned, since it's already normalized to an integer
t4="$user>APN001,TCPIP*::$senduser :EQNS.0,1,0,0,0,0,0,0,0,0,0,0,0,0,0"

#Send bits and project comment. Original author didn't generate bits, mine does
#t5="$user>APN001,TCPIP*::$senduser :BITS.00000000,$projectcomment"
t5="$user>APN001,TCPIP*::$senduser :BITS.$bits,$projectcomment"

#Another tricky part is, $senduser total lenght has to be 9 characters. 
#For example
#If my $senduser=S55MA-10 means it's only 8 characters long and we need 
#to add 1 space to it. S55MA-10 :PARM
#If my $senduser=S55MA means it's only 5 characters long and we need to 
#add 4 spaces to it S55MA    :PARM
#The same goes for UNIT, EQNS and BITS
#Make sure to set $senduser to be 8 characters, in the beginning of this script

#Send data to the server
#For telemetry to work we need to have an object before, from previous script.
#We'll only send an object and telemetry non value data every hour so we don't 
#spam the network.
#We need to compare dates to see if the timeout has past.

#Check if file exist
if [ ! -f "/tmp/date.txt" ]; then
   echo 0 > /tmp/date.txt

#calculate time difference
read olddate < /tmp/date.txt
date="$(date +%s)"
diff="$(echo "$date - $olddate" | bc)"

echo debug
echo $t1

#If $updateT is past, execute the first command with headers, else just status
#if [ "$diff" -gt 3600 ]; then
if [ "$diff" -gt "$updateT" ]; then
   printf "%s\n" "$aprsauth" "$data" | ncat -v --send-only $server $port #this is your QTH object from the first script
   printf "%s\n" "$aprsauth" "$t1" "$t2" "$t3" "$t4" "$t5" | ncat -v --send-only $server $port 
   printf "%s\n" "$aprsauth" "$data" | cat
   printf "%s\n" "$aprsauth" "$t1" "$t2" "$t3" "$t4" "$t5" | cat
   echo "$date" > /tmp/date.txt
   printf "%s\n" "$aprsauth" "$t1" | ncat -v --send-only $server $port
   printf "%s\n" "$aprsauth" "$t1" | cat

#Write the last sequence number.
echo "$num" > /tmp/sequence_number.txt

© 2020 Ken Wallich 2020March5


Popular Posts