1333 wiersze
34 KiB
C++
1333 wiersze
34 KiB
C++
/*
|
|
* 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"
|
|
|
|
#define MAX_PATTERN_TEXT_LENGTH 20
|
|
|
|
volatile int seconds = 0; /* Init timer to first second. Set in ISR checked by main. */
|
|
volatile int minutes = 1; /* Init timer to cycle 1. */
|
|
volatile int g_fox = 0; /* Sets Fox number not set by ISR. Set in startup and checked in main. */
|
|
volatile int active = 0; /* Disable active. set and clear in ISR. Checked in main. */
|
|
volatile int EOC_KEY = 0; /* Used for end of cycle flag. Set in main is cleared in ISR and checked in main. */
|
|
volatile int demo = 0; /* used to put us in ARDF cycle mode sw = 6. */
|
|
volatile int foxO = 0; /* used to put us in fox o mode switch 7. */
|
|
|
|
/***********************************************************************
|
|
* 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 volatile EC g_last_error_code = ERROR_CODE_NO_ERROR;
|
|
static BOOL EEMEM ee_interface_eeprom_initialization_flag = EEPROM_UNINITIALIZED;
|
|
static char EEMEM ee_stationID_text[MAX_PATTERN_TEXT_LENGTH + 1];
|
|
static char EEMEM ee_pattern_text[MAX_PATTERN_TEXT_LENGTH + 1];
|
|
static uint8_t EEMEM ee_pattern_codespeed;
|
|
static uint8_t EEMEM ee_id_codespeed;
|
|
static uint16_t EEMEM ee_ID_time;
|
|
static uint16_t EEMEM ee_clock_calibration;
|
|
static uint8_t EEMEM ee_override_DIP_switches;
|
|
static uint8_t EEMEM ee_enable_LEDs;
|
|
static uint8_t EEMEM ee_enable_sync;
|
|
|
|
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 int16_t g_ID_period_seconds = EEPROM_ID_TIME_INTERVAL_DEFAULT; /* amount of time between ID/callsign transmissions */
|
|
static volatile uint16_t g_clock_calibration = EEPROM_CLOCK_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_sync;
|
|
|
|
static char g_tempStr[21] = { '\0' };
|
|
|
|
static volatile uint8_t g_start_override = FALSE;
|
|
|
|
/*
|
|
* Function Prototypes
|
|
*/
|
|
void handleLinkBusMsgs(void);
|
|
void initializeEEPROMVars(void);
|
|
void saveAllEEPROM(void);
|
|
void transmitString(char msg[], int speed);
|
|
void word_space(int speed);
|
|
int codeBits(char alphaNum);
|
|
float getTemp(void);
|
|
uint16_t readADC();
|
|
void setUpTemp(void);
|
|
|
|
#ifndef USE_WATCHDOG
|
|
void (* resetFunc) (void) = 0; /*declare reset function @ address 0 */
|
|
#endif
|
|
|
|
void setup()
|
|
{
|
|
initializeEEPROMVars(); /* Initialize variables stored in EEPROM */
|
|
linkbus_init(BAUD); /* Start the Link Bus serial comms */
|
|
setUpTemp();
|
|
linkbus_send_text((char*)"\n\nStored Data:\n");
|
|
sprintf(g_tempStr, " ID: %s\n", g_messages_text[STATION_ID]);
|
|
linkbus_send_text(g_tempStr);
|
|
sprintf(g_tempStr, " CAL: %u\n", g_clock_calibration);
|
|
linkbus_send_text(g_tempStr);
|
|
sprintf(g_tempStr, " DIP: %u\n", g_override_DIP_switches);
|
|
linkbus_send_text(g_tempStr);
|
|
sprintf(g_tempStr, " LED: %s\n", g_enable_LEDs == TRUE ? "ON" : "OFF");
|
|
linkbus_send_text(g_tempStr);
|
|
sprintf(g_tempStr, " SYN: %s\n", g_enable_sync == TRUE ? "ON" : "OFF");
|
|
linkbus_send_text(g_tempStr);
|
|
lb_send_NewPrompt();
|
|
|
|
/* set pins as outputs */
|
|
pinMode(13, OUTPUT); /* The nano amber LED
|
|
* This led blinks when off cycle and
|
|
* blinks with code when on cycle. */
|
|
pinMode(2, OUTPUT); /* This pin is used to control the KEY line to the transmittter
|
|
* only active on cycle. */
|
|
|
|
/* set the pins that are inputs */
|
|
pinMode(3, INPUT); /* This pin is used as the sync line
|
|
* NOTE: The original albq PIC controllers used the CPU reset line as the
|
|
* sync line. We had issues with transmitters getting out of sync during transport.
|
|
* later investigation found that ESD events during transport was resetting the
|
|
* PIC. This code will read this I/O line for sync and after finding that the I/O line
|
|
* has switched to the high state it never reads it again until a power cycle.
|
|
* The Arduino reset line does not go off board. ESD event caused out of sync issue fixed. */
|
|
pinMode(4, INPUT); /* fox switch LSB */
|
|
pinMode(5, INPUT); /* fox switch middle bit */
|
|
pinMode(6, INPUT); /* fox switch MSB */
|
|
|
|
/*
|
|
* read the dip switches and compute the desired fox number
|
|
* */
|
|
if(g_override_DIP_switches)
|
|
{
|
|
g_fox = g_override_DIP_switches;
|
|
}
|
|
else
|
|
{
|
|
if(digitalRead(4) == HIGH ) /*Lsb */
|
|
{
|
|
g_fox++;
|
|
}
|
|
if(digitalRead(5) == HIGH ) /* middle bit */
|
|
{
|
|
g_fox += 2;
|
|
}
|
|
if(digitalRead(6) == HIGH ) /* MSB */
|
|
{
|
|
g_fox += 4;
|
|
}
|
|
}
|
|
|
|
if(g_fox == 6 ) /* trap for invalid > 6 fox */
|
|
{
|
|
demo = 1; /* force to demo mode */
|
|
g_fox = 1; /* start with MOE */
|
|
}
|
|
|
|
if(g_fox == 7 ) /* put us in fox O mode */
|
|
{
|
|
foxO = 1; /* force to fox O mode */
|
|
}
|
|
|
|
cli(); /*stop interrupts for setup */
|
|
|
|
digitalWrite(13, LOW); /* Turn off led sync switch is now open */
|
|
|
|
/*
|
|
* get ready set go
|
|
* time to start fox operation
|
|
*
|
|
* The timer came from the below source and I modified it
|
|
* for use with microfox. it is the 1 second core timer
|
|
* this replaced the PIC timer as the hardware is different.
|
|
* ******************************************************
|
|
* timer interrupts
|
|
* by Amanda Ghassaei
|
|
* June 2012 */
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
*/
|
|
/*
|
|
* Modifications to the time for microfox done by Jerry Boyd WB8WFK November 2019
|
|
* Removed the unused timers we only use timer 1.
|
|
*
|
|
* timer setup for timer1,
|
|
* For Arduino uno or any board with ATMEL 328/168.. diecimila, duemilanove, lilypad, nano, mini...
|
|
* this code will enable arduino timer 1 interrupts.
|
|
* timer1 will interrupt at 1Hz
|
|
* master variables
|
|
* 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 FIX 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);
|
|
sei(); /*allow interrupts. Arm and run */
|
|
|
|
/* I found this on the web
|
|
* note Timer0 - An 8 bit timer used by Arduino functions delay(), millis() and micros().
|
|
* Timer1 - A 16 bit timer used by the Servo() library
|
|
* Timer2 - An 8 bit timer used by the Tone() library */
|
|
/*
|
|
* we now look for the sync line and then arm the timer interrupts
|
|
* when the sync switch is released.
|
|
* */
|
|
|
|
if(g_enable_sync)
|
|
{
|
|
linkbus_send_text((char*)"Waiting for sync.\n");
|
|
linkbus_send_text((char*)"Type \"GO\"\n");
|
|
lb_send_NewPrompt();
|
|
|
|
while(( digitalRead(3) == LOW) && !g_start_override)
|
|
{
|
|
if(g_enable_LEDs)
|
|
{
|
|
digitalWrite(13, HIGH); /* arduino nano LED
|
|
* turn on led to show sync switch is closed */
|
|
}
|
|
|
|
minutes = 1; /* Reset the clock start of cycle addresses different power up times*/
|
|
seconds = 0; /* Reset the clock addresses different power up times*/
|
|
handleLinkBusMsgs();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
linkbus_send_text((char*)"Tx is running!\n");
|
|
lb_send_NewPrompt();
|
|
}
|
|
|
|
g_start_override = TRUE;
|
|
|
|
#ifndef COMPILE_FOR_ARDUINO
|
|
loop();
|
|
#endif
|
|
}/*end setup */
|
|
|
|
|
|
/***********************************************************************
|
|
* 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_MSG_FIELD_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)
|
|
{
|
|
rx_char = toupper(rx_char);
|
|
/* SMCR = 0x00; // exit power-down mode */
|
|
|
|
/* if(g_terminal_mode)
|
|
* { */
|
|
static uint8_t ignoreCount = 0;
|
|
|
|
if(ignoreCount)
|
|
{
|
|
rx_char = '\0';
|
|
ignoreCount--;
|
|
}
|
|
else if(rx_char == 0x1B) /* ESC sequence start */
|
|
{
|
|
rx_char = '\0';
|
|
|
|
if(charIndex < LINKBUS_MAX_MSG_FIELD_LENGTH)
|
|
{
|
|
rx_char = textBuff[charIndex];
|
|
}
|
|
|
|
ignoreCount = 2; /* throw out the next two characters */
|
|
}
|
|
|
|
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--;
|
|
}
|
|
else
|
|
{
|
|
buff->fields[field_index][0] = '\0';
|
|
field_index--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(rx_char == ' ')
|
|
{
|
|
if(textBuff[charIndex - 1] == ' ')
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
else
|
|
{
|
|
/* if(field_index == 0) // message ID received */
|
|
if(field_index > 0)
|
|
{
|
|
buff->fields[field_index - 1][field_len] = 0;
|
|
}
|
|
|
|
field_index++;
|
|
field_len = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(field_index == 0) /* message ID received */
|
|
{
|
|
msg_ID = msg_ID * 10 + rx_char;
|
|
}
|
|
else
|
|
{
|
|
buff->fields[field_index - 1][field_len++] = rx_char;
|
|
}
|
|
}
|
|
|
|
charIndex = MIN(charIndex + 1, LINKBUS_MAX_MSG_FIELD_LENGTH);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((rx_char == 0x7F) || (rx_char == ' ')) /* Handle backspace and Space */
|
|
{
|
|
rx_char = '\0';
|
|
}
|
|
else /* start of new message */
|
|
{
|
|
uint8_t i;
|
|
field_index = 0;
|
|
msg_ID = 0;
|
|
|
|
msg_ID = msg_ID * 10 + 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 = MIN(charIndex + 1, LINKBUS_MAX_MSG_FIELD_LENGTH);
|
|
}
|
|
}
|
|
|
|
if(rx_char)
|
|
{
|
|
lb_echo_char(rx_char);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
* here is our main ISR for the ARDF 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 */
|
|
|
|
{/*This is the ARDF core timer */
|
|
++seconds; /* one second event - update seconds */
|
|
|
|
/*
|
|
* Code to make home transmitter (MO) cycle work
|
|
* this lets transmitter go off for a second and then restart
|
|
* by override of the timer in the MO mode.
|
|
* */
|
|
if((g_fox == 0) && (minutes > 0)) /* check for override for home transmitter mode
|
|
* all other ARDF transmitters use 1,2,3,4,and 5 */
|
|
{
|
|
minutes = 0; /* override timer */
|
|
}
|
|
|
|
/* normal fox mode (MOx cycle)
|
|
* check seconds and update minutes */
|
|
if(seconds == 59)
|
|
{
|
|
++minutes; /* make minutes adjustment */
|
|
EOC_KEY = 0; /* flag to kick out of end cycle long tone keydown */
|
|
}
|
|
if(seconds == 60)
|
|
{
|
|
|
|
seconds = 0; /* set seconds back to 0 */
|
|
}
|
|
/* adjust timer for cycle */
|
|
if(minutes == 6) /* adjust counter for ARDF cycle */
|
|
{
|
|
minutes = 1; /* set for first transmitter */
|
|
}
|
|
|
|
/******************************************************
|
|
*
|
|
* Code below enables ARDF cycle based on Fox number
|
|
*
|
|
****************************************************** */
|
|
|
|
if(g_fox == minutes) /* test for active ARDF cycle */
|
|
{
|
|
active = 1; /* active is a flag that the main state machine uses */
|
|
}
|
|
else
|
|
{
|
|
active = 0;
|
|
}
|
|
} /* end of timer ISR */
|
|
|
|
|
|
|
|
/****************************************************
|
|
**
|
|
** Function to send a CW message
|
|
**
|
|
**************************************************** */
|
|
void Send_Message(int imessage, int speed)
|
|
{
|
|
/* Morse Code timing
|
|
* Dit = 1 time unit (reference)
|
|
* Dah = 3 time units
|
|
* Symbol space = 1 time units
|
|
* letter Space = 3 time units
|
|
* Word space = 7 time units
|
|
*{ */
|
|
int time;
|
|
|
|
if(imessage == ' ')
|
|
{
|
|
word_space(speed);
|
|
}
|
|
else
|
|
{
|
|
while(1)
|
|
{
|
|
/*
|
|
* A trap to catch the condition where number 5 is same as ASCII space
|
|
* changed encoding in code table to set MSB for # 5 as a workaround
|
|
*/
|
|
if(imessage == TX5)
|
|
{
|
|
imessage = 0x20; /* set it back to this so this loop will encode a number 5 */
|
|
}
|
|
|
|
if((imessage & 0X01 ) == 0x01)
|
|
{
|
|
if(g_enable_LEDs)
|
|
{
|
|
digitalWrite(13, HIGH); /* nano LED */
|
|
}
|
|
digitalWrite(2, HIGH); /* TX key line */
|
|
time = speed * 3;
|
|
delay(time);
|
|
digitalWrite(13, LOW); /* arduino nano LED */
|
|
digitalWrite(2, LOW); /* TX key line */
|
|
|
|
}
|
|
else
|
|
{
|
|
/*OUTPUT_BIT(CW_KEY, 1); // Dit */
|
|
if(g_enable_LEDs)
|
|
{
|
|
digitalWrite(13, HIGH); /* arduino nano LED */
|
|
}
|
|
digitalWrite(2, HIGH); /* TX key line */
|
|
delay(speed);
|
|
|
|
digitalWrite(13, LOW); /* arduino nano LED */
|
|
digitalWrite(2, LOW); /* TX key line */
|
|
}
|
|
|
|
imessage = imessage >> 1; /* shift right one bit */
|
|
if(imessage == 1)
|
|
{
|
|
break; /* break the loop */
|
|
}
|
|
/*Do a symbol space */
|
|
delay(speed); /*Symbol space */
|
|
}
|
|
|
|
/* Do a letter space */
|
|
time = speed * 3; /* */
|
|
delay(time); /*inter character (letter) space */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************
|
|
**
|
|
** Function to do a word space based on selected speed
|
|
**
|
|
****************************************** */
|
|
void word_space(int speed)
|
|
{
|
|
int time;
|
|
|
|
time = speed * 7;
|
|
delay(time);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
* here is the main microfox code controller by Jerry Boyd WB8WFK
|
|
*
|
|
* */
|
|
void loop()
|
|
{
|
|
/* put your main code here, to run repeatedly: */
|
|
|
|
int message, id;
|
|
int speed;
|
|
int fast_speed = 50; /* for last part of cycle */
|
|
int slow_speed = 80; /* for first 30 seconds of cycle */
|
|
int very_fast_speed = 45; /* for CW ID */
|
|
|
|
/*
|
|
*
|
|
* F O X O MODE
|
|
*
|
|
* This will place us in fox O mode
|
|
* FOX O mode */
|
|
if(foxO == 1)
|
|
{
|
|
while(1)
|
|
{
|
|
id = 1; /* id flag */
|
|
speed = 55;
|
|
/* do the CW ID for fox O */
|
|
if(id == 1) /* turns on ID for US version */
|
|
{
|
|
/* send transmitter call sign
|
|
* call sign WB8WFK
|
|
* */
|
|
transmitString(g_messages_text[STATION_ID], speed);
|
|
id = 0; /* clear ID Flag */
|
|
} /* end of CW ID space */
|
|
|
|
|
|
speed = 70;
|
|
while(1)
|
|
{
|
|
|
|
message = 0x02; /* E */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
word_space(speed);
|
|
if(seconds > 58)
|
|
{
|
|
id = 1;
|
|
break;
|
|
}
|
|
handleLinkBusMsgs();
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Handle arriving Linkbus messages
|
|
************************************************************************/
|
|
} /* end of the fox O loop */
|
|
|
|
|
|
/*
|
|
* A R D F cycles MO and DEMO
|
|
*/
|
|
while(1) /* Start of the standard ARDF and demo mode loop */
|
|
{
|
|
/*get ready for first pass
|
|
* */
|
|
id = 1; /* set first time pass flag */
|
|
speed = very_fast_speed; /* super Fast ID CW speed */
|
|
/* ID real fast to get past the ID ASAP
|
|
*
|
|
* ID the transmitter with the call sign
|
|
* */
|
|
|
|
while(active)
|
|
{
|
|
/*Serial.println(seconds);
|
|
* Serial.println(minutes);
|
|
* Serial.println(active); */
|
|
if(id == 1) /* turns on ID for US version */
|
|
{
|
|
/* send transmitter call sign
|
|
* call sign WB8WFK
|
|
* */
|
|
transmitString(g_messages_text[STATION_ID], speed);
|
|
id = 0; /* clear ID Flag */
|
|
} /* end of CW ID space */
|
|
|
|
/*************************************************************
|
|
**
|
|
** now send MOX based on Fox number
|
|
**
|
|
************************************************************* */
|
|
speed = slow_speed; /* Set Slow CW speed for non hams // was 100
|
|
* I.E. boy scouts, school groups
|
|
* and Orinereering clubs.
|
|
* */
|
|
word_space(speed); /* have a gap between CW ID and first Mox */
|
|
/*
|
|
* Main MOX send loop
|
|
* */
|
|
while( seconds < 50)
|
|
{
|
|
if(seconds > 30) /* do we switch CW speed to high speed for hams */
|
|
{ /* at 1/2 cycle point */
|
|
speed = fast_speed; /* Yes set fast CW speed for balance of cycle was 55 for pic */
|
|
} /* for hams. */
|
|
|
|
switch(g_fox) /* select the FOX (MOX) to send */
|
|
{
|
|
case 0: /* Fox number home */
|
|
{
|
|
/* Send MO */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
|
|
case 1: /* Fox number 1 */
|
|
{
|
|
/* Send MOE */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x02; /* E */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
|
|
case 2: /* Fox number 2 */
|
|
{
|
|
/* Send MOI */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x04; /* I */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
|
|
case 3: /* Fox number 3 */
|
|
{
|
|
/* Send MOS */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x08; /* S */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
|
|
case 4: /* Fox number 4 */
|
|
{
|
|
/* Send MOH */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x10; /* H */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
|
|
case 5: /* Fox number 5 */
|
|
{
|
|
/* Send MO5 */
|
|
message = 0x07; /* M */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = 0x0F; /* O */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
message = TX5; /* 5 make A0 so to get around space issue */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
handleLinkBusMsgs();
|
|
}
|
|
break;
|
|
} /* end of fox # case test */
|
|
} /* end of mox loop */
|
|
|
|
if((seconds > 50) && (seconds < 55)) /* a window to enter */
|
|
{
|
|
/* Signal the end of the cycle
|
|
* send a letter C then do the end of cycle tone */
|
|
message = 0x15; /*letter C */
|
|
Send_Message(message, speed);
|
|
word_space(speed);
|
|
EOC_KEY = 1; /* force a key down for end of cycle */
|
|
/*
|
|
* To do. For the Arduino version its possable to add a low battery monitor
|
|
* that will turn off the long tone at the end of the cycle to
|
|
* aleart the event crew to swap out battery.
|
|
* */
|
|
|
|
/*******************************************
|
|
**
|
|
** send the long tone at the
|
|
** end of an ARDF cycle
|
|
**
|
|
******************************************* */
|
|
if(g_enable_LEDs)
|
|
{
|
|
digitalWrite(13, HIGH); /*arduino turn on end of cycle LED */
|
|
}
|
|
digitalWrite(2, HIGH); /*TX key line. Force a EOC key down */
|
|
while(EOC_KEY)
|
|
{
|
|
/* key down transmitter for end of cycle tone after letter C
|
|
* KEYdown was set before entering this loop
|
|
* just spend some time here until done */
|
|
|
|
handleLinkBusMsgs();
|
|
}
|
|
|
|
digitalWrite(13, LOW); /* arduino turn off end of cycle LED */
|
|
digitalWrite(2, LOW); /* turn off TX key line */
|
|
} /* end of > 50 second checker */
|
|
} /* end of while active loop */
|
|
|
|
/* a trap to walk us through demo mode by cycling fox number */
|
|
if(demo == 1)
|
|
{
|
|
g_fox++; /* cycle to next one */
|
|
if(g_fox > 5)
|
|
{
|
|
g_fox = 1; /*reset to 1 */
|
|
}
|
|
}
|
|
|
|
/* below will flash LED when offcycle for a heartbeat indicator */
|
|
if(g_enable_LEDs)
|
|
{
|
|
delay(1000); /* delay between offf cycle flash events */
|
|
digitalWrite(13, HIGH); /* arduino flash led when off cycle turn it on */
|
|
delay(100); /* led on time */
|
|
digitalWrite(13, LOW); /* arduino flash led when off cycle turn it off */
|
|
}
|
|
|
|
handleLinkBusMsgs();
|
|
} /* end of while 1 loop */
|
|
}
|
|
|
|
int codeBits(char alphaNum)
|
|
{
|
|
switch(alphaNum)
|
|
{
|
|
case ' ':
|
|
{
|
|
return( ' ');
|
|
}
|
|
|
|
case '0': /* 0 ----- 00111111 0x3F */
|
|
{ return( 0x3F); }
|
|
|
|
case '1': /* 1 .---- 00111110 0x3E */
|
|
{ return( 0x3E); }
|
|
|
|
case '2': /* 2 ..--- 00111100 0x3C */
|
|
{ return( 0x3C); }
|
|
|
|
case '3': /* 3 ...-- 00111000 0x38 */
|
|
{ return( 0x38); }
|
|
|
|
case '4': /* 4 ....- 00110000 0x30 */
|
|
{ return( 0x30); }
|
|
|
|
case '5': /* 5 ..... 00100000 0x20 make 10100000 0xA0 so its not same as ASCII space and mask out upper bit and trap in send message to make back to 0x20*/
|
|
{ return( TX5); }
|
|
|
|
case '6': /* 6 -.... 00100001 0x21 */
|
|
{ return( 0x21); }
|
|
|
|
case '7': /* 7 --... 00100011 0x23 */
|
|
{ return( 0x23); }
|
|
|
|
case '8': /* 8 ---.. 00100111 0x27 */
|
|
{ return( 0x27); }
|
|
|
|
case '9': /* 9 ----. 00101111 0x2F */
|
|
{ return( 0x2F); }
|
|
|
|
case 'A': /* A .- 00000110 0x06 */
|
|
{ return( 0x06); }
|
|
|
|
case 'B': /* B -... 00010001 0x11 */
|
|
{ return( 0x11); }
|
|
|
|
case 'C': /* C -.-. 00010101 0x15 */
|
|
{ return( 0x15); }
|
|
|
|
case 'D': /* D -.. 00001001 0x09 */
|
|
{ return( 0x09); }
|
|
|
|
case 'E': /* E . 00000010 0x02 */
|
|
{ return( 0x02); }
|
|
|
|
case 'F': /* F ..-. 00010100 0x14 */
|
|
{ return( 0x14); }
|
|
|
|
case 'G': /* G --. 00001011 0x0B */
|
|
{ return( 0x0B); }
|
|
|
|
case 'H': /* H .... 00010000 0x10 */
|
|
{ return( 0x10); }
|
|
|
|
case 'I': /* I .. 00000100 0x04 */
|
|
{ return( 0x04); }
|
|
|
|
case 'J': /* J .--- 00011110 0x1E */
|
|
{ return( 0x1E); }
|
|
|
|
case 'K': /* K -.- 00001101 0x0D */
|
|
{ return( 0x0D); }
|
|
|
|
case 'L': /* L .-.. 00010010 0x12 */
|
|
{ return( 0x12); }
|
|
|
|
case 'M': /* M -- 00000111 0x07 */
|
|
{ return( 0x07); }
|
|
|
|
case 'N': /* N -. 00000101 0x05 */
|
|
{ return( 0x05); }
|
|
|
|
case 'O': /* O --- 00001111 0x0F */
|
|
{ return( 0x0F); }
|
|
|
|
case 'P': /* P .--. 00010110 0x16 */
|
|
{ return( 0x16); }
|
|
|
|
case 'Q': /* Q --.- 00011011 0x1B */
|
|
{ return( 0x1B); }
|
|
|
|
case 'R': /* R .-. 00001010 0x0A */
|
|
{ return( 0x0A); }
|
|
|
|
case 'S': /* S ... 00001000 0x08 */
|
|
{ return( 0x08); }
|
|
|
|
case 'T': /* T - 00000011 0x03 */
|
|
{ return( 0x03); }
|
|
|
|
case 'U': /* U ..- 00001100 0x0C */
|
|
{ return( 0x0C); }
|
|
|
|
case 'V': /* V ...- 00011000 0x18 */
|
|
{ return( 0x18); }
|
|
|
|
case 'W': /* W .-- 00001110 0x0E */
|
|
{ return( 0x0E); }
|
|
|
|
case 'X': /* X -..- 00011001 0x19 */
|
|
{ return( 0x19); }
|
|
|
|
case 'Y': /* Y -.-- 00011101 0x1D */
|
|
{ return( 0x1D); }
|
|
|
|
case 'Z': /* Z --.. 00010011 0x13 */
|
|
{ return( 0x13); }
|
|
|
|
case '?': /* ? ..--.. 01001100 0x49 Question mark */
|
|
{ return( 0x49); }
|
|
|
|
default:
|
|
return( 0);
|
|
}
|
|
}
|
|
|
|
/* 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:
|
|
{
|
|
#ifdef USE_WATCHDOG
|
|
wdt_init(WD_FORCE_RESET);
|
|
while(1)
|
|
{
|
|
;
|
|
}
|
|
#else
|
|
resetFunc(); /*call reset */
|
|
#endif /* USE_WATCHDOG */
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_OVERRIDE_DIP:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
int d = atoi(lb_buff->fields[FIELD1]);
|
|
|
|
if((d >= 0) && (d <= 7))
|
|
{
|
|
g_override_DIP_switches = d;
|
|
}
|
|
|
|
saveAllEEPROM();
|
|
}
|
|
|
|
sprintf(g_tempStr, "DIP=%u\n", g_override_DIP_switches);
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
g_enable_LEDs = TRUE;
|
|
}
|
|
|
|
saveAllEEPROM();
|
|
}
|
|
|
|
sprintf(g_tempStr, "LED:%s\n", g_enable_LEDs ? "ON" : "OFF");
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_SYNC_ENABLE:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
if((lb_buff->fields[FIELD1][1] == 'F') || (lb_buff->fields[FIELD1][0] == '0'))
|
|
{
|
|
g_enable_sync = FALSE;
|
|
}
|
|
else
|
|
{
|
|
g_enable_sync = TRUE;
|
|
}
|
|
|
|
saveAllEEPROM();
|
|
}
|
|
|
|
sprintf(g_tempStr, "SYN:%s\n", g_enable_sync ? "ON" : "OFF");
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_GO:
|
|
{
|
|
if(g_start_override)
|
|
{
|
|
linkbus_send_text((char*)"Already synced!\n");
|
|
}
|
|
else
|
|
{
|
|
g_start_override = TRUE;
|
|
linkbus_send_text((char*)"We're off!\n");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_FACTORY_RESET:
|
|
{
|
|
uint8_t flag = EEPROM_INITIALIZED_FLAG + 1;
|
|
eeprom_write_byte(&ee_interface_eeprom_initialization_flag, flag);
|
|
#ifdef USE_WATCHDOG
|
|
wdt_init(WD_FORCE_RESET);
|
|
while(1)
|
|
{
|
|
;
|
|
}
|
|
#else
|
|
resetFunc(); /*call reset */
|
|
#endif /* USE_WATCHDOG */
|
|
}
|
|
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;
|
|
}
|
|
|
|
saveAllEEPROM();
|
|
}
|
|
|
|
sprintf(g_tempStr, "Cal=%u\n", g_clock_calibration);
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_SET_STATION_ID:
|
|
{
|
|
if(lb_buff->fields[FIELD1][0])
|
|
{
|
|
strncpy(g_messages_text[STATION_ID], lb_buff->fields[FIELD1], MAX_PATTERN_TEXT_LENGTH);
|
|
|
|
if(g_messages_text[STATION_ID][0])
|
|
{
|
|
/* g_time_needed_for_ID = (500 + timeRequiredToSendStrAtWPM(g_messages_text[STATION_ID], g_id_codespeed)) / 1000; */
|
|
}
|
|
|
|
saveAllEEPROM();
|
|
}
|
|
|
|
sprintf(g_tempStr, "ID:%s\n", g_messages_text[STATION_ID]);
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_VERSION:
|
|
{
|
|
sprintf(g_tempStr, "SW Ver:%s\n", SW_REVISION);
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
case MESSAGE_TEMP:
|
|
{
|
|
float temp = getTemp();
|
|
sprintf(g_tempStr, "Temp: %dC\n", (int)temp);
|
|
linkbus_send_text(g_tempStr);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
lb_send_Help();
|
|
}
|
|
break;
|
|
}
|
|
|
|
lb_buff->id = (LBMessageID)MESSAGE_EMPTY;
|
|
if(send_ack)
|
|
{
|
|
lb_send_NewPrompt();
|
|
}
|
|
}
|
|
}
|
|
|
|
void transmitString(char msg[], int speed)
|
|
{
|
|
int len = strlen(msg);
|
|
|
|
for(int i = 0; i < len; i++)
|
|
{
|
|
Send_Message(codeBits(msg[i]), speed);
|
|
word_space(speed);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set EEPROM to its default values
|
|
*/
|
|
void initializeEEPROMVars()
|
|
{
|
|
uint8_t i;
|
|
|
|
if(eeprom_read_byte(&ee_interface_eeprom_initialization_flag) == EEPROM_INITIALIZED_FLAG)
|
|
{
|
|
g_pattern_codespeed = eeprom_read_byte(&ee_pattern_codespeed);
|
|
g_id_codespeed = eeprom_read_byte(&ee_id_codespeed);
|
|
g_ID_period_seconds = eeprom_read_word(&ee_ID_time);
|
|
g_clock_calibration = eeprom_read_word(&ee_clock_calibration);
|
|
g_override_DIP_switches = eeprom_read_byte(&ee_override_DIP_switches);
|
|
g_enable_LEDs = eeprom_read_byte(&ee_enable_LEDs);
|
|
g_enable_sync = eeprom_read_byte(&ee_enable_sync);
|
|
|
|
for(i = 0; i < 20; 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;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < 20; i++)
|
|
{
|
|
g_messages_text[PATTERN_TEXT][i] = (char)eeprom_read_byte((uint8_t*)(&ee_pattern_text[i]));
|
|
if(!g_messages_text[PATTERN_TEXT][i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_id_codespeed = EEPROM_ID_CODE_SPEED_DEFAULT;
|
|
g_pattern_codespeed = EEPROM_PATTERN_CODE_SPEED_DEFAULT;
|
|
g_ID_period_seconds = EEPROM_ID_TIME_INTERVAL_DEFAULT;
|
|
g_clock_calibration = EEPROM_CLOCK_CALIBRATION_DEFAULT;
|
|
g_override_DIP_switches = EEPROM_OVERRIDE_DIP_SW_DEFAULT;
|
|
g_enable_LEDs = EEPROM_ENABLE_LEDS_DEFAULT;
|
|
g_enable_sync = EEPROM_ENABLE_SYNC_DEFAULT;
|
|
strncpy(g_messages_text[STATION_ID], EEPROM_STATION_ID_DEFAULT, MAX_PATTERN_TEXT_LENGTH);
|
|
strncpy(g_messages_text[PATTERN_TEXT], EEPROM_PATTERN_TEXT_DEFAULT, MAX_PATTERN_TEXT_LENGTH);
|
|
saveAllEEPROM();
|
|
eeprom_write_byte(&ee_interface_eeprom_initialization_flag, EEPROM_INITIALIZED_FLAG);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save all non-volatile values to EEPROM
|
|
*/
|
|
void saveAllEEPROM()
|
|
{
|
|
uint8_t i;
|
|
|
|
eeprom_update_byte(&ee_id_codespeed, g_id_codespeed);
|
|
eeprom_update_byte(&ee_pattern_codespeed, g_pattern_codespeed);
|
|
eeprom_update_word(&ee_ID_time, g_ID_period_seconds);
|
|
eeprom_update_word(&ee_clock_calibration, g_clock_calibration);
|
|
eeprom_update_byte(&ee_override_DIP_switches, g_override_DIP_switches);
|
|
eeprom_update_byte(&ee_enable_LEDs, g_enable_LEDs);
|
|
eeprom_update_byte(&ee_enable_sync, g_enable_sync);
|
|
|
|
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);
|
|
|
|
for(i = 0; i < strlen(g_messages_text[PATTERN_TEXT]); i++)
|
|
{
|
|
eeprom_update_byte((uint8_t*)&ee_pattern_text[i], (uint8_t)g_messages_text[PATTERN_TEXT][i]);
|
|
}
|
|
|
|
eeprom_update_byte((uint8_t*)&ee_pattern_text[i], 0);
|
|
}
|
|
|
|
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(200); /* wait for voltages to become stable. */
|
|
|
|
ADCSRA |= (1 << ADSC); /* Start the ADC */
|
|
|
|
readADC();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
float getTemp(void)
|
|
{
|
|
/* The offset (first term) was determined empirically */
|
|
readADC(); /* throw away first reading */
|
|
return(14.7 + (readADC() - 324.31) / 1.22);
|
|
}
|