1676 wiersze
40 KiB
C++
1676 wiersze
40 KiB
C++
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2020 DigitalConfections
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
/*
|
|
* Microfox Arduino nano version. Converted from PIC C November 2019
|
|
* Jerry Boyd WB8WFK
|
|
* This controller will replace the Albuquerque VHF and HF microfox pic based controller
|
|
*
|
|
* */
|
|
|
|
|
|
#include "defs.h"
|
|
#include "linkbus.h"
|
|
#include "morse.h"
|
|
#include <avr/wdt.h>
|
|
|
|
#if COMPILE_FOR_ATMELSTUDIO7
|
|
#include <avr/io.h>
|
|
#include <avr/eeprom.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include "delay.h"
|
|
#include "ardooweeno.h"
|
|
#endif /* COMPILE_FOR_ATMELSTUDIO7 */
|
|
|
|
#define MAX_PATTERN_TEXT_LENGTH 20
|
|
#define TEMP_STRING_LENGTH (MAX_PATTERN_TEXT_LENGTH + 10)
|
|
|
|
volatile int32_t g_seconds_since_sync = 0; /* Total elapsed time counter */
|
|
volatile FoxType g_fox = BEACON; /* Sets Fox number not set by ISR. Set in startup and checked in main. */
|
|
volatile int g_active = 0; /* Disable active. set and clear in ISR. Checked in main. */
|
|
|
|
volatile int g_on_the_air = 0; /* Controls transmitter Morse activity */
|
|
volatile int g_code_throttle = 0; /* Adjusts Morse code speed */
|
|
const char g_morsePatterns[][6] = { "MO ", "MOE ", "MOI ", "MOS ", "MOH ", "MO5 ", "", "5", "S", "ME", "MI", "MS", "MH", "M5", "OE", "OI", "OS", "OH", "O5" };
|
|
volatile BOOL g_callsign_sent = FALSE;
|
|
|
|
volatile BOOL g_blinky_time = FALSE;
|
|
|
|
volatile int g_on_air_interval = 0;
|
|
volatile int g_fox_seconds_into_interval = 0;
|
|
volatile int g_fox_counter = 1;
|
|
volatile int g_number_of_foxes = 0;
|
|
volatile BOOL g_fox_transition = FALSE;
|
|
volatile int g_fox_id_offset = 0;
|
|
volatile int g_id_interval = 0;
|
|
volatile BOOL g_time_to_ID = FALSE;
|
|
volatile int g_startclock_interval = 60;
|
|
volatile int g_fox_tone_offset = 1;
|
|
|
|
volatile BOOL g_audio_tone_state = FALSE;
|
|
volatile uint8_t g_lastSeconds = 0x00;
|
|
volatile int16_t g_sync_pin_timer = 0;
|
|
volatile BOOL g_sync_pin_stable = FALSE;
|
|
volatile BOOL g_sync_enabled = TRUE;
|
|
|
|
#if !COMPILE_FOR_ATMELSTUDIO7
|
|
FoxType operator++ (volatile FoxType &orig, int)
|
|
{
|
|
orig = static_cast < FoxType > (orig + 1); /* static_cast required because enum + int -> int */
|
|
if(orig > INVALID_FOX)
|
|
{
|
|
orig = INVALID_FOX;
|
|
}
|
|
return( orig);
|
|
}
|
|
|
|
FoxType operator += (volatile FoxType &a, int b)
|
|
{
|
|
a = static_cast < FoxType > (a + b); /* static_cast required because enum + int -> int */
|
|
return( a);
|
|
}
|
|
#endif /* COMPILE_FOR_ATMELSTUDIO7 */
|
|
|
|
|
|
/***********************************************************************
|
|
* Global Variables & String Constants
|
|
*
|
|
* Identify each global with a "g_" prefix
|
|
* Whenever possible limit globals' scope to this file using "static"
|
|
* Use "volatile" for globals shared between ISRs and foreground
|
|
************************************************************************/
|
|
static uint8_t EEMEM ee_interface_eeprom_initialization_flag = EEPROM_UNINITIALIZED;
|
|
static char EEMEM ee_stationID_text[MAX_PATTERN_TEXT_LENGTH + 1];
|
|
static uint8_t EEMEM ee_id_codespeed;
|
|
static uint16_t EEMEM ee_clock_calibration;
|
|
static uint8_t EEMEM ee_override_DIP_switches;
|
|
static uint8_t EEMEM ee_enable_LEDs;
|
|
static int16_t EEMEM ee_temp_calibration;
|
|
static uint8_t EEMEM ee_enable_start_timer;
|
|
static uint8_t EEMEM ee_enable_transmitter;
|
|
|
|
static char g_messages_text[2][MAX_PATTERN_TEXT_LENGTH + 1] = { "\0", "\0" };
|
|
static volatile uint8_t g_id_codespeed = EEPROM_ID_CODE_SPEED_DEFAULT;
|
|
static volatile uint8_t g_pattern_codespeed = EEPROM_PATTERN_CODE_SPEED_DEFAULT;
|
|
static volatile uint16_t g_time_needed_for_ID = 0;
|
|
static volatile uint16_t g_clock_calibration = EEPROM_CLOCK_CALIBRATION_DEFAULT;
|
|
static volatile int16_t g_temp_calibration = EEPROM_TEMP_CALIBRATION_DEFAULT;
|
|
static volatile uint8_t g_override_DIP_switches = EEPROM_OVERRIDE_DIP_SW_DEFAULT;
|
|
static volatile uint8_t g_enable_LEDs;
|
|
static volatile uint8_t g_enable_start_timer;
|
|
static volatile uint8_t g_enable_transmitter;
|
|
|
|
static char g_tempStr[TEMP_STRING_LENGTH] = { '\0' };
|
|
static volatile uint8_t g_LEDs_Timed_Out = FALSE;
|
|
/*
|
|
* Function Prototypes
|
|
*/
|
|
void handleLinkBusMsgs(void);
|
|
void initializeEEPROMVars(BOOL resetAll);
|
|
float getTemp(void);
|
|
uint16_t readADC();
|
|
void setUpTemp(void);
|
|
void sendMorseTone(BOOL onOff);
|
|
void playStartingTone(uint8_t toneFreq);
|
|
void wdt_set(WDReset resetType);
|
|
void doSynchronization(void);
|
|
void setupForFox(void);
|
|
void softwareReset(void);
|
|
|
|
#if USE_WDT_RESET
|
|
uint8_t mcusr_copy __attribute__ ((section (".noinit")));
|
|
void disable_wdt(void) \
|
|
__attribute__((naked)) \
|
|
__attribute__((section(".init3")));
|
|
void disable_wdt(void) {
|
|
mcusr_copy = MCUSR;
|
|
MCUSR = 0x00;
|
|
wdt_disable();
|
|
}
|
|
#endif // USE_WDT_RESET
|
|
|
|
|
|
#if COMPILE_FOR_ATMELSTUDIO7
|
|
void loop(void);
|
|
int main(void)
|
|
#else
|
|
void setup()
|
|
#endif /* COMPILE_FOR_ATMELSTUDIO7 */
|
|
{
|
|
initializeEEPROMVars(FALSE);
|
|
setUpTemp();
|
|
|
|
cli(); /*stop interrupts for setup */
|
|
|
|
/* set pins as outputs */
|
|
pinMode(PIN_LED, OUTPUT); /* The amber LED: This led blinks when off cycle and blinks with code when on cycle. */
|
|
digitalWrite(PIN_LED, OFF);
|
|
pinMode(PIN_MORSE_KEY, OUTPUT); /* This pin is used to control the KEY line to the transmitter only active on cycle. */
|
|
digitalWrite(PIN_MORSE_KEY, OFF);
|
|
pinMode(PIN_AUDIO_OUT, OUTPUT);
|
|
digitalWrite(PIN_AUDIO_OUT, OFF);
|
|
|
|
/* Set unused pins as outputs pulled high */
|
|
pinMode(PIN_UNUSED_7, INPUT_PULLUP);
|
|
pinMode(PIN_UNUSED_8, INPUT_PULLUP);
|
|
pinMode(PIN_UNUSED_10, INPUT_PULLUP);
|
|
pinMode(PIN_UNUSED_12, INPUT_PULLUP);
|
|
pinMode(A0, INPUT_PULLUP);
|
|
pinMode(A1, INPUT_PULLUP);
|
|
pinMode(A2, INPUT_PULLUP);
|
|
pinMode(A3, INPUT_PULLUP);
|
|
#if !CAL_SIGNAL_ON_PD3
|
|
pinMode(PIN_CAL_OUT, INPUT_PULLUP);
|
|
#endif
|
|
|
|
/* set timer1 interrupt at 1Hz */
|
|
TCCR1A = 0; /* set entire TCCR1A register to 0 */
|
|
TCCR1B = 0; /* same for TCCR1B */
|
|
TCNT1 = 0; /*initialize counter value to 0 */
|
|
|
|
/* Set compare match register for 1hz increments
|
|
************************************************************
|
|
** USE THIS TO CALIBRATE FOR BOARD PROCESSOR CLOCK ERROR
|
|
************************************************************/
|
|
/* first testing found bad drift relative to a gps stable clock (iphone timer) is was 10 seconds in about 50 minutes
|
|
* Today I measured the Arduino nano 16 Mhz clock and it was 16.050 MHz. Yes 50 Khz high!!
|
|
* OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536) //was 15624 for 16.000 MHz on center frequency clock
|
|
* OCR1A = 15672;// = (16.043*10^6) / (1*1024) - 1 (must be <65536) //15672 computed for + 16.050 MHz off frequency clock board 4
|
|
* OCR1A = 15661;// = (16.0386*10^6) / (1*1024) - 1 (must be <65536) //15661 computed for + 16.0386 MHz off frequency clock board 3 */
|
|
/* OCR1A = 15629;/ * = (16.006*10^6) / (1*1024) - 1 (must be <65536) //15629 computed for + 16.006 MHz off frequency clock board 3 * / */
|
|
OCR1A = g_clock_calibration;
|
|
|
|
/* turn on CTC mode */
|
|
TCCR1B |= (1 << WGM12);
|
|
/* Set CS11 bit for 1024 prescaler */
|
|
TCCR1B |= (1 << CS12) | (1 << CS10);
|
|
/* enable timer compare interrupt */
|
|
TIMSK1 |= (1 << OCIE1A);
|
|
|
|
/**
|
|
* TIMER2 is for periodic interrupts to drive Morse code generation */
|
|
/* Reset control registers */
|
|
TCCR2A = 0;
|
|
TCCR2B = 0;
|
|
TCCR2A |= (1 << WGM21); /* set Clear Timer on Compare Match (CTC) mode with OCR2A setting the top */
|
|
#if CAL_SIGNAL_ON_PD3
|
|
pinMode(PIN_CAL_OUT, OUTPUT); /* 601Hz Calibration Signal */
|
|
TCCR2A |= (1 << COM2A0); /* Toggle OC2A (PB3) on compare match */
|
|
#endif /* CAL_SIGNAL_ON_PD3 */
|
|
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); /* 1024 Prescaler */
|
|
|
|
OCR2A = 0x0C; /* set frequency to ~300 Hz (0x0c) */
|
|
OCR2B = 0x00;
|
|
/* Use system clock for Timer/Counter2 */
|
|
ASSR &= ~(1 << AS2);
|
|
/* Reset Timer/Counter2 Interrupt Mask Register */
|
|
TIMSK2 = 0;
|
|
TIMSK2 |= (1 << OCIE2B); /* Output Compare Match B Interrupt Enable */
|
|
|
|
/* Timer 0 is for audio Start tone generation and control
|
|
* Note: Do not use millis() or DELAY() after TIMER0 has been reconfigured here! */
|
|
TCCR0A = 0x00;
|
|
TCCR0A |= (1 << WGM01); /* Set CTC mode */
|
|
TCCR0B = 0x00;
|
|
TCCR0B |= (1 << CS02); /* Prescale 256 */
|
|
OCR0A = DEFAULT_TONE_FREQUENCY;
|
|
TIMSK0 = 0x00;
|
|
TIMSK0 |= (1 << OCIE0A);
|
|
|
|
/* Sync button pin change interrupt */
|
|
PCMSK2 = 0x00;
|
|
PCMSK2 = (1 << PCINT19); /* Enable PCINT19 */
|
|
PCICR = 0x00;
|
|
PCICR = (1 << PCIE2); /* Enable pin change interrupt 2 */
|
|
|
|
sei(); /* Enable interrupts */
|
|
|
|
linkbus_init(BAUD); /* Start the Link Bus serial comms */
|
|
|
|
setupForFox();
|
|
|
|
lb_send_string((char*)"\n\nStored Data:\n", TRUE);
|
|
sprintf(g_tempStr, " ID: %s\n", g_messages_text[STATION_ID]);
|
|
lb_send_string(g_tempStr, TRUE);
|
|
sprintf(g_tempStr, " CAL: %u\n", g_clock_calibration);
|
|
lb_send_string(g_tempStr, TRUE);
|
|
sprintf(g_tempStr, " DIP: %u\n", g_override_DIP_switches);
|
|
lb_send_string(g_tempStr, TRUE);
|
|
sprintf(g_tempStr, " LED: %s\n", g_enable_LEDs ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr, TRUE);
|
|
sprintf(g_tempStr, " STA: %s\n", g_enable_start_timer ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr, TRUE);
|
|
sprintf(g_tempStr, " TXE: %s\n", g_enable_transmitter ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr, TRUE);
|
|
lb_send_NewPrompt();
|
|
|
|
#if COMPILE_FOR_ATMELSTUDIO7
|
|
while(1)
|
|
{
|
|
loop();
|
|
}
|
|
#endif /* COMPILE_FOR_ATMELSTUDIO7 */
|
|
}/*end setup */
|
|
|
|
/***********************************************************************
|
|
* Pin Change Interrupt 2 ISR
|
|
*
|
|
* Handles SYNC pin operation
|
|
*
|
|
************************************************************************/
|
|
ISR(PCINT2_vect)
|
|
{
|
|
BOOL pinVal = digitalRead(PIN_SYNC);
|
|
|
|
if(pinVal) /* Sync is high */
|
|
{
|
|
if(g_sync_pin_stable)
|
|
{
|
|
doSynchronization();
|
|
}
|
|
}
|
|
|
|
g_sync_pin_timer = 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* USART Rx Interrupt ISR
|
|
*
|
|
* This ISR is responsible for reading characters from the USART
|
|
* receive buffer to implement the Linkbus.
|
|
*
|
|
* Message format:
|
|
* $id,f1,f2... fn;
|
|
* where
|
|
* id = Linkbus MessageID
|
|
* fn = variable length fields
|
|
* ; = end of message flag
|
|
*
|
|
************************************************************************/
|
|
ISR(USART_RX_vect)
|
|
{
|
|
static char textBuff[LINKBUS_MAX_COMMANDLINE_LENGTH];
|
|
static LinkbusRxBuffer* buff = NULL;
|
|
static uint8_t charIndex = 0;
|
|
static uint8_t field_index = 0;
|
|
static uint8_t field_len = 0;
|
|
static int msg_ID = 0;
|
|
static BOOL receiving_msg = FALSE;
|
|
uint8_t rx_char;
|
|
|
|
rx_char = UDR0;
|
|
|
|
if(!buff)
|
|
{
|
|
buff = nextEmptyRxBuffer();
|
|
}
|
|
|
|
if(buff)
|
|
{
|
|
static uint8_t ignoreCount = 0;
|
|
rx_char = toupper(rx_char);
|
|
|
|
if(ignoreCount)
|
|
{
|
|
rx_char = '\0';
|
|
ignoreCount--;
|
|
}
|
|
else if(rx_char == 0x1B) /* Ignore ESC sequences */
|
|
{
|
|
rx_char = '\0';
|
|
|
|
if(charIndex < LINKBUS_MAX_MSG_FIELD_LENGTH)
|
|
{
|
|
rx_char = textBuff[charIndex];
|
|
}
|
|
|
|
ignoreCount = 2; /* throw out the next two characters */
|
|
}
|
|
else if(rx_char == 0x0D) /* Handle carriage return */
|
|
{
|
|
if(receiving_msg)
|
|
{
|
|
if(charIndex > 0)
|
|
{
|
|
buff->type = LINKBUS_MSG_QUERY;
|
|
buff->id = (LBMessageID)msg_ID;
|
|
|
|
if(field_index > 0) /* terminate the last field */
|
|
{
|
|
buff->fields[field_index - 1][field_len] = 0;
|
|
}
|
|
|
|
textBuff[charIndex] = '\0'; /* terminate last-message buffer */
|
|
}
|
|
|
|
lb_send_NewLine();
|
|
}
|
|
else
|
|
{
|
|
buff->id = INVALID_MESSAGE; /* print help message */
|
|
}
|
|
|
|
charIndex = 0;
|
|
field_len = 0;
|
|
msg_ID = MESSAGE_EMPTY;
|
|
|
|
field_index = 0;
|
|
buff = NULL;
|
|
|
|
receiving_msg = FALSE;
|
|
}
|
|
else if(rx_char)
|
|
{
|
|
textBuff[charIndex] = rx_char; /* hold the characters for re-use */
|
|
|
|
if(charIndex)
|
|
{
|
|
if(rx_char == 0x7F) /* Handle backspace */
|
|
{
|
|
charIndex--;
|
|
if(field_index == 0)
|
|
{
|
|
msg_ID -= textBuff[charIndex];
|
|
msg_ID /= 10;
|
|
}
|
|
else if(field_len)
|
|
{
|
|
field_len--;
|
|
buff->fields[field_index - 1][field_len] = '\0';
|
|
}
|
|
else if(textBuff[charIndex] == ' ')
|
|
{
|
|
field_index--;
|
|
field_len = strlen(buff->fields[field_index]);
|
|
}
|
|
else
|
|
{
|
|
buff->fields[field_index][0] = '\0';
|
|
field_index--;
|
|
}
|
|
|
|
textBuff[charIndex] = '\0'; /* replace deleted char with null */
|
|
|
|
if(charIndex == 0)
|
|
{
|
|
receiving_msg = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(rx_char == ' ')
|
|
{
|
|
if((textBuff[charIndex - 1] == ' ') || ((field_index + 1) >= LINKBUS_MAX_MSG_NUMBER_OF_FIELDS))
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
else
|
|
{
|
|
if(field_index > 0)
|
|
{
|
|
buff->fields[field_index - 1][field_len] = '\0';
|
|
}
|
|
|
|
field_index++;
|
|
field_len = 0;
|
|
charIndex = MIN(charIndex + 1, (LINKBUS_MAX_COMMANDLINE_LENGTH - 1));
|
|
}
|
|
}
|
|
else if(field_len < LINKBUS_MAX_MSG_FIELD_LENGTH)
|
|
{
|
|
if(field_index == 0) /* message ID received */
|
|
{
|
|
msg_ID = msg_ID * 10 + rx_char;
|
|
field_len++;
|
|
}
|
|
else
|
|
{
|
|
buff->fields[field_index - 1][field_len++] = rx_char;
|
|
buff->fields[field_index - 1][field_len] = '\0';
|
|
}
|
|
|
|
charIndex = MIN(charIndex + 1, (LINKBUS_MAX_COMMANDLINE_LENGTH - 1));
|
|
}
|
|
else
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(rx_char == 0x7F) /* Handle Backspace */
|
|
{
|
|
if(msg_ID <= 0)
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
|
|
msg_ID = 0;
|
|
}
|
|
else if(rx_char == ' ') /* Handle Space */
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
else /* start of new message */
|
|
{
|
|
uint8_t i;
|
|
field_index = 0;
|
|
msg_ID = rx_char;
|
|
|
|
/* Empty the field buffers */
|
|
for(i = 0; i < LINKBUS_MAX_MSG_NUMBER_OF_FIELDS; i++)
|
|
{
|
|
buff->fields[i][0] = '\0';
|
|
}
|
|
|
|
receiving_msg = TRUE;
|
|
charIndex++;
|
|
}
|
|
}
|
|
|
|
if(rx_char)
|
|
{
|
|
lb_echo_char(rx_char);
|
|
}
|
|
}
|
|
}
|
|
} /* End of UART Rx ISR */
|
|
|
|
|
|
/***********************************************************************
|
|
* USART Tx UDRE ISR
|
|
*
|
|
* This ISR is responsible for filling the USART transmit buffer. It
|
|
* implements the transmit function of the Linkbus.
|
|
*
|
|
************************************************************************/
|
|
ISR(USART_UDRE_vect)
|
|
{
|
|
static LinkbusTxBuffer* buff = 0;
|
|
static uint8_t charIndex = 0;
|
|
|
|
if(!buff)
|
|
{
|
|
buff = nextFullTxBuffer();
|
|
}
|
|
|
|
if((*buff)[charIndex])
|
|
{
|
|
/* Put data into buffer, sends the data */
|
|
UDR0 = (*buff)[charIndex++];
|
|
}
|
|
else
|
|
{
|
|
charIndex = 0;
|
|
(*buff)[0] = '\0';
|
|
buff = nextFullTxBuffer();
|
|
if(!buff)
|
|
{
|
|
linkbus_end_tx();
|
|
}
|
|
}
|
|
} /* End of UART Tx ISR */
|
|
|
|
|
|
/***********************************************************************
|
|
* Timer/Counter2 Compare Match B ISR
|
|
*
|
|
* Handles periodic tasks not requiring precise timing.
|
|
************************************************************************/
|
|
ISR( TIMER2_COMPB_vect )
|
|
{
|
|
static uint16_t codeInc = 0;
|
|
static int blink_counter = 100;
|
|
static int blink_count_direction = -1;
|
|
static uint8_t hold_last10sec = 0;
|
|
static int starting_blip = 0;
|
|
static int starting_boop = 0;
|
|
static BOOL playMorse = TRUE;
|
|
BOOL repeat = TRUE, finished = FALSE;
|
|
|
|
if(g_sync_enabled)
|
|
{
|
|
if(digitalRead(PIN_SYNC) == LOW)
|
|
{
|
|
if(g_sync_pin_timer < TIMER2_SECONDS_3)
|
|
{
|
|
g_sync_pin_timer++;
|
|
}
|
|
|
|
if(g_sync_pin_timer > TIMER2_SECONDS_1)
|
|
{
|
|
g_sync_pin_stable = TRUE;
|
|
digitalWrite(PIN_LED, HIGH);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(blink_counter < -BLINK_LONG)
|
|
{
|
|
blink_count_direction = 1;
|
|
}
|
|
|
|
if(blink_counter > BLINK_SHORT)
|
|
{
|
|
blink_count_direction = -1;
|
|
}
|
|
|
|
blink_counter += blink_count_direction;
|
|
|
|
if(blink_counter < 0)
|
|
{
|
|
g_blinky_time = TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_blinky_time = FALSE;
|
|
}
|
|
|
|
if(g_enable_start_timer)
|
|
{
|
|
if(hold_last10sec != g_lastSeconds)
|
|
{
|
|
hold_last10sec = g_lastSeconds;
|
|
|
|
if(hold_last10sec > 0)
|
|
{
|
|
playMorse = FALSE;
|
|
starting_blip = BLINK_SHORT;
|
|
}
|
|
else if(hold_last10sec == 0)
|
|
{
|
|
starting_blip = 0;
|
|
starting_boop = TIMER2_SECONDS_2;
|
|
}
|
|
}
|
|
|
|
if(starting_blip)
|
|
{
|
|
starting_blip--;
|
|
|
|
if(starting_blip)
|
|
{
|
|
if(g_lastSeconds > 5)
|
|
{
|
|
playStartingTone(TONE_500Hz);
|
|
}
|
|
else
|
|
{
|
|
playStartingTone(TONE_600Hz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
playStartingTone(0);
|
|
}
|
|
|
|
}
|
|
else if(starting_boop)
|
|
{
|
|
starting_boop--;
|
|
|
|
if(!starting_boop)
|
|
{
|
|
playStartingTone(0);
|
|
playMorse = TRUE;
|
|
}
|
|
else
|
|
{
|
|
playStartingTone(TONE_400Hz);
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL key = OFF;
|
|
|
|
if(g_on_the_air > 0)
|
|
{
|
|
if(codeInc)
|
|
{
|
|
codeInc--;
|
|
|
|
if(!codeInc)
|
|
{
|
|
key = makeMorse(NULL, &repeat, &finished);
|
|
|
|
if(!repeat && finished) /* ID has completed, so resume pattern */
|
|
{
|
|
key = OFF;
|
|
g_callsign_sent = TRUE;
|
|
if(playMorse)
|
|
{
|
|
sendMorseTone(OFF);
|
|
}
|
|
}
|
|
|
|
if(key)
|
|
{
|
|
if(!g_LEDs_Timed_Out)
|
|
{
|
|
digitalWrite(PIN_LED, HIGH); /* LED */
|
|
}
|
|
|
|
if(g_enable_transmitter)
|
|
{
|
|
digitalWrite(PIN_MORSE_KEY, HIGH); /* TX key line */
|
|
}
|
|
}
|
|
|
|
if(playMorse)
|
|
{
|
|
sendMorseTone(key);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!g_LEDs_Timed_Out && !g_sync_pin_stable)
|
|
{
|
|
digitalWrite(PIN_LED, key); /* LED */
|
|
}
|
|
|
|
if(g_enable_transmitter)
|
|
{
|
|
digitalWrite(PIN_MORSE_KEY, key); /* TX key line */
|
|
}
|
|
|
|
codeInc = g_code_throttle;
|
|
|
|
if(playMorse)
|
|
{
|
|
sendMorseTone(key);
|
|
}
|
|
}
|
|
}
|
|
else if(!g_on_the_air)
|
|
{
|
|
if(key)
|
|
{
|
|
key = OFF;
|
|
if(!g_sync_pin_stable)
|
|
{
|
|
digitalWrite(PIN_LED, OFF); /* LED Off */
|
|
}
|
|
|
|
digitalWrite(PIN_MORSE_KEY, OFF); /* TX key line */
|
|
}
|
|
|
|
if(playMorse)
|
|
{
|
|
sendMorseTone(OFF);
|
|
}
|
|
}
|
|
} /* End of Timer 2 ISR */
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
* Timer/Counter1 Compare Match A ISR
|
|
*
|
|
* Handles once-per-second tasks
|
|
************************************************************************/
|
|
/*
|
|
* here is our main ISR for the ARDF 1-second timer
|
|
* modified from ISR example for microfox by Jerry Boyd WB8WFK
|
|
* this runs once a second and generates the cycle and sets control flags for the main controller.
|
|
*/
|
|
ISR(TIMER1_COMPA_vect) /*timer1 interrupt 1Hz */
|
|
{
|
|
static int id_countdown = 0;
|
|
|
|
if(g_seconds_since_sync == 0) /* sync just occurred */
|
|
{
|
|
id_countdown = g_id_interval;
|
|
g_fox_counter = 1;
|
|
g_lastSeconds = 0;
|
|
}
|
|
|
|
g_seconds_since_sync++; /* Total elapsed time counter */
|
|
g_fox_seconds_into_interval++;
|
|
|
|
if(id_countdown)
|
|
{
|
|
id_countdown--;
|
|
}
|
|
|
|
if(g_number_of_foxes && ((g_seconds_since_sync % g_on_air_interval) == 0))
|
|
{
|
|
g_fox_counter++;
|
|
|
|
if(g_fox_counter > g_number_of_foxes)
|
|
{
|
|
g_fox_counter = 1;
|
|
|
|
if(g_sync_enabled)
|
|
{
|
|
PCMSK2 &= ~(1 << PCINT19); /* Disable PCINT19 */
|
|
PCICR &= ~(1 << PCIE2); /* Disable pin change interrupt 2 */
|
|
pinMode(PIN_SYNC, INPUT);
|
|
pinMode(PIN_SYNC, OUTPUT); /* Set sync pin as output low */
|
|
g_sync_enabled = FALSE;
|
|
}
|
|
|
|
g_LEDs_Timed_Out = TRUE;
|
|
digitalWrite(PIN_LED, OFF);
|
|
}
|
|
g_fox_transition = TRUE;
|
|
g_fox_seconds_into_interval = 0;
|
|
|
|
if(!id_countdown)
|
|
{
|
|
id_countdown = g_id_interval;
|
|
g_time_to_ID = TRUE;
|
|
}
|
|
}
|
|
|
|
if(g_enable_start_timer && ((g_seconds_since_sync + 11) % g_startclock_interval <= 10))
|
|
{
|
|
g_lastSeconds = (uint8_t)((g_seconds_since_sync + 11) % g_startclock_interval);
|
|
}
|
|
else
|
|
{
|
|
g_lastSeconds = 0;
|
|
}
|
|
} /* end of Timer1 ISR */
|
|
|
|
/* This interrupt generates an audio tone on the audio out pin. */
|
|
SIGNAL(TIMER0_COMPA_vect)
|
|
{
|
|
static BOOL toggle = 0;
|
|
|
|
toggle = !toggle;
|
|
|
|
if(g_audio_tone_state)
|
|
{
|
|
if(toggle)
|
|
{
|
|
digitalWrite(PIN_AUDIO_OUT,ON);
|
|
}
|
|
else
|
|
{
|
|
digitalWrite(PIN_AUDIO_OUT,OFF);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
digitalWrite(PIN_AUDIO_OUT,OFF);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*
|
|
* Here is the main loop for the Microfox Transmitter
|
|
*
|
|
* */
|
|
void loop()
|
|
{
|
|
static int time_for_id = 99;
|
|
static BOOL id_set = TRUE;
|
|
static BOOL proceed = FALSE;
|
|
|
|
/**************************************
|
|
* The watchdog must be petted periodically to keep it from barking
|
|
**************************************/
|
|
cli(); wdt_reset(); /* HW watchdog */ sei();
|
|
|
|
handleLinkBusMsgs();
|
|
|
|
if(!g_on_the_air || proceed)
|
|
{
|
|
proceed = FALSE;
|
|
|
|
/* Choose the appropriate Morse pattern to be sent */
|
|
if(g_fox == FOX_DEMO)
|
|
{
|
|
strcpy(g_messages_text[PATTERN_TEXT],g_morsePatterns[g_fox_counter]);
|
|
}
|
|
else if(g_fox == SPRINT_DEMO)
|
|
{
|
|
strcpy(g_messages_text[PATTERN_TEXT],g_morsePatterns[g_fox_counter + 8]);
|
|
}
|
|
else
|
|
{
|
|
strcpy(g_messages_text[PATTERN_TEXT],g_morsePatterns[g_fox]);
|
|
}
|
|
|
|
/* At the appropriate time set the pattern to be sent and start transmissions */
|
|
if((g_fox == FOX_DEMO) || (g_fox == SPRINT_DEMO) || (g_fox == BEACON) || (g_fox == FOXORING) || (g_fox == SPECTATOR) || (g_fox == (g_fox_counter + g_fox_id_offset)))
|
|
{
|
|
BOOL repeat = TRUE;
|
|
g_code_throttle = THROTTLE_VAL_FROM_WPM(g_pattern_codespeed);
|
|
makeMorse(g_messages_text[PATTERN_TEXT],&repeat,NULL);
|
|
|
|
if(g_time_to_ID || (g_id_interval <= g_on_air_interval))
|
|
{
|
|
time_for_id = g_on_air_interval - (500 + timeRequiredToSendStrAtWPM(g_messages_text[STATION_ID],g_id_codespeed)) / 1000;
|
|
g_time_to_ID = FALSE;
|
|
}
|
|
else
|
|
{
|
|
time_for_id = g_on_air_interval + 99; /* prevent sending ID */
|
|
}
|
|
|
|
id_set = FALSE;
|
|
g_on_the_air = TRUE;
|
|
g_callsign_sent = FALSE;
|
|
g_fox_transition = FALSE;
|
|
g_fox_tone_offset = g_fox_counter;
|
|
}
|
|
else
|
|
{
|
|
if(!g_LEDs_Timed_Out) /* below will flash LED when off cycle for a heartbeat indicator */
|
|
{
|
|
if(g_blinky_time)
|
|
{
|
|
if(!g_sync_pin_stable)
|
|
{
|
|
digitalWrite(PIN_LED,OFF);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
digitalWrite(PIN_LED,ON);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!id_set && (g_fox_seconds_into_interval == time_for_id)) /* Send the call sign at the right time */
|
|
{
|
|
g_code_throttle = THROTTLE_VAL_FROM_WPM(g_id_codespeed);
|
|
BOOL repeat = FALSE;
|
|
makeMorse(g_messages_text[STATION_ID],&repeat,NULL);
|
|
id_set = TRUE;
|
|
g_callsign_sent = FALSE;
|
|
}
|
|
else if((g_fox >= SPRINT_S1) && (g_fox <= SPRINT_DEMO))
|
|
{
|
|
if(g_fox_transition)
|
|
{
|
|
g_fox_transition = FALSE;
|
|
g_on_the_air = FALSE;
|
|
proceed = TRUE;
|
|
}
|
|
}
|
|
|
|
if((g_fox == FOX_DEMO) || (g_fox == SPRINT_DEMO))
|
|
{
|
|
if((g_callsign_sent) && g_fox_transition) /* Ensure we've begun the next minute before proceeding */
|
|
{
|
|
proceed = TRUE;
|
|
}
|
|
}
|
|
else if((g_fox == BEACON) || (g_fox == FOXORING) || (g_fox == SPECTATOR)) /* Proceed as soon as the callsign has been sent */
|
|
{
|
|
if(g_callsign_sent)
|
|
{
|
|
proceed = TRUE;
|
|
}
|
|
}
|
|
else if((g_fox >= SPRINT_S1) && (g_fox <= SPRINT_F5) && g_callsign_sent)
|
|
{
|
|
g_on_the_air = FALSE;
|
|
}
|
|
else if((g_fox != g_fox_counter) && g_callsign_sent) /* Turn off transmissions during minutes when this fox should be silent */
|
|
{
|
|
g_on_the_air = FALSE;
|
|
}
|
|
}
|
|
} /* End of main loop() */
|
|
|
|
void sendMorseTone(BOOL onOff)
|
|
{
|
|
if(!g_lastSeconds)
|
|
{
|
|
OCR0A = DEFAULT_TONE_FREQUENCY - g_fox_tone_offset;
|
|
g_audio_tone_state = onOff;
|
|
}
|
|
else
|
|
{
|
|
OCR0A = DEFAULT_TONE_FREQUENCY;
|
|
g_audio_tone_state = OFF;
|
|
}
|
|
}
|
|
|
|
void playStartingTone(uint8_t toneFreq)
|
|
{
|
|
if(toneFreq > 0)
|
|
{
|
|
OCR0A = toneFreq;
|
|
g_audio_tone_state = ON;
|
|
}
|
|
else
|
|
{
|
|
OCR0A = DEFAULT_TONE_FREQUENCY;
|
|
g_audio_tone_state = OFF;
|
|
}
|
|
}
|
|
|
|
/* The compiler does not seem to optimize large switch statements correctly */
|
|
void __attribute__((optimize("O0"))) handleLinkBusMsgs()
|
|
{
|
|
LinkbusRxBuffer* lb_buff;
|
|
BOOL send_ack = TRUE;
|
|
|
|
while((lb_buff = nextFullRxBuffer()))
|
|
{
|
|
LBMessageID msg_id = lb_buff->id;
|
|
|
|
switch(msg_id)
|
|
{
|
|
case MESSAGE_RESET:
|
|
{
|
|
softwareReset();
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_OVERRIDE_DIP:
|
|
{
|
|
FoxType holdFox = (FoxType)g_override_DIP_switches;
|
|
int c = (int)(lb_buff->fields[FIELD1][0]);
|
|
|
|
if(c)
|
|
{
|
|
if(c == 'B')
|
|
{
|
|
c = BEACON;
|
|
}
|
|
else if(c == 'D')
|
|
{
|
|
char t = lb_buff->fields[FIELD2][0];
|
|
|
|
if(t == 'S')
|
|
{
|
|
c = SPRINT_DEMO;
|
|
}
|
|
else
|
|
{
|
|
c = FOX_DEMO;
|
|
}
|
|
}
|
|
else if(c == 'F')
|
|
{
|
|
c = FOXORING;
|
|
}
|
|
else if(c == 'C')
|
|
{
|
|
char t = lb_buff->fields[FIELD2][0];
|
|
lb_buff->fields[FIELD2][1] = '\0';
|
|
|
|
if(t == 'B')
|
|
{
|
|
t = '0';
|
|
}
|
|
|
|
if(isdigit(t))
|
|
{
|
|
c = CLAMP(BEACON,atoi(lb_buff->fields[FIELD2]),FOX_5);
|
|
}
|
|
}
|
|
else if(c == 'S')
|
|
{
|
|
int x = 0;
|
|
char t = lb_buff->fields[FIELD2][0];
|
|
char u = lb_buff->fields[FIELD2][1];
|
|
lb_buff->fields[FIELD2][2] = '\0';
|
|
|
|
if(t == 'B')
|
|
{
|
|
x = BEACON;
|
|
}
|
|
else if(t == 'F')
|
|
{
|
|
if((u > '0') && (u < '6'))
|
|
{
|
|
x = SPRINT_F1 + (u - '1');
|
|
}
|
|
}
|
|
else if(t == 'S')
|
|
{
|
|
if((u > '0') && (u < '6'))
|
|
{
|
|
x = SPRINT_S1 + (u - '1');
|
|
}
|
|
else
|
|
{
|
|
x = SPECTATOR;
|
|
}
|
|
}
|
|
else if(u == 'F')
|
|
{
|
|
if((t > '0') && (t < '6'))
|
|
{
|
|
x = SPRINT_F1 + (t - '1');
|
|
}
|
|
}
|
|
else if(u == 'S')
|
|
{
|
|
if((t > '0') && (t < '6'))
|
|
{
|
|
x = SPRINT_S1 + (t - '1');
|
|
}
|
|
}
|
|
|
|
if(x != BEACON)
|
|
{
|
|
c = CLAMP(SPECTATOR,x,SPRINT_F5);
|
|
}
|
|
}
|
|
else if(c == 'N')
|
|
{
|
|
char t = lb_buff->fields[FIELD2][0];
|
|
|
|
if(t == '2')
|
|
{
|
|
c = NO_CODE_START_TONES_2M;
|
|
}
|
|
else if(t == '5')
|
|
{
|
|
c = NO_CODE_START_TONES_5M;
|
|
}
|
|
else
|
|
{
|
|
c = BEACON;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
c = atoi(lb_buff->fields[FIELD1]);
|
|
}
|
|
|
|
if((c >= BEACON) && (c < INVALID_FOX))
|
|
{
|
|
g_override_DIP_switches = c;
|
|
g_fox = (FoxType)c;
|
|
eeprom_update_byte(&ee_override_DIP_switches,g_override_DIP_switches);
|
|
if(holdFox != g_fox)
|
|
{
|
|
doSynchronization();
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(g_tempStr,"DIP=%u\n",g_override_DIP_switches);
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_LEDS:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
if((lb_buff->fields[FIELD1][1] == 'F') || (lb_buff->fields[FIELD1][0] == '0'))
|
|
{
|
|
g_enable_LEDs = FALSE;
|
|
digitalWrite(PIN_LED,OFF); /* LED Off */
|
|
}
|
|
else
|
|
{
|
|
g_enable_LEDs = TRUE;
|
|
}
|
|
|
|
eeprom_update_byte(&ee_enable_LEDs,g_enable_LEDs);
|
|
g_LEDs_Timed_Out = !g_enable_LEDs;
|
|
}
|
|
|
|
sprintf(g_tempStr,"LED:%s\n",g_enable_LEDs ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_STARTTONES_ENABLE:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
if((lb_buff->fields[FIELD1][1] == 'F') || (lb_buff->fields[FIELD1][0] == '0'))
|
|
{
|
|
g_enable_start_timer = FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_enable_start_timer = TRUE;
|
|
}
|
|
|
|
eeprom_update_byte(&ee_enable_start_timer,g_enable_start_timer);
|
|
}
|
|
|
|
sprintf(g_tempStr,"STA:%s\n",g_enable_start_timer ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_TRANSMITTER_ENABLE:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
if((lb_buff->fields[FIELD1][1] == 'F') || (lb_buff->fields[FIELD1][0] == '0'))
|
|
{
|
|
cli();
|
|
g_enable_transmitter = FALSE;
|
|
digitalWrite(PIN_MORSE_KEY,OFF);
|
|
sei();
|
|
}
|
|
else
|
|
{
|
|
g_enable_transmitter = TRUE;
|
|
}
|
|
|
|
eeprom_update_byte(&ee_enable_transmitter,g_enable_transmitter);
|
|
}
|
|
|
|
sprintf(g_tempStr,"TXE:%s\n",g_enable_transmitter ? "ON" : "OFF");
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_GO:
|
|
{
|
|
doSynchronization();
|
|
lb_send_string((char*)"Re-sync successful!\n",FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_FACTORY_RESET:
|
|
{
|
|
initializeEEPROMVars(TRUE);
|
|
softwareReset();
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_CLOCK_CAL:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
uint16_t c = (uint16_t)atoi(lb_buff->fields[FIELD1]);
|
|
|
|
if(c > 100)
|
|
{
|
|
g_clock_calibration = c;
|
|
OCR1A = g_clock_calibration;
|
|
eeprom_update_word(&ee_clock_calibration,g_clock_calibration);
|
|
}
|
|
}
|
|
|
|
sprintf(g_tempStr,"Cal=%u\n",g_clock_calibration);
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_SET_STATION_ID:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
strcpy(g_tempStr," "); /* Space before ID gets sent */
|
|
strcat(g_tempStr,lb_buff->fields[FIELD1]);
|
|
|
|
if(lb_buff->fields[FIELD2][0])
|
|
{
|
|
strcat(g_tempStr," ");
|
|
strcat(g_tempStr,lb_buff->fields[FIELD2]);
|
|
}
|
|
|
|
if(strlen(g_tempStr) <= MAX_PATTERN_TEXT_LENGTH)
|
|
{
|
|
uint8_t i;
|
|
strcpy(g_messages_text[STATION_ID],g_tempStr);
|
|
|
|
for(i = 0; i < strlen(g_messages_text[STATION_ID]); i++)
|
|
{
|
|
eeprom_update_byte((uint8_t*)&ee_stationID_text[i],(uint8_t)g_messages_text[STATION_ID][i]);
|
|
}
|
|
|
|
eeprom_update_byte((uint8_t*)&ee_stationID_text[i],0);
|
|
}
|
|
}
|
|
|
|
if(g_messages_text[STATION_ID][0])
|
|
{
|
|
g_time_needed_for_ID = (500 + timeRequiredToSendStrAtWPM(g_messages_text[STATION_ID],g_id_codespeed)) / 1000;
|
|
}
|
|
|
|
sprintf(g_tempStr,"ID:%s\n",g_messages_text[STATION_ID]);
|
|
lb_send_string(g_tempStr,TRUE);
|
|
}
|
|
break;
|
|
|
|
|
|
case MESSAGE_CODE_SPEED:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0] == 'I')
|
|
{
|
|
if(lb_buff->fields[FIELD2][0])
|
|
{
|
|
uint8_t speed = atol(lb_buff->fields[FIELD2]);
|
|
g_id_codespeed = CLAMP(MIN_CODE_SPEED_WPM,speed,MAX_CODE_SPEED_WPM);
|
|
eeprom_update_byte(&ee_id_codespeed,g_id_codespeed);
|
|
|
|
if(g_messages_text[STATION_ID][0])
|
|
{
|
|
g_time_needed_for_ID = (500 + timeRequiredToSendStrAtWPM(g_messages_text[STATION_ID],g_id_codespeed)) / 1000;
|
|
}
|
|
}
|
|
}
|
|
sprintf(g_tempStr,"ID: %d wpm\n",g_id_codespeed);
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_VERSION:
|
|
{
|
|
sprintf(g_tempStr,"SW Ver:%s\n",SW_REVISION);
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_TEMP:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0] == 'C')
|
|
{
|
|
if(lb_buff->fields[FIELD2][0])
|
|
{
|
|
int16_t v = atoi(lb_buff->fields[FIELD2]);
|
|
|
|
if((v > -2000) && (v < 2000))
|
|
{
|
|
g_temp_calibration = v;
|
|
eeprom_update_word((uint16_t*)&ee_temp_calibration,(uint16_t)g_temp_calibration);
|
|
}
|
|
}
|
|
|
|
sprintf(g_tempStr,"T Cal= %d\n",g_temp_calibration);
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
|
|
float temp = 10. * getTemp();
|
|
sprintf(g_tempStr,"Temp: %d.%dC\n",(int)temp / 10,abs((int)temp % 10));
|
|
lb_send_string(g_tempStr,FALSE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
lb_send_Help();
|
|
}
|
|
break;
|
|
}
|
|
|
|
lb_buff->id = (LBMessageID)MESSAGE_EMPTY;
|
|
if(send_ack)
|
|
{
|
|
lb_send_NewPrompt();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set non-volatile variables to their values stored in EEPROM
|
|
*/
|
|
void initializeEEPROMVars(BOOL resetAll)
|
|
{
|
|
uint8_t i;
|
|
|
|
uint8_t initialization_flag = eeprom_read_byte(&ee_interface_eeprom_initialization_flag);
|
|
|
|
if(!resetAll && (initialization_flag == EEPROM_INITIALIZED_FLAG)) /* EEPROM is up to date */
|
|
{
|
|
g_id_codespeed = CLAMP(MIN_CODE_SPEED_WPM,eeprom_read_byte(&ee_id_codespeed),MAX_CODE_SPEED_WPM);
|
|
g_clock_calibration = eeprom_read_word(&ee_clock_calibration);
|
|
g_temp_calibration = (int16_t)eeprom_read_word((uint16_t*)&ee_temp_calibration);
|
|
g_override_DIP_switches = eeprom_read_byte(&ee_override_DIP_switches);
|
|
g_enable_LEDs = eeprom_read_byte(&ee_enable_LEDs);
|
|
g_enable_start_timer = eeprom_read_byte(&ee_enable_start_timer);
|
|
g_enable_transmitter = eeprom_read_byte(&ee_enable_transmitter);
|
|
|
|
for(i = 0; i < MAX_PATTERN_TEXT_LENGTH; i++)
|
|
{
|
|
g_messages_text[STATION_ID][i] = (char)eeprom_read_byte((uint8_t*)(&ee_stationID_text[i]));
|
|
if(!g_messages_text[STATION_ID][i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else /* EEPROM is missing some updates */
|
|
{
|
|
if(resetAll || (eeprom_read_byte(&ee_id_codespeed) == 0xFF))
|
|
{
|
|
g_id_codespeed = EEPROM_ID_CODE_SPEED_DEFAULT;
|
|
eeprom_update_byte(&ee_id_codespeed,g_id_codespeed);
|
|
}
|
|
else
|
|
{
|
|
g_id_codespeed = CLAMP(MIN_CODE_SPEED_WPM,eeprom_read_byte(&ee_id_codespeed),MAX_CODE_SPEED_WPM);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_word(&ee_clock_calibration) == 0xFFFF))
|
|
{
|
|
g_clock_calibration = EEPROM_CLOCK_CALIBRATION_DEFAULT;
|
|
eeprom_update_word(&ee_clock_calibration,g_clock_calibration); /* Calibration only gets set by a serial command */
|
|
}
|
|
else
|
|
{
|
|
g_clock_calibration = eeprom_read_word(&ee_clock_calibration);
|
|
}
|
|
|
|
if(resetAll || ((uint16_t)eeprom_read_word((uint16_t*)&ee_temp_calibration) == 0xFFFF))
|
|
{
|
|
g_temp_calibration = EEPROM_TEMP_CALIBRATION_DEFAULT;
|
|
eeprom_update_word((uint16_t*)&ee_temp_calibration,(uint16_t)g_temp_calibration);
|
|
}
|
|
else
|
|
{
|
|
g_temp_calibration = (int16_t)eeprom_read_word((uint16_t*)&ee_temp_calibration);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_byte(&ee_override_DIP_switches) == 0xFF))
|
|
{
|
|
g_override_DIP_switches = EEPROM_OVERRIDE_DIP_SW_DEFAULT;
|
|
eeprom_update_byte(&ee_override_DIP_switches,g_override_DIP_switches); /* Only gets set by a serial command */
|
|
}
|
|
else
|
|
{
|
|
g_override_DIP_switches = eeprom_read_byte(&ee_override_DIP_switches);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_byte(&ee_enable_LEDs) == 0xFF))
|
|
{
|
|
g_enable_LEDs = EEPROM_ENABLE_LEDS_DEFAULT;
|
|
eeprom_update_byte(&ee_enable_LEDs,g_enable_LEDs); /* Only gets set by a serial command */
|
|
}
|
|
else
|
|
{
|
|
g_enable_LEDs = eeprom_read_byte(&ee_enable_LEDs);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_byte(&ee_enable_start_timer) == 0xFF))
|
|
{
|
|
g_enable_start_timer = EEPROM_ENABLE_STARTTIMER_DEFAULT;
|
|
eeprom_update_byte(&ee_enable_start_timer,g_enable_start_timer); /* Only gets set by a serial command */
|
|
}
|
|
else
|
|
{
|
|
g_enable_start_timer = eeprom_read_byte(&ee_enable_start_timer);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_byte(&ee_enable_transmitter) == 0xFF))
|
|
{
|
|
g_enable_transmitter = EEPROM_ENABLE_TRANSMITTER_DEFAULT;
|
|
eeprom_update_byte(&ee_enable_transmitter,g_enable_transmitter); /* Only gets set by a serial command */
|
|
}
|
|
else
|
|
{
|
|
g_enable_transmitter = eeprom_read_byte(&ee_enable_transmitter);
|
|
}
|
|
|
|
if(resetAll || (eeprom_read_byte((uint8_t*)(&ee_stationID_text[0])) == 0xFF))
|
|
{
|
|
uint16_t i;
|
|
strncpy(g_messages_text[STATION_ID],EEPROM_STATION_ID_DEFAULT,MAX_PATTERN_TEXT_LENGTH);
|
|
|
|
for(i = 0; i < strlen(g_messages_text[STATION_ID]); i++) /* Only gets set by a serial command */
|
|
{
|
|
eeprom_update_byte((uint8_t*)&ee_stationID_text[i],(uint8_t)g_messages_text[STATION_ID][i]);
|
|
}
|
|
eeprom_update_byte((uint8_t*)&ee_stationID_text[i],0);
|
|
}
|
|
else
|
|
{
|
|
for(i = 0; i < MAX_PATTERN_TEXT_LENGTH; i++)
|
|
{
|
|
g_messages_text[STATION_ID][i] = (char)eeprom_read_byte((uint8_t*)(&ee_stationID_text[i]));
|
|
if(!g_messages_text[STATION_ID][i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
eeprom_write_byte(&ee_interface_eeprom_initialization_flag,EEPROM_INITIALIZED_FLAG);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set up registers for measuring processor temperature
|
|
*/
|
|
void setUpTemp(void)
|
|
{
|
|
/* The internal temperature has to be used
|
|
* with the internal reference of 1.1V.
|
|
* Channel 8 can not be selected with
|
|
* the analogRead function yet. */
|
|
/* Set the internal reference and mux. */
|
|
ADMUX = ((1 << REFS1) | (1 << REFS0) | (1 << MUX3));
|
|
|
|
/* Slow the ADC clock down to 125 KHz
|
|
* by dividing by 128. Assumes that the
|
|
* standard Arduino 16 MHz clock is in use. */
|
|
ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
|
|
|
|
ADCSRA |= (1 << ADEN); /* enable the ADC */
|
|
|
|
// _delay_ms(200); /* wait for voltages to become stable. */
|
|
|
|
ADCSRA |= (1 << ADSC); /* Start the ADC */
|
|
|
|
readADC();
|
|
}
|
|
|
|
/*
|
|
* Read the temperature ADC value
|
|
*/
|
|
uint16_t readADC()
|
|
{
|
|
/* Make sure the most recent ADC read is complete. */
|
|
while((ADCSRA & (1 << ADSC)))
|
|
{
|
|
; /* Just wait for ADC to finish. */
|
|
}
|
|
uint16_t result = ADCW;
|
|
/* Initiate another reading. */
|
|
ADCSRA |= (1 << ADSC);
|
|
return( result);
|
|
}
|
|
|
|
/*
|
|
* Returns the most recent temperature reading
|
|
*/
|
|
float getTemp(void)
|
|
{
|
|
float offset = CLAMP(-200.,(float)g_temp_calibration / 10.,200.);
|
|
|
|
/* The offset (first term) was determined empirically */
|
|
readADC(); /* throw away first reading */
|
|
return(offset + (readADC() - 324.31) / 1.22);
|
|
}
|
|
|
|
|
|
void doSynchronization()
|
|
{
|
|
setupForFox();
|
|
cli();
|
|
/* Sync button pin change interrupt */
|
|
PCMSK2 = 0x00;
|
|
PCMSK2 = (1 << PCINT19); /* Enable PCINT19 */
|
|
PCICR = 0x00;
|
|
PCICR = (1 << PCIE2); /* Enable pin change interrupt 2 */
|
|
pinMode(PIN_SYNC,INPUT_PULLUP);
|
|
g_sync_enabled = TRUE;
|
|
|
|
TCNT1 = 0; /* Initialize 1-second counter value to 0 */
|
|
g_seconds_since_sync = 0;
|
|
g_fox_seconds_into_interval = 0;
|
|
g_sync_pin_stable = FALSE;
|
|
digitalWrite(PIN_LED,LOW);
|
|
g_on_the_air = FALSE;
|
|
g_fox_counter = 1; /* Don't count on the 1-sec timer resetting this quickly enough */
|
|
sei();
|
|
}
|
|
|
|
void setupForFox()
|
|
{
|
|
cli();
|
|
pinMode(PIN_SYNC,INPUT_PULLUP); /* Sync button */
|
|
pinMode(PIN_DIP_0,INPUT_PULLUP); /* DIP switch LSB */
|
|
pinMode(PIN_DIP_1,INPUT_PULLUP); /* DIP switch middle bit */
|
|
pinMode(PIN_DIP_2,INPUT_PULLUP); /* DIP switch MSB */
|
|
digitalWrite(PIN_LED,OFF); /* Turn off led sync switch is now open */
|
|
|
|
g_seconds_since_sync = 0; /* Total elapsed time counter */
|
|
g_on_the_air = FALSE; /* Controls transmitter Morse activity */
|
|
g_code_throttle = 0; /* Adjusts Morse code speed */
|
|
g_callsign_sent = FALSE;
|
|
|
|
g_on_air_interval = 0;
|
|
g_fox_seconds_into_interval = 0;
|
|
g_number_of_foxes = 0;
|
|
g_fox_transition = FALSE;
|
|
g_fox_id_offset = 0;
|
|
g_id_interval = 0;
|
|
g_time_to_ID = FALSE;
|
|
g_audio_tone_state = OFF;
|
|
sei();
|
|
|
|
g_LEDs_Timed_Out = !g_enable_LEDs;
|
|
|
|
if(g_override_DIP_switches)
|
|
{
|
|
g_fox = CLAMP(BEACON,(FoxType)g_override_DIP_switches,INVALID_FOX);
|
|
if(g_fox == INVALID_FOX)
|
|
{
|
|
g_fox = BEACON;
|
|
}
|
|
}
|
|
else /* Read DIP Switches */
|
|
{
|
|
g_fox = (FoxType)0;
|
|
|
|
if(digitalRead(PIN_DIP_0) == LOW) /* LSB */
|
|
{
|
|
g_fox += 1;
|
|
}
|
|
|
|
if(digitalRead(PIN_DIP_1) == LOW) /* middle bit */
|
|
{
|
|
g_fox += 2;
|
|
}
|
|
|
|
if(digitalRead(PIN_DIP_2) == LOW) /* MSB */
|
|
{
|
|
g_fox += 4;
|
|
}
|
|
}
|
|
|
|
#if !HARDWARE_EXTERNAL_DIP_PULLUPS_INSTALLED
|
|
/* Disable pull-ups to save power */
|
|
pinMode(PIN_DIP_0,INPUT); /* fox switch LSB */
|
|
pinMode(PIN_DIP_1,INPUT); /* fox switch middle bit */
|
|
pinMode(PIN_DIP_2,INPUT); /* fox switch MSB */
|
|
pinMode(PIN_DIP_0,OUTPUT); /* Don't allow pin to float */
|
|
pinMode(PIN_DIP_1,OUTPUT); /* Don't allow pin to float */
|
|
pinMode(PIN_DIP_2,OUTPUT); /* Don't allow pin to float */
|
|
#endif /* HARDWARE_EXTERNAL_DIP_PULLUPS_INSTALLED */
|
|
|
|
switch(g_fox)
|
|
{
|
|
case NO_CODE_START_TONES_2M:
|
|
{
|
|
g_startclock_interval = 120;
|
|
g_enable_start_timer = TRUE;
|
|
}
|
|
break;
|
|
|
|
case NO_CODE_START_TONES_5M:
|
|
{
|
|
g_startclock_interval = 300;
|
|
g_enable_start_timer = TRUE;
|
|
}
|
|
break;
|
|
|
|
case FOX_1:
|
|
case FOX_2:
|
|
case FOX_3:
|
|
case FOX_4:
|
|
case FOX_5:
|
|
case FOX_DEMO:
|
|
{
|
|
g_on_air_interval = 60;
|
|
g_number_of_foxes = 5;
|
|
g_fox_id_offset = 0;
|
|
g_pattern_codespeed = 8;
|
|
g_id_interval = 60;
|
|
g_startclock_interval = 300;
|
|
}
|
|
break;
|
|
|
|
case SPRINT_S1:
|
|
case SPRINT_F5:
|
|
case SPRINT_DEMO:
|
|
{
|
|
g_on_air_interval = 12;
|
|
g_number_of_foxes = 5;
|
|
g_pattern_codespeed = ((g_fox == SPRINT_DEMO) || (g_fox <= SPRINT_S5)) ? 10 : 15;
|
|
g_fox_id_offset = g_fox <= SPRINT_S5 ? SPRINT_S1 - 1 : SPRINT_F1 - 1;
|
|
g_id_interval = 600;
|
|
g_startclock_interval = 120;
|
|
}
|
|
break;
|
|
|
|
/* case BEACON: */
|
|
/* case SPECTATOR: */
|
|
default:
|
|
{
|
|
g_on_air_interval = 600;
|
|
g_number_of_foxes = 1;
|
|
g_pattern_codespeed = 8;
|
|
g_id_interval = 600;
|
|
g_startclock_interval = (g_fox == SPECTATOR) ? 120 : 300;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void softwareReset()
|
|
{
|
|
#if USE_WDT_RESET
|
|
cli();
|
|
wdt_enable(WDTO_1S);
|
|
while(1)
|
|
{
|
|
;
|
|
}
|
|
#else
|
|
lb_send_string((char*)"Press RESET button now.\n",FALSE);
|
|
#endif // USE_WDT_RESET
|
|
}
|