diff --git a/vhf-allmode-microfox.ino b/Arduino-microfox.ino similarity index 78% rename from vhf-allmode-microfox.ino rename to Arduino-microfox.ino index 721ac51..231aa78 100644 --- a/vhf-allmode-microfox.ino +++ b/Arduino-microfox.ino @@ -1,1360 +1,1332 @@ - -/* */ -/* 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 */ - -#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 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 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); - -#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 */ - 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); - - /* 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. - * */ - - linkbus_send_text((char*)"Waiting for sync.\n"); - linkbus_send_text((char*)"Type \"$GO;\"\n"); - - 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 */ - } - handleLinkBusMsgs(); - } - - 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 uint32_t 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 = 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 = LINKBUS_MSG_UNKNOWN; - - 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); - } - } -// } -// else -// { -// if((rx_char == '$') || (rx_char == '!')) /* start of new message = $ */ -// { -// charIndex = 0; -// buff->type = (rx_char == '!') ? LINKBUS_MSG_REPLY : LINKBUS_MSG_COMMAND; -// field_len = 0; -// msg_ID = LINKBUS_MSG_UNKNOWN; -// receiving_msg = TRUE; -// -// /* Empty the field buffers */ -// for(field_index = 0; field_index < LINKBUS_MAX_MSG_NUMBER_OF_FIELDS; field_index++) -// { -// buff->fields[field_index][0] = '\0'; -// } -// -// field_index = 0; -// } -// else if(receiving_msg) -// { -// if((rx_char == ',') || (rx_char == ';') || (rx_char == '?')) /* new field = ,; end of message = ; */ -// { -// /* if(field_index == 0) // message ID received */ -// if(field_index > 0) -// { -// buff->fields[field_index - 1][field_len] = 0; -// } -// -// field_index++; -// field_len = 0; -// -// if(rx_char == ';') -// { -// if(charIndex > LINKBUS_MIN_MSG_LENGTH) -// { -// buff->id = msg_ID; -// } -// receiving_msg = FALSE; -// } -// else if(rx_char == '?') -// { -// buff->type = LINKBUS_MSG_QUERY; -// if(charIndex >= LINKBUS_MIN_MSG_LENGTH) -// { -// buff->id = msg_ID; -// } -// receiving_msg = FALSE; -// } -// -// if(!receiving_msg) -// { -// buff = 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; -// } -// } -// } -// else if(rx_char == 0x0D) /* Handle carriage return */ -// { -// buff->id = LINKBUS_MSG_UNKNOWN; -// charIndex = LINKBUS_MAX_MSG_LENGTH; -// field_len = 0; -// msg_ID = LINKBUS_MSG_UNKNOWN; -// field_index = 0; -// buff = NULL; -// } -// -// if(++charIndex >= LINKBUS_MAX_MSG_LENGTH) -// { -// receiving_msg = FALSE; -// charIndex = 0; -// } -// } - } -} - - -/*********************************************************************** - * 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) - { - - 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 = 0x20; /* 5 */ - 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 */ - { return( 0x20); } - - 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); - send_ack = 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; - } - else - { - g_enable_LEDs = TRUE; - } - - saveAllEEPROM(); - } - - sprintf(g_tempStr, "LED:%s\n", g_enable_LEDs ? "ON" : "OFF"); - linkbus_send_text(g_tempStr); - send_ack = FALSE; - } - break; - - case MESSAGE_TX_POWER: - { - /* Your code goes here */ - } - break; - - case MESSAGE_PERM: - { - saveAllEEPROM(); - } - 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"); - } - send_ack = FALSE; - } - 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); - send_ack = FALSE; - } - 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); - send_ack = FALSE; - } - break; - - case MESSAGE_CODE_SPEED: - { - /* Your code goes here */ - } - break; - - case MESSAGE_SET_PATTERN: - { - if(lb_buff->fields[FIELD1][0]) - { - strncpy(g_messages_text[PATTERN_TEXT], lb_buff->fields[FIELD1], MAX_PATTERN_TEXT_LENGTH); - } - } - break; - - case MESSAGE_VERSION: - { - sprintf(g_tempStr, "SW Ver:%u\n", SW_REVISION); - linkbus_send_text(g_tempStr); - send_ack = FALSE; - } - break; - - case MESSAGE_BAT: - { - /* Your code goes here */ - } - break; - - case MESSAGE_TEMP: - { - /* Your code goes here */ - } - break; - - default: - { - linkbus_reset_rx(); /* flush buffer */ - g_last_error_code = ERROR_CODE_ILLEGAL_COMMAND_RCVD; - } - break; - } - - lb_buff->id = (LBMessageID)MESSAGE_EMPTY; - if(send_ack) - { - linkbus_send_text((char*)MESSAGE_ACK); - } - } -} - -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); - - 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; - 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); - - 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); -} +/* + * 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); +} diff --git a/defs.h b/defs.h index 654e61f..f639927 100644 --- a/defs.h +++ b/defs.h @@ -36,7 +36,7 @@ //#define TRANQUILIZE_WATCHDOG #define PRODUCT_NAME_SHORT "ARDF Tx" -#define PRODUCT_NAME_LONG "ARDF Dual-Band Transmitter" +#define PRODUCT_NAME_LONG "WB8WFK ARDF Transmitter" /*******************************************************/ @@ -65,6 +65,14 @@ typedef unsigned char uint8_t; #define BIAS_DAC DAC081C_I2C_SLAVE_ADDR_A2 #endif +typedef enum { +// TX1 = 0xA4, +// TX2 = 0xA3, +// TX3 = 0xA2, +// TX4 = 0xA1, + TX5 = 0xA0 + } Tx_t; + /*******************************************************/ /* Error Codes */ /*******************************************************/ @@ -157,7 +165,7 @@ typedef uint16_t BatteryLevel; /* in milliVolts */ /****************************************************** * EEPROM definitions */ -#define EEPROM_INITIALIZED_FLAG 0xAC +#define EEPROM_INITIALIZED_FLAG 0xAD #define EEPROM_UNINITIALIZED 0x00 #define EEPROM_STATION_ID_DEFAULT "FOXBOX" @@ -175,6 +183,7 @@ typedef uint16_t BatteryLevel; /* in milliVolts */ #define EEPROM_CLOCK_CALIBRATION_DEFAULT 15629 #define EEPROM_OVERRIDE_DIP_SW_DEFAULT 0 #define EEPROM_ENABLE_LEDS_DEFAULT 1 +#define EEPROM_ENABLE_SYNC_DEFAULT 1 #define EEPROM_SI5351_CALIBRATION_DEFAULT 0x00 #define EEPROM_CLK0_OUT_DEFAULT 133000000 diff --git a/linkbus.cpp b/linkbus.cpp index b00c890..d1b2fc8 100644 --- a/linkbus.cpp +++ b/linkbus.cpp @@ -25,7 +25,6 @@ #include "linkbus.h" #include "defs.h" - //#include "util.h" //#include //#include @@ -35,7 +34,18 @@ static volatile BOOL g_bus_disabled = TRUE; static const char crlf[] = "\n"; static char lineTerm[8] = "\n"; -static const char textPrompt[] = "TX> "; +static const char textPrompt[] = "> "; + +static const char textHelp[][LINKBUS_MAX_TX_MSG_LENGTH] = { "\nCommands:\n", + " CAL - Calibrate\n", + " DIP - Override DIP\n", + " FAC - Factory reset\n", + " GO - Sync clock\n", + " ID - Set callsign\n", + " LED - LED on/off\n", + " RST - Reset\n", + " SYN - Sync on/off\n", + " VER - S/W version\n" }; static char g_tempMsgBuff[LINKBUS_MAX_MSG_LENGTH]; @@ -43,124 +53,124 @@ static char g_tempMsgBuff[LINKBUS_MAX_MSG_LENGTH]; BOOL linkbus_start_tx(void); /* Module global variables */ -static volatile BOOL linkbus_tx_active = FALSE; // volatile is required to ensure optimizer handles this properly +static volatile BOOL linkbus_tx_active = FALSE; /* volatile is required to ensure optimizer handles this properly */ static LinkbusTxBuffer tx_buffer[LINKBUS_NUMBER_OF_TX_MSG_BUFFERS]; static LinkbusRxBuffer rx_buffer[LINKBUS_NUMBER_OF_RX_MSG_BUFFERS]; LinkbusTxBuffer* nextFullTxBuffer(void) { - BOOL found = TRUE; - static uint8_t bufferIndex = 0; - uint8_t count = 0; + BOOL found = TRUE; + static uint8_t bufferIndex = 0; + uint8_t count = 0; - while(tx_buffer[bufferIndex][0] == '\0') - { - if(++count >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) - { - found = FALSE; - break; - } + while(tx_buffer[bufferIndex][0] == '\0') + { + if(++count >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) + { + found = FALSE; + break; + } - bufferIndex++; - if(bufferIndex >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) - { - bufferIndex = 0; - } - } + bufferIndex++; + if(bufferIndex >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) + { + bufferIndex = 0; + } + } - if(found) - { - return( &tx_buffer[bufferIndex]); - } + if(found) + { + return( &tx_buffer[bufferIndex]); + } - return(null); + return(null); } LinkbusTxBuffer* nextEmptyTxBuffer(void) { - BOOL found = TRUE; - static uint8_t bufferIndex = 0; - uint8_t count = 0; + BOOL found = TRUE; + static uint8_t bufferIndex = 0; + uint8_t count = 0; - while(tx_buffer[bufferIndex][0] != '\0') - { - if(++count >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) - { - found = FALSE; - break; - } + while(tx_buffer[bufferIndex][0] != '\0') + { + if(++count >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) + { + found = FALSE; + break; + } - bufferIndex++; - if(bufferIndex >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) - { - bufferIndex = 0; - } - } + bufferIndex++; + if(bufferIndex >= LINKBUS_NUMBER_OF_TX_MSG_BUFFERS) + { + bufferIndex = 0; + } + } - if(found) - { - return( &tx_buffer[bufferIndex]); - } + if(found) + { + return( &tx_buffer[bufferIndex]); + } - return(null); + return(null); } LinkbusRxBuffer* nextEmptyRxBuffer(void) { - BOOL found = TRUE; - static uint8_t bufferIndex = 0; - uint8_t count = 0; + BOOL found = TRUE; + static uint8_t bufferIndex = 0; + uint8_t count = 0; - while(rx_buffer[bufferIndex].id != MESSAGE_EMPTY) - { - if(++count >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) - { - found = FALSE; - break; - } + while(rx_buffer[bufferIndex].id != MESSAGE_EMPTY) + { + if(++count >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) + { + found = FALSE; + break; + } - bufferIndex++; - if(bufferIndex >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) - { - bufferIndex = 0; - } - } + bufferIndex++; + if(bufferIndex >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) + { + bufferIndex = 0; + } + } - if(found) - { - return( &rx_buffer[bufferIndex]); - } + if(found) + { + return( &rx_buffer[bufferIndex]); + } - return(null); + return(null); } LinkbusRxBuffer* nextFullRxBuffer(void) { - BOOL found = TRUE; - static uint8_t bufferIndex = 0; - uint8_t count = 0; + BOOL found = TRUE; + static uint8_t bufferIndex = 0; + uint8_t count = 0; - while(rx_buffer[bufferIndex].id == MESSAGE_EMPTY) - { - if(++count >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) - { - found = FALSE; - break; - } + while(rx_buffer[bufferIndex].id == MESSAGE_EMPTY) + { + if(++count >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) + { + found = FALSE; + break; + } - bufferIndex++; - if(bufferIndex >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) - { - bufferIndex = 0; - } - } + bufferIndex++; + if(bufferIndex >= LINKBUS_NUMBER_OF_RX_MSG_BUFFERS) + { + bufferIndex = 0; + } + } - if(found) - { - return( &rx_buffer[bufferIndex]); - } + if(found) + { + return( &rx_buffer[bufferIndex]); + } - return(null); + return(null); } @@ -169,125 +179,131 @@ LinkbusRxBuffer* nextFullRxBuffer(void) ************************************************************************/ BOOL linkbusTxInProgress(void) { - return(linkbus_tx_active); + return(linkbus_tx_active); } BOOL linkbus_start_tx(void) { - BOOL success = !linkbus_tx_active; + BOOL success = !linkbus_tx_active; - if(success) /* message will be lost if transmit is busy */ - { - linkbus_tx_active = TRUE; - UCSR0B |= (1 << UDRIE0); - } + if(success) /* message will be lost if transmit is busy */ + { + linkbus_tx_active = TRUE; + UCSR0B |= (1 << UDRIE0); + } - return(success); + return(success); } void linkbus_end_tx(void) { - if(linkbus_tx_active) - { - UCSR0B &= ~(1 << UDRIE0); - linkbus_tx_active = FALSE; - } + if(linkbus_tx_active) + { + UCSR0B &= ~(1 << UDRIE0); + linkbus_tx_active = FALSE; + } } void linkbus_reset_rx(void) { - if(UCSR0B & (1 << RXEN0)) /* perform only if rx is currently enabled */ - { - UCSR0B &= ~(1 << RXEN0); + if(UCSR0B & (1 << RXEN0)) /* perform only if rx is currently enabled */ + { + UCSR0B &= ~(1 << RXEN0); /* uint16_t s = sizeof(rx_buffer); // test */ - memset(rx_buffer, 0, sizeof(rx_buffer)); + memset(rx_buffer, 0, sizeof(rx_buffer)); /* if(s) s = 0; // test */ - UCSR0B |= (1 << RXEN0); - } + UCSR0B |= (1 << RXEN0); + } } void linkbus_init(uint32_t baud) { - memset(rx_buffer, 0, sizeof(rx_buffer)); + memset(rx_buffer, 0, sizeof(rx_buffer)); - for(int bufferIndex=0; bufferIndex> 8); - UBRR0L = (uint8_t)myubrr; - /* Enable receiver and transmitter and related interrupts */ - UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); -/* UCSR0B = (1<> 8); + UBRR0L = (uint8_t)myubrr; + /* Enable receiver and transmitter and related interrupts */ + UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); +/* UCSR0B = (1< LINKBUS_MAX_MSG_LENGTH) return TRUE; + if(str == NULL) + { + return( TRUE); + } + if(strlen(str) > LINKBUS_MAX_MSG_LENGTH) + { + return( TRUE); + } strncpy(g_tempMsgBuff, str, LINKBUS_MAX_MSG_LENGTH); linkbus_send_text(g_tempMsgBuff); - return FALSE; + return( FALSE); } void lb_send_value(uint16_t value, char* label) @@ -377,3 +402,41 @@ void lb_send_value(uint16_t value, char* label) sprintf(g_tempMsgBuff, "> %s=%d%s", label, value, lineTerm); linkbus_send_text(g_tempMsgBuff); } + +/*********************************************************************** + * lb_send_Help(void) + ************************************************************************/ +void lb_send_Help(void) +{ + if(g_bus_disabled) + { + return; + } + + sprintf(g_tempMsgBuff, "\n*** %s Ver. %s ***", PRODUCT_NAME_LONG, SW_REVISION); + + while(linkbus_send_text(g_tempMsgBuff)) + { + ; + } + while(linkbusTxInProgress()) + { + ; + } + + size_t n = sizeof(textHelp) / sizeof(textHelp[0]); + for(uint8_t i = 0; i < n; i++) + { + while(linkbus_send_text((char*)textHelp[i])) + { + ; + } + while(linkbusTxInProgress()) + { + ; + } + } + + lb_send_NewLine(); +} + diff --git a/linkbus.h b/linkbus.h index dbf4e92..b8c6e76 100644 --- a/linkbus.h +++ b/linkbus.h @@ -35,7 +35,8 @@ #define LINKBUS_MAX_MSG_FIELD_LENGTH 21 #define LINKBUS_MAX_MSG_NUMBER_OF_FIELDS 3 #define LINKBUS_NUMBER_OF_RX_MSG_BUFFERS 2 -#define LINKBUS_NUMBER_OF_TX_MSG_BUFFERS 8 +#define LINKBUS_MAX_TX_MSG_LENGTH 40 +#define LINKBUS_NUMBER_OF_TX_MSG_BUFFERS 10 #define LINKBUS_POWERUP_DELAY_SECONDS 6 @@ -97,21 +98,10 @@ typedef enum MESSAGE_FACTORY_RESET = 'F' * 100 + 'A' * 10 + 'C', /* Sets EEPROM back to defaults */ MESSAGE_OVERRIDE_DIP = 'D' *100 + 'I' * 10 + 'P', /* Override DIP switch settings using this value */ MESSAGE_LEDS = 'L' * 100 + 'E' * 10 + 'D', /* Turn on or off LEDs - accepts 1 or 0 or ON or OFF */ - MESSAGE_CLOCK = 'T' * 100 + 'I' * 10 + 'M', /* Sets/reads the real-time clock */ - MESSAGE_STARTFINISH = 'S' * 10 + 'F', /* Sets the start and finish times */ - MESSAGE_BAT = 'B' * 100 + 'A' * 10 + 'T', /* Battery charge data */ + MESSAGE_SYNC_ENABLE = 'S' * 100 + 'Y' * 10 + 'N', /* Enable or disable transmitter syncing */ MESSAGE_TEMP = 'T' * 100 + 'E' * 10 + 'M', /* Temperature data */ - MESSAGE_PERM = 'P' * 100 + 'R' * 10 + 'M', /* Saves most settings to EEPROM "perm" */ - MESSAGE_TX_POWER = 'P' * 100 + 'O' * 10 + 'W', /* Sets transmit power level */ - MESSAGE_TX_MOD = 'M' * 100 + 'O' * 10 + 'D', /* Sets 2m modulation format to AM or CW */ -#ifdef DONOTUSE - MESSAGE_DRIVE_LEVEL = 'D' * 100 + 'R' * 10 + 'I', /* Adjust 2m drive level */ -#endif // DONOTUSE MESSAGE_SET_STATION_ID = 'I' * 10 + 'D', /* Sets amateur radio callsign text */ - MESSAGE_SET_PATTERN = 'P' * 10 + 'A', /* Sets unique transmit pattern */ - MESSAGE_CODE_SPEED = 'S' * 100 + 'P' * 10 + 'D', /* Sets id and pattern code speeds */ - MESSAGE_ESP_COMM = 'E' * 100 + 'S' * 10 + 'P', /* Communications with ESP8266 controller */ - MESSAGE_GO = 'G' * 10 + 'O', /* Start transmitting now without delay */ + MESSAGE_GO = 'G' * 10 + 'O', /* Synchronizes clock */ /* UTILITY MESSAGES */ MESSAGE_RESET = 'R' * 100 + 'S' * 10 + 'T', /* Processor reset */ @@ -120,14 +110,6 @@ typedef enum INVALID_MESSAGE = UINT16_MAX /* This value must never overlap a valid message ID */ } LBMessageID; -#define MESSAGE_CLOCK_LABEL "TIM" -#define MESSAGE_ESP_LABEL "ESP" -#define MESSAGE_ERRORCODE_LABEL "EC" -#define MESSAGE_STATUSCODE_LABEL "SC" -#define MESSAGE_BAND_LABEL "BND" -#define MESSAGE_TX_POWER_LABEL "POW" -#define MESSAGE_ACK "!ACK;" - typedef enum { LINKBUS_MSG_UNKNOWN = 0, @@ -161,7 +143,7 @@ typedef enum TRANSMITTER_ID = 3 } DeviceID; -typedef char LinkbusTxBuffer[LINKBUS_MAX_MSG_LENGTH]; +typedef char LinkbusTxBuffer[LINKBUS_MAX_TX_MSG_LENGTH]; typedef struct { @@ -263,4 +245,8 @@ BOOL lb_send_string(char* str); */ void lb_send_value(uint16_t value, char* label); +/** + */ +void lb_send_Help(void); + #endif /* LINKBUS_H_ */