APRS microModem

posted Feb 14, 2017, 11:27 AM by Northeast Georgia Arc   [ updated Mar 16, 2018, 10:27 AM ]

Gary Griffin - WT4Y

I was looking for a home for some recently purchased Arduino Nanos when I came across a project for an APRS Modem created by Mark Qvist in Denmark ( . Mark's design is built around just about any flavor of Arduino. In addition to a schematic, Mark provides the firmware for a KISS mode modem. The hardware is super simple to build, but you have to tweak the design to interface with your particular radio. The hardware is super simple to build, but you have to tweak the design to interface with your particular radio, especially the PTT circuit. Coming from the Arduino, the PTT signal is a HIGH so I had to add a transistor to reverse it to a LOW required by my Yaesu FT-2500M 2 meter transceiver.

R1 - 1k, R2 - 2.2k, R3 - 3.9k, R4 8.2k, R5 - 270, R6 - 100k, R7 - 1k, R8 - 270, R9, R10 - 100k, R11 - 10k, R12 - 470
C1 - 100nf, C2 - 4.7uf, C3 - 1mf non-polarized (I used a .1mf, worked just fine)
Q1 - generic NPN transistor (2n2222 etc.)
D1 - Red LED (indicates transmit out)
D2 - Green LED (indicates received data)

Here's the completed hardware, the APRSmicroModem on the left, a Raspberry Pi in the center and a 5 volt power supply on the right.  Note the external USB WiFi required because the Pi's internal WiFi  doesn't work so good inside an aluminum box. I had problems with a too low voltage on the Arduino because the power was coming from the USB connection. The ribbon cable connects 5 volts and ground in parallel from the power supply to the Raspberry Pi and the Arduino.

Now you have to flash the firmware for the Arduino to turn it into a KISS mode modem. To do this, I used a Linux program called "avrdude". Since I intended to interface the modem with a Raspberry Pi, I used the Pi to flash the image.

Initially I used a full Raspian with desktop image so I could run a GUI based APRS client called Xastir. I found Xastir to be exceeding slow, so I downloaded Raspian Lite (no desktop) and installed APRX, a command line client. More about that later.

To install avrdude, from a Terminal window on the Pi, run sudo apt-get install avrdude
Download the pre-compiled firmware image from Mark's repository.
sudo wget
Plug the Arduino into a Raspberry Pi USB port. Now you need to find out what USB port is connected to the Arduino.
The command dmesg | grep ttyUSB* will return the port number you will need for flashing the firmware. In my case, the command returned ttyUSB0.
From the same directory where the firmware image was downloaded, issue the following command to flash the firmware:
avrdude -p m328p -c arduino -P /dev/ttyUSB0 -b 57600 -F -U flash:w:microaprs-5v-kiss-latest.hex
Note: baud rate for Nano is 57600, I think Uno is 115200. If the flash was successful, you will no longer have an Arduino. You will now have an AVR microprocessor with an APRS KISS mode modem firmware. If you want your Arduino back, you will need to find the boot loader for your Arduino on the Internet and flash the image back.

Note: If you encounter errors flashing the Nano, see this page:

Now it's time to install the APRS client on the Raspberry Pi. If you want the GUI client Xastir, you will need a Raspberry Pi running the desktop version of Raspian. 

The following directions are for APRX, a command line only client. 
I run commands as root (not recommended) so if you get errors, precede the commands with sudo.
Check for latest version at: When these instructions were created the latest version is 2.9.0.
This will download it:
Now install it:
dpkg -i aprx_2.9.0_raspi.deb

Edit the file /etc/default/aprx so the client starts when the Pi is restarted:

nano /etc/default/aprx
Change the file so that the configuration reads: STARTAPRX="yes" and DAEMON_OPTS=""
Save the file and issue the following commands:
update-rc.d aprx defaults 84
/etc/init.d/aprx start

The manual you will need to read and understand to configure the client is at:
The configuration file is /etc/aprx.conf
Here is my configuration for a Tx/Rx iGate. Read the manual if you want something else like a Rx only iGate. You must change the values highlighted in yellow. See the notes at the end.

mycall  YourCallSign

myloc lat xxxx.xxN lon xxxxx.xxW

   passcode           xxxx
   heartbeat-timeout  1m

   pidfile            /var/run/
   rflog              /var/log/aprx/aprx-rf.log
   aprxlog            /var/log/aprx/aprx.log
   erlangfile         /var/run/aprx.state

   serial-device /dev/ttyUSB0  9600 8n1    KISS
   tx-ok        true

     transmitter $mycall
          source $mycall

   beaconmode   both
   cycle-size   15m
   beacon       symbol "I&" $myloc comment "PHG70966/ RxTx iGate Nicholson, GA"

  • Obviously, use your call sign.
  • To understand how APRS reads the latitude and longitude, see the following: Make sure the value for longitude is 5 digits before the decimal point, even if you have to precede it with a zero. 
  • If you don't know your APRS passcode, go here and generate yours:
  • You can leave the server value at if you live in North America.
  • Make sure the correct USB port is listed for serial-device.
  • The beacon you transmit can be anything you want. The optional "PHG" (Power Height Gain) code will advertise the characteristics of your antenna. To calculate your very own PHG, go to: Unless you live in Nicholson, GA, you should change this also.
The red LED will turn on when transmitting and the Green LED will turn on as the modem decodes received packets. Make sure the volume control is turned up high enough, but not too high that you over-drive the modem. After a few seconds of operation, you should be able to find yourself on the map at: You may have to search for your call sign.

Note: If you really really want to run a Raspberry Pi desktop with a GUI APRS client, here are some brief instructions:
Get the latest Raspian image at:
You will want Raspian with Desktop
Download and unzip the file.
Write the image to your SD card using Win32 Disk Imager
A very nice fellow has written a script to run about 100 commands that install Xastir. To get it, from a terminal windows run:
sudo wget then run it with the command: ./
(run as the Pi user, not root) Good luck.

Arduino Controlled Fox

posted Jan 16, 2017, 8:12 AM by Northeast Georgia Arc   [ updated Jan 25, 2018, 8:53 AM ]

Gary Griffin - WT4Y
View the notes for the presentation HERE.

In the January 2017 QST "Letters From Our Members" section, there was a story about a Ham supported charity event that was being disrupted by a stuck microphone tying up three linked repeaters. Using skills acquired through Fox Hunting, a Ham was able to find the stuck microphone in a nearby vehicle.

That story inspired me to create an Arduino controlled Fox Hunting transmitter. The Fox consists of an Arduino Uno, an old Icom HT, a battery pack and a one gallon re-purposed alcohol can. Just about any type of Arduino could be used including the Arduino Nano, but I had a Uno on hand.

  • Transmits a patriotic tune followed by a CW identification message
  • Remotely programmable pause between transmissions (5, 10, 15 or 20 seconds)
  • Remote controlled start and stop
Some photos of the project:

Update: I ordered an Arduino Nano and managed to pack it and the associated circuitry into an Altoids can. It makes for a neater package, don't you think?

Update 2: I found a DTMF Decoder Library for Arduino, so I added circuitry to connect to the HT speaker. Now I can remotely turn transmissions on or off using touch tones from another rig. See the updated schematic and sketch.

Because the Arduino puts out a Square Wave, I made a 3 stage R-C filter to turn it into something like a Sine Wave. The resulting tone sounds a bit smoother than the harsh Square Wave. The Icom IC-2SAT HT uses a single pin for both microphone input and PTT, so in this case, I used a digital pin on the Arduino for PTT and when the pin is Low, it pulls a 2.2K resistor to Ground. I fed the audio from the R-C filter to the same pin through a .2 micro-farad capacitor. Every type of HT uses a different scheme for PTT and microphone input, so do some research for your particular type of radio.

R1-3 = 3.3k
R4 = 47k
R5 = 2.25
C1-4 = 200 nf
R6 = 1k
R7-8 = 100k
C5 = 100 nf

The six 1.2 volt NiMH batteries in the battery pack provide a nominal 7.2 volts, about 8.4 volts when the batteries are fresh. The battery pack powers the Arduino and HT. Initial testing shows that my old batteries will power the Fox for over 2 hours transmitting an intermittent beacon at 1/2 watt.

Note: If your power supply is over 9 volts, you might want to use the Arduino Uno instead of the Nano. The on-board regulator on some of the Nano clones will not handle voltages over about 9v. Check the specs on the Nano you purchase just to be certain.

I cobbled together some code from various places on the Internet and created the following Arduino Sketch.

You can download the sketch here: Fox.ino
You'll also need the associated header file: pitches.h, DTMF.h and DTMF.cpp.
Copy pitches.h to the same folder as the main sketch. Copy DTMF.h and DTMF.cpp to a folder called "DTMF" in the Arduino Libraries folder.

Here's the sketch.
Can you guess the patriotic melody?

// Arduino controlled Fox Hunting Xmtr and / or Beacon 
// Code cobbled together from various sources
// Pin 6 is the square wave output for the Morse Code
// Pin 7 is Push to Talk pin (low = PTT)
// A0 is the Analog Input for DTMF detection

#include <DTMF.h>
#include "pitches.h"

int sensorPin = A0;  //The following block used by DTMF
float n = 128.0;
float sampling_rate = 8926.0;
DTMF dtmf = DTMF(n, sampling_rate);
float d_mags[8];
char thischar;

int xmit = 0;  // if xmit = 0, don't transmit
int pause = 220;  // pause between transmissions, 22 = 1 second (approx)

// notes in the melody:
int melody[] = {
// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  8, 8, 4, 4, 8, 8, 8, 8, 4, 4, 4, 4

#define PTT  7
#define BUZZER_PIN 6  // send music and cw out this pin
#define BUZZER_FREQUENCY 700  // cw pitch
char beaconstring[] = "CQ WT4Y/FOX";
char idstring[] = "DE WT4Y";
int randNumber;

char chartemp[4] ;
char incomingByte = 0;

int interval = 60;   //length of time for one dot - basic unit of measurement;  one interval occurs between each dot or dash; dash is equivalent to 3 dots; 7 dots between words

String alphabet = "abcdefghijklmnopqrstuvwxyz1234567890/ ";
String ALPHABET = alphabet;

String morseCode[] = { 
    ".-", // A
    "-...", // B
    "-.-.", // C
    "-..", // D
    ".", // E
    "..-.", // F
    "--.", // G
    "....", // H
    "..", // I
    ".---", // J
    "-.-", // K
    ".-..", // L
    "--", // M
    "-.", // N
    "---",  // O
    ".--.", // P
    "--.-", // Q
    ".-.", // R
    "...",  // S
    "-", // T
    "..-", // U
    "...-", // V
    ".--", // W
    "-..-", // X
    "-.--", // Y
    "--..", // Z
    ".----", // 1
    "..---", // 2
    "...--", // 3
    "....-", // 4
    ".....", // 5
    "-....", // 6
    "--...", // 7
    "---..", // 8
    "----.", // 9
    "-----", // 0
    "-..-.", // forward slash
    " "   //space character

void setup() {
  pinMode(PTT, OUTPUT);
  digitalWrite(PTT, HIGH);
//  pinMode(morseOutput, OUTPUT);
  randomSeed(analogRead(0));  // in case random delays between 
                              // transmissions are used
  digitalWrite(PTT, LOW);
  digitalWrite(PTT, HIGH);
  xmit = 0;

void loop() {

//  randNumber = random(110, 440);      // remove comments to use random delays
//  pause = randNumber;                 // example is 5 to 20 second pause

for (int x = 0; x < pause; x ++)  // while waiting between transmissions, look for DTMF
//  Serial.println(x);
  dtmf.detect(d_mags, 506);
  thischar = dtmf.button(d_mags, 1800.);
  if (thischar) {  // decide what to do if DTMF tone is received
    switch (thischar) {
      case 49:  // the number 1
        xmit = 1;  // set the flag to enable transmissions
      case 50:  //number 2
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        xmit = 0;
      case 51:  //number 3
        digitalWrite(PTT, LOW);
        for (int i = 0; i < sizeof(idstring); i++){
        digitalWrite(PTT, HIGH);
        xmit = 0;
      case 52:  //number 4
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        xmit = 0;
      case 53:  //number 5
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        xmit = 0;
      case 54:  //number 6
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        pause = 110;  // 5 second pause
     case 55:  //number 7
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        pause = 220;  // 10 second pause
     case 56:  //number 8
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        pause = 330;  // 15 second pause
     case 57:  //number 9
        digitalWrite(PTT, LOW);
        digitalWrite(PTT, HIGH);
        pause = 440;  // 20 second pause
      default:  // any other number, turn off transmissions
        xmit = 0;  // set the flag to disable transmissions

if (xmit == 1)
  digitalWrite(PTT, LOW);
  delay(2000); // delay 2 seconds after PTT to account for race condition

// play a little melody
  for (int thisNote = 0; thisNote < 12; thisNote++) {
    // to calculate the note duration, take one second
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(BUZZER_PIN, melody[thisNote], noteDuration);
    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    // stop the tone playing:

  delay(1000);  //one second between melody and beaconstring

  for (int i = 0; i < sizeof(beaconstring); i++){

  digitalWrite(PTT, HIGH);  // drop the PTT and wait a while


// End of Loop()
// Functions follow

void sendLetter (char c) {
   int i;
   for (i = 0; i < alphabet.length(); i = i + 1) {
       if (alphabet[i] == c || ALPHABET[i] == c) {
   if (c == '\n')

void sendMorseCode (String tokens) {
   int i;
   for (i = 0; i < tokens.length(); i = i + 1) {
       switch (tokens[i]) {
           case '-':
           case '.':
           case ' ':
//   Serial.print(" ");

void sendEndOfWord() {
//   Serial.print("  ");

//basic functions - Morse code concepts  
void sendDot() {
//   Serial.print(".");
void sendDash() {
//   Serial.print("-");

//Low level functions - how the actions are accomplished
// n = number of intervals 
// interval is a fixed length of time determined at start, for example 200 milliseconds
void morseOutputOn (int n) {
//   example: morseOutputOn(1) means turn output on and keep it on for 1 interval 
//            morseOutputOn(3) means turn output on and keep it on for 3 intervals 
//   digitalWrite(morseOutput, HIGH);
   delay(n * interval);

void morseOutputOff (int n) {
//   digitalWrite(morseOutput, LOW);
   delay(n * interval);

79,319 miles per Watt

posted Sep 3, 2016, 12:08 PM by Northeast Georgia Arc   [ updated Jan 21, 2017, 11:16 AM ]

Bobby Goins - KN4I

The subject of the email I sent out said “79,319 miles per Watt on 40 Meters”.

Was that correct?  I ran the numbers again, and actually came up with 79,330 miles per Watt, but who is counting?  (The second calculation used the calculated distance from the QRZ website.  The first used the calculated distance from a British amateur radio website.  I'll go with QRZ's numbers.)

What equipment did I use, what mode did I use, and how was it confirmed?  The last two questions are the easiest to answer, so I will answer them first.

I used WSPR (Weak Signal Propagation Reporter) and confirmed the contact at  (See Table 1.)

Table 1.  My first four contact confirmations from  (Note that I am actually at grid EM84ef, so my actual distance to KK1D, per QRZ, is 1276.6 km, or 793.3 miles.)


What is WSPR?  WSPR is a protocol designed for probing potential paths with low-power transmissions.  The WSPR software was created by Joe Taylor, a professor at Princeton University, who's call sign is K1JT.  WSPR was originally design for the MF and HF bands, and can decode signals with S/N ratios as low as -28 dB in a 2.5kHz bandwidth.   

As a side-note, Joe Taylor is also known for creating the WSJT (Weak Signal Communication, by K1JT) software suite, which is optimized for EME / moonbounce (JT65 and JT4 modes), meteor scatter (JTMS and FSK441 modes), and ionospheric scatter (ISCAT and JT6M modes).

How does WSPR work?  A low-power frequency-shift keying signal (in my case 10 mW), containing the sending station's callsign, maidenhead grid location, and transmitter power (in dBm) is transmitted.  The transmission uses a very slow data rate, and usually takes approximately 110-113 seconds to send this information.  The timing for sending the information is very crucial, since the signal of a transmission that starts a couple of seconds before, or after, the prescribed start time, will not be decoded.  Many users of WSPR sync their computer's time to a time standard, such as NTP.

WSPR does operate on a very specific set of published frequencies, which are:

Band Dial f (MHz)       Trans f (MHz)

160m    1.8366    1.8380 –     1.8382

 80m    3.5926    3.5940 –     3.5942

 60m    5.2872    5.2886 –     5.2888

 40m    7.0386    7.0400 –     7.0402

 30m  10.1387  10.1401 –   10.1403

 20m  14.0956  14.0970 –   14.0972

 17m  18.1046  18.1060 –   18.1062

 15m  21.0946  21.0960 –   21.0962

 12m  24.9246  24.9260 –   24.9262

 10m  28.1246  28.1260 –   28.1262

   6m  50.2930  50.2944 –   50.2946

   2m 144.4885 144.4899 – 144.4901


Okay, this sound good, but what kind of equipment do I need to buy to do WSPR?  Any amateur HF / VHF transmitter that can handle data from a computer can be used.  WSPR software can downloaded at:

In my case, I do not have a high power HF rig, so I was trying to figure out how to use my 1W QRPp Softrock Ensemble RXTX 20/30/40 (Softrock) that I built some time ago with my Raspberry Pi 2B.  The Softrock is a Software Defined Radio (SDR) that uses a computer's software to do some of the functions of a radio (mixers, filters, amplifiers, modulators/demodulators, detectors, etc.) that were traditionally done with hardware.  The Softrock performs the transition from radio frequency to digital signals and visa-versa.

The Raspberry Pi (Rpi) is a credit card-sized single-board computer.  The Rpi runs around $35, or so.  It has everything you might need, except a monitor and keyboard / mouse.  The Rpi 3 has an onboard wifi chip that previous models don't.

Unfortunately, I was not happy with the S/N ratio that I was getting with the Softrock / Rpi  combination, versus my laptop / Rpi combination.  (I think the problem was with the USB soundcard that I was using to translate the analog sound signals to digital, and visa-versa.)  I still wanted to use my Rpi in some sort of ham radio activity.  There had to be something.  Then I found it.  Enter WsprryPi.

While watching YouTube videos about the Rpi and ham radio, I found a video that used the Rpi as a WSPR beacon.  No SDR or HF rig was needed.  The video used a card/shield that piggybacked on to the Rpi.  This card, called QRPi, was created by Zoltan Doczi (HA7DCD in Budapest Hungary) and is sold in the USA by TAPR (Tucson Amateur Packet Radio Corp).  The QRPi shield includes some filtering and signal amplification, but only works on 20 meter, where my antenna is cut for 40 meters.  Also, the QRPi shield sold for around $25 plus shipping, which would be getting close to the $35 I spent for the Rpi.  (Note: I noticed on Sept. 6, that TAPR has sold out of these units.)

Being as frugal as I am, I searched the Internet and found where experimenters noticed that a couple of GPIO (general purpose input – output) pins on the Rpi could be programmed to output a square wave signal in the HF range.  This meant that one only needs the correct software to make the Rpi transmit, and a low-pass filter (LPF) to reduce the signal harmonics.  Since I had built a 40m LPF for the Softrock already, I was only out the cost of the jumpers to go between the Rpi's GPIO pins and the RG-8 cable to my antenna.

The software turned out to be WsprryPi.  (  This software is designed to essentially turn a Rpi into a WSPR beacon.  It runs on Raspbian, which is the operating system of the Rpi.  There are several command line options available to do different things, but what I use is:

pi@raspberrypi:~ $ sudo /home/pi/WsprryPi/wspr -s -r KN4I EM84ef 10 40m

The -s option self-calibrates with the NTP time standard

The -r option repeats the transmission until a ctrl-C is issued to stop it

KN4I is my callsign

EM84ef is my maidenhead grid location

10 is my transmitting power in dBm (10 mW)

40m is the frequency band that I wish to transmit on


As you can see in Table 2, my WSPR transmissions have been received by many WSPR reporting stations, primarily in the Northeast and Northwest.  Because of the nature of beacons, there are a lot of duplicate entries for each reporting station shown.  

Table 2. The WSPR reporting stations that have received my transmissions.  (Note that my wire antenna is strung out at 240°, so the right angle response to it would be at 150° and 330°.)

I have found the WSPR community to be very interested in propagation and how various factors, such as greyline, influences it.  In communicating with Mark - KB9AMG, I mentioned that my antenna was not tuned to the WSPR frequency, and that I wondered how much further out that I could get a signal out with a properly tuned antenna.  He was very interested to find out as well, and actually forwarded the entries in his log of my signal so we could compare it to a future signal with a properly tuned antenna.

Going forward

There are a few things that I need to do going forward to improve my WSPR experience.

First, I need to verify that my five-element Chebyshev LPF is adequate to reduce any spurious emissions from the Rpi to FCC acceptable levels.  (After seeing that my WSPR system worked, I shut it down.)  I am currently trying to run an analysis of my LPF in Qucs (simulation software) to see if the 3rd harmonic, from the Rpi square waveform, is reduced my 43 dB, or more.  If not, I need add another filtering stage (two elements).

Next, I need to tune and better analyze my antenna.  My antenna is a 40m end-fed half-wave antenna fed through a matchbox tuner that I built.  One end of the antenna is fixed to the side of my house, and the other is fixed to a tree.  The ground slopes downward at nearly a right angle from the antenna in a northwesterly direction.  (Maybe that is why I am getting such good reception in the Northwest.)  I will need to model this in Xnec2 (antenna analysis program) to see if I need to tweak the height of the antenna, swing the antenna to another tree, or move it away from the house.

Also, I need to analyze my antenna for 20m and maybe 80m to see if I should be able to get a decent signal out.  If so, I need to build LPFs for these frequencies, and expand my “experiment”.

Lastly, I need to add a 0.1 mF capacitor between my Rpi and the LPF to reduce any DC voltage that might build up on my antenna.  This would help protect the Rpi.


I am glad that I was able to find a ham radio-related use for my Rpi.  I am also glad to be able to learn more about propagation, especially as it relates to beacons.  This is part of Amateur Radio that I knew enough to pass my exam, but did not fully understand.  Hopefully, I can learn more about how signals propagate through my WSPR transmitting station.

If you would like to know more about WSPR, the Softrock Ensemble RXTX, the Raspberry Pi, my matchbox tuner, my antenna, or the Qucs and Xnec2 software, feel free to contact me at

1-3 of 3