Send APRS objects or telemetry via Bash

This is to internet only, not to RF. You need TNC software and a radio station to send it via RF.

We need ncat which is a part of nmap and bc.

sudo apt-get install nmap bc

First, some usefull tools:
APRS coordinate converter: http://digined.pe1mew.nl/?How_to:Convert_coordinates
APRS passcode generator: http://apps.magicbug.co.uk/passcode/

There are more APRS IS servers available to connect to, I decided to use poland.aprs2.net, the generic one is rotate.aprs.net.
Default port is 14580.

The protocol goes like this (more info http://www.aprs-is.net/Connecting.aspx):
– Authenticate with APRS-IS server
– Send data

So the data you are sending is:
– user yourHAMSIGN pass passcode
– objectdata

Bash one line command to send data:

printf "%s\n" "user yourHAMSIGN pass passcode" "yourHAMSIGN>APN100,TCPIP*:=latitude/longtitude-comment" | ncat poland.aprs2.net 14580

Example:

printf "%s\n" "user S55MA-13 pass 23558" "S55MA-13>APN100,TCPIP*:=4246.29N/01412.79E_247-QTH" | ncat poland.aprs2.net 14580

This should place S55MA-13 on a map with a house icon.
Look into aprs.fi raw data for your ham sign (https://aprs.fi/?c=raw&call=yourHAMSIGN) to find any formatting errors. It should look like this:

2017-07-27 16:18:32 CEST: S55MA-13>APN100,TCPIP*,qAC,T2POLAND:=4246.29N/01412.79E_247-QTH

Important: With this commands you can place (fake) or “steal” an objects that are not your own stations by placing another station sign in the second yourHAMSIGN field. This will work, but it will look like the object is coming via another station, igated by your station. That’s ok, the object will be placed correctly on the map, but for the sake of following correct protocol procedures, you need to change a string a little bit so the other station object will show correct path of where it’s coming from.

When placing another object (not your own) on the map, you need to add the timestamp to the string. If the object is permanent, timestamp should be “111111z” (http://www.aprs.org/info/object-perm.txt)

Example:

printf "%s\n" "user yourHAMSIGN pass passcode" "yourHAMSIGN>APN100,TCPIP*:;ANOTHERSTATION_CALLSIGN *111111zlatitude/longtitude-comment" | ncat poland.aprs2.net 14580
printf "%s\n" "user S55MA-10 pass 22358" "S55MA-10>APN100,WIDE2-1*:;S55MA-14 *111111z4139.94N/01217.67E-test" | ncat --send-only poland.aprs2.net 14580

Note: ANOTHERSTATION_CALLSIGN is by protocol defined as 9 characters long and you need to add whitespaces between ANOTHERSTATION_CALLSIGN and *111111z
Example: S51Y has four characters only so you need to add 5 whitespaces between ANOTHERSTATION_CALLSIGN and *111111z
The string would look like:

printf "%s\n" "user S55MA-10 pass 22358" "S55MA-10>APN100,WIDE2-1*:;S51Y     *111111z4139.94N/01217.67E-test" | ncat --send-only poland.aprs2.net 14580

If you don’t want to use one line command, you can create a simple script (for placing your own station):

#!/bin/bash

#Define login info
user=yourHAMSIGN
password=passcode

#Define object user info
senduser=yourHAMSIGN

#Define APRS-IS server
server=poland.aprs2.net
port=14580

#Define station location
lat=4146.72N
lon=01213.80E

#Define data
comment="QTH"
data="$senduser>APN100,TCPIP*:=${lat}/${lon}-${comment}"

#Send data to the server
printf "%s\n" "user $user pass $password" "${senduser}>APN100,TCPIP*:=${lat}/${lon}-${comment}" | ncat poland.aprs2.net 14580

NOTE: Don’t send data to often. For objects it’s sufficient to send it every few hours. Don’t spam the network.

You can send telemetry the same way but it’s a little bit trickier because APRS protocol has coeficients for displaying the correct values.
For example let’s say we’ll read a raspberryPI CPU temperature and send it to APRS-IS network.

#!/bin/bash
#Define login info
user=yourHAMSIGN
password=passcode

#Define object user info
senduser=yourHAMSIGN

#Define APRS-IS server
server=poland.aprs2.net
port=14580

#Define station location
lat=4146.72N
lon=01213.80E

#Define data
comment="QTH"
data="$senduser>APN100,TCPIP*:=${lat}/${lon}-${comment}"

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

#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}')"

#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 bellow.
temp="$(echo "$tempfloat * 10" | bc | awk -F. '{print $1}')"

#Project comment
projectcomment=Rpi temperature

#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
fi

#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
 num=0
fi 

#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"

#Define telemetry parameters
t2="$user>APN001,TCPIP*::$senduser :PARM.CPU Temp"

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

#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"

#Send bits and project comment
t5="$user>APN001,TCPIP*::$senduser :BITS.00000000,$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

#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 1 hour is past.

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

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

#If 3600 is past, execute the first command, else other
if [ "$diff" -gt 3600 ]; then
   printf "%s\n" "$aprsauth" "$data" | ncat --send-only $server $port #this is your QTH object from the first script
   printf "%s\n" "$aprsauth" "$t1" "$t2" "$t3" "$t4" "$t5" | ncat --send-only $server $port 
   echo "$date" > /tmp/date.txt
else
   printf "%s\n" "$aprsauth" "$t1" | ncat --send-only $server $port
fi
#Write the last sequence number.
echo "$num" > /tmp/sequence_number.txt

Telemetry should form on aprs.fi, for example: https://aprs.fi/telemetry/a/S55MA-10
Note, formatting in script might be broken due html formatting, here is a raw script: https://pastebin.com/24CaMXLT

13 comments

  1. Zdravo,

    imam eno vprašanje ali ali je možno tudi spremeniti ikono ali je vedno po defaultu hiška ?

    Hvala in lep pozdrav

    1. Zdravo.

      Simbol lahko poljubno menjaš. Tukaj so znaki za simbole http://www.aprs.net/vm/DOS/SYMBOLS.HTM.
      Za hiško je znak -. Znake vstaviš med stringom in commentom: printf “%s\n” “user yourHAMSIGN pass passcode” “yourHAMSIGN>APN100,TCPIP*:;ANOTHERSTATION_CALLSIGN *111111zlatitude/longtitudecomment” | ncat poland.aprs2.net 14580

      Naprimer, če želiš, da je simbol vremenska postaja, spremenis znakec na _ namesto -, torej: printf “%s\n” “user yourHAMSIGN pass passcode” “yourHAMSIGN>APN100,TCPIP*:;ANOTHERSTATION_CALLSIGN *111111zlatitude/longtitude_comment” | ncat poland.aprs2.net 14580

  2. good info. I wanted to know , if I could connect a BME280 sensor to the GPIO pins read the values and send it over APRS-IS.

    I plan to use a RPi-0/W for this project.

    Any Python code for this.

    1. The quickest way would be to output the result from the python script to a file and then read this file with a bash script. Another way is to execute the python script within the bash script into variable. I don’t have a ready script for your case, but it’s not that hard to make it.

  3. Hi,
    I’ve tried your telemetry script on a Raspberry. It works so far but aprs.fi has problems with it.
    I got the following error message:
    2019-01-03 10:03:53 CET: DL8JF-8>APN001,TCPIP*,qAC,T2FFL:=5354.61N/01041.93EYQTH
    2019-01-03 10:03:54 CET: DL8JF-8>APN001,TCPIP*,qAC,T2KA:T#011,293,000,000,000,000,00000000
    2019-01-03 10:03:54 CET: DL8JF-8>APN001,TCPIP*,qAC,T2KA::DL8JF-8 :PARM.CPU [Invalid message packet]
    2019-01-03 10:03:54 CET: DL8JF-8>APN001,TCPIP*,qAC,T2KA::DL8JF-8 :UNIT.Deg.C [Invalid message packet]
    2019-01-03 10:03:54 CET: DL8JF-8>APN001,TCPIP*,qAC,T2KA::DL8JF-8 :EQNS.0,0.1,0,0,0,0,0,0,0,0,0,0,0,0,0 [Invalid message packet]
    2019-01-03 10:03:54 CET: DL8JF-8>APN001,TCPIP*,qAC,T2KA::DL8JF-8 :BITS.00000000,Temperature [Invalid message packet]
    Any ideas ?

    Sven

  4. I played with this for a while trying to get data appearing on APRS-FI.

    I was getting a lot of “Invalid message packet” errors for the various telemetry metadata packets.

    It turns out that the $senduser callsign must be 8 characters long- mine is only five. So I padded my callsign whitespaces like this:
    senduser=”M6UBC ” with three white spaces after it before the final “, and that made aprs.fi happy!

Leave a Reply

Your email address will not be published. Required fields are marked *