Explanation of how the software functions explained below..
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <stdint.h>
#define UNIT_LENGTH 120 //ms for morse speed
#define VL_LOW 189 // low battery indicator level 12:1 dividor
static const int numReadings = 3;
static const struct { const char letter, *code; } MorseMap[] =
{
{ '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', "--.." },
{ ' ', " " }, //Gap between word, seven units
{ '1', ".----" },
{ '2', "..---" },
{ '3', "...--" },
{ '4', "....-" },
{ '5', "....." },
{ '6', "-...." },
{ '7', "--..." },
{ '8', "---.." },
{ '9', "----." },
{ '0', "-----" },
{ '.', "·–·–·–" },
{ ',', "--..--" },
{ '?', "..--.." },
{ '!', "-.-.--" },
{ ':', "---..." },
{ ';', "-.-.-." },
{ '(', "-.--." },
{ ')', "-.--.-" }
};
uint8_t watchdog_counter, mcucr1, mcucr2;
int readings[numReadings]; // the readings from the analog input
int index = 0; // the index of the current reading
int total = 0; // the running total
int average = 0;
ISR(WDT_vect) {
watchdog_counter++; // this is where the watch dog interupt lands up
}
void enable_watchdog(void) {
wdt_reset();
cli();
mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
MCUSR = 0x00;
WDTCR |= _BV(WDCE) | _BV(WDE);
WDTCR = _BV(WDIE) | _BV(WDP2) | _BV(WDP1); //1024ms
sei();
}
void disable_watchdog(void)
{
wdt_reset();
cli();
MCUSR = 0x00;
WDTCR |= _BV(WDCE) | _BV(WDE);
WDTCR = 0x00;
sei();
}
void setup()
{
pinMode(0, OUTPUT); // led morse
pinMode(1, OUTPUT); // audio morse
pinMode(2, OUTPUT); // tx on (high active)
pinMode(A3, INPUT); // analog to read battery voltage
digitalWrite(0, LOW); // ensure off at start
digitalWrite(2, LOW); // ensure off at start
ADCSRA &= ~(1<<ADEN); //Disable ADC, saves ~230uA
analogReference(INTERNAL);
for (int thisReading = 0; thisReading < numReadings; thisReading++)
readings[thisReading] = 0;
enable_watchdog(); //Setup watchdog to go off after 1sec
}
void loop()
{
sleep_cpu(); //Go to sleep!
if(watchdog_counter > 60)
{
disable_watchdog(); // stop WDT (else we could run into ourselves)
watchdog_counter = 0; // reset the timer counter
// check battery voltage
averageVoltage();
int voltage = average;
if(voltage < VL_LOW)
{
sleep_cpu(); //Go to sleep until power on reset
}
String morseWord = encode("PD9JS");
digitalWrite(2, HIGH); // tx on
delay(UNIT_LENGTH * 4); // wait for tx to power up
for (int i = 0; i <= morseWord.length(); i++)
{
switch (morseWord[i])
{
case '.': //dit
digitalWrite(0, HIGH);
TinyTone(239, 5, UNIT_LENGTH);
digitalWrite(0, LOW);
delay(UNIT_LENGTH);
break;
case '-': //dah
digitalWrite(0, HIGH);
TinyTone(239, 5, UNIT_LENGTH *3);
digitalWrite(0, LOW);
delay(UNIT_LENGTH);
break;
case ' ': //gap
delay(UNIT_LENGTH * 2);
}
}
delay(UNIT_LENGTH);
digitalWrite(2, LOW); // tx off
enable_watchdog();
}
}
void TinyTone(unsigned char divisor, unsigned char octave, unsigned long duration)
{
TCCR1 = 0x90 | (11-octave); // for 8MHz clock
OCR1C = divisor-1; // set the OCR
delay(duration);
TCCR1 = 0x90; // stop the counter
}
String encode(const char *string)
{
size_t i, j;
String morseWord = "";
for (i = 0; string[i]; ++i)
{
for (j = 0; j < sizeof MorseMap / sizeof *MorseMap; ++j)
{
if (toupper(string[i]) == MorseMap[j].letter)
{
morseWord += MorseMap[j].code;
break;
}
}
morseWord += " "; //Add tailing space to seperate the chars
}
return morseWord;
}
void averageVoltage()
{
total= total - readings[index];
readings[index] = analogRead(A3);
total= total + readings[index];
index = index + 1;
if (index >= numReadings)
index = 0;
average = total / numReadings;
delay(1); // delay in between reads for stability
}