Projects‎ > ‎

Arduino Controlled Fox

posted Jan 16, 2017, 8:12 AM by Northeast Georgia Arc   [ updated Feb 2, 2017, 11:45 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.

Features:
  • 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:

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 and sketch.

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


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

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_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 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
  digitalWrite(PTT, LOW);
  delay(2000);
  sendLetter('h');
  sendLetter('i');
  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.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 50:  //number 2
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('h');
        sendLetter('i');
        digitalWrite(PTT, HIGH);
        xmit = 0;
        break;
      case 51:  //number 3
        digitalWrite(PTT, LOW);
        delay(2000);
        for (int i = 0; i < sizeof(idstring); i++){
          sendLetter(idstring[i]);
        }
        digitalWrite(PTT, HIGH);
        xmit = 0;
        break;
      case 52:  //number 4
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('7');
        sendLetter('3');
        digitalWrite(PTT, HIGH);
        xmit = 0;
        break;
      case 53:  //number 5
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('8');
        sendLetter('8');
        digitalWrite(PTT, HIGH);
        xmit = 0;
        break;
      case 54:  //number 6
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('R');
        digitalWrite(PTT, HIGH);
        pause = 110;  // 5 second pause
        break;
     case 55:  //number 7
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('R');
        digitalWrite(PTT, HIGH);
        pause = 220;  // 10 second pause
        break;
     case 56:  //number 8
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('R');
        digitalWrite(PTT, HIGH);
        pause = 330;  // 15 second pause
        break;
     case 57:  //number 9
        digitalWrite(PTT, LOW);
        delay(2000);
        sendLetter('R');
        digitalWrite(PTT, HIGH);
        pause = 440;  // 20 second pause
        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

  }
}

// 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);
}