Arduino Fox Controller

26 January 2017

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.

Some features:
  • Transmissions can be remotely started and stopped with another HT sending DTMF tones (Touch Tones). Because the DTMF decoder is all software, you have to wait between transmissions to send commands. Requiring no extra components is a plus though.
  • Final project uses an Arduino Nano costing about $4.
  • Only a few resistors and capacitors are required to interface the HT.
  • Only one power supply required. If your power supply is over 9 volts, use an Arduino Uno because the voltage regulator on the Uno can handle voltages a bit higher than 12 volts. You could optionally build a "pre-regulator" for the Nano.
  • The Arduino can be programmed to send musical notes in addition to Morse Code. The following sketch includes the first few notes of a song revered by all true Southern Hams.
Here's a little demonstration of the Fox Transmitter:

Some photos of the project:

https://sites.google.com/site/ne4gaweb/projects/arduinocontrolledfox/FoxGuts.jpg?attredirects=0



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.

http://www.wt4y.com/Altoids_open.jpg

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.

https://sites.google.com/site/ne4gaweb/projects/arduinocontrolledfox/ArduinoFox.jpg?attredirects=0

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

Adjust the volume on the HT so that it's sufficient for the Arduino to decode the DTMF tones, not not so much that it over drives the Analog input. An oscilloscope is handy for this, but a little experimentation yield the same result. With my Icom HT, the volume is at approximately one third.

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.

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.hDTMF.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.

Can you guess the 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)

#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_G4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_G4, NOTE_G4, NOTE_E4
};
// 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 morseOutput = 4; //Pin for receiving Morse output - attach LED to this pin
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);
  Serial.begin(9600);
//  pinMode(morseOutput, OUTPUT);
  
  ALPHABET.toUpperCase();
  randomSeed(analogRead(0));  // in case random delays between 
                              // transmissions are used
}

void loop() {

for (int x = 0; x < pause; x ++)  // while waiting between transmissions, look for DTMF
{
//  Serial.println(x);
  dtmf.sample(sensorPin);
  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
        break;
      case 53:  //number 5
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('h');
        sendLetter('i');
        digitalWrite(PTT, HIGH);
        delay(2000);
        break;
      case 54:  //number 6
        digitalWrite(PTT, LOW);
        delay(2000);
        for (int i = 0; i < sizeof(idstring); i++){
          sendLetter(idstring[i]);
        }
        digitalWrite(PTT, HIGH);
        delay(2000);
        break;
      case 55:  //number 7
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('7');
        sendLetter('3');
        digitalWrite(PTT, HIGH);
        delay(2000);
        break;
      default:  // any other number, turn off transmissions
        xmit = 0;  // set the flag to disable transmissions
      break;
    }
  }
  delay(1);
}

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;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(BUZZER_PIN);
  }

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

  for (int i = 0; i < sizeof(beaconstring); i++){
    sendLetter(beaconstring[i]);
    }

  delay(500);
  digitalWrite(PTT, HIGH);  // drop the PTT and wait a while
//  randNumber = random(10000, 20000);  // remove comments and add comments to delay(5000)
//  Serial.print(randNumber);           // to use random delays
//  Serial.println();
//  delay(randNumber);
//  delay(5000);
  }
}

// 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) {
           //Serial.print(c);
           sendMorseCode(morseCode[i]);
           return;
       }
   }
   if (c == '\n')
      Serial.println();
}

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

void sendEndOfWord() {
   morseOutputOff(4);
//   Serial.print("  ");
}

//basic functions - Morse code concepts  
void sendDot() {
   tone(BUZZER_PIN, BUZZER_FREQUENCY);
   morseOutputOn(1);
   noTone(BUZZER_PIN);
   morseOutputOff(1);
//   Serial.print(".");
}
void sendDash() {
   tone(BUZZER_PIN, BUZZER_FREQUENCY);
   morseOutputOn(3);
   noTone(BUZZER_PIN);
   morseOutputOff(1);
//   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);
}