//Firmware Version
//+ : This symbol identifies the firmware. 
//    It was originally called 'CEC V1.072' but it is too long to waste the LCD window.
//    I do not want to make this Firmware users's uBITX messy with my callsign.
//    Putting one alphabet in front of 'v' has a different meaning.
//    So I put + in the sense that it was improved one by one based on Original Firmware.
//    This firmware has been gradually changed based on the original firmware created by Farhan, Jack, Jerry and others.

#define FIRMWARE_VERSION_INFO F("+v1.200")  
#define FIRMWARE_VERSION_NUM 0x04       //1st Complete Project : 1 (Version 1.061), 2st Project : 2, 1.08: 3, 1.09 : 4

/**
 Cat Suppoort uBITX CEC Version
 This firmware has been gradually changed based on the original firmware created by Farhan, Jack, Jerry and others.
 Most features(TX, Frequency Range, Ham Band, TX Control, CW delay, start Delay... more) have been added by KD8CEC.
 My wish is to keep the original author's Comment as long as the meaning does not change much, even if the code looks a bit long.
 Ian KD8CEC

 Original source comment            -------------------------------------------------------------
 * This source file is under General Public License version 3.
 * 
 * This verision uses a built-in Si5351 library
 * Most source code are meant to be understood by the compilers and the computers. 
 * Code that has to be hackable needs to be well understood and properly documented. 
 * Donald Knuth coined the term Literate Programming to indicate code that is written be 
 * easily read and understood.
 * 
 * The Raduino is a small board that includes the Arduin Nano, a 16x2 LCD display and
 * an Si5351a frequency synthesizer. This board is manufactured by Paradigm Ecomm Pvt Ltd
 * 
 * To learn more about Arduino you may visit www.arduino.cc. 
 * 
 * The Arduino works by starts executing the code in a function called setup() and then it 
 * repeatedly keeps calling loop() forever. All the initialization code is kept in setup()
 * and code to continuously sense the tuning knob, the function button, transmit/receive,
 * etc is all in the loop() function. If you wish to study the code top down, then scroll
 * to the bottom of this file and read your way up.
 * 
 * Below are the libraries to be included for building the Raduino 
 * The EEPROM library is used to store settings like the frequency memory, caliberation data, 
 * callsign etc .
 *
 *  The main chip which generates upto three oscillators of various frequencies in the
 *  Raduino is the Si5351a. To learn more about Si5351a you can download the datasheet 
 *  from www.silabs.com although, strictly speaking it is not a requirment to understand this code. 
 *  Instead, you can look up the Si5351 library written by xxx, yyy. You can download and 
 *  install it from www.url.com to complile this file.
 *  The Wire.h library is used to talk to the Si5351 and we also declare an instance of 
 *  Si5351 object to control the clocks.
 */
#include <Wire.h>
#include <EEPROM.h>
#include "ubitx.h"
#include "ubitx_eemap.h"

/**
 * The uBITX is an upconnversion transceiver. The first IF is at 45 MHz.
 * The first IF frequency is not exactly at 45 Mhz but about 5 khz lower,
 * this shift is due to the loading on the 45 Mhz crystal filter by the matching
 * L-network used on it's either sides.
 * The first oscillator works between 48 Mhz and 75 MHz. The signal is subtracted
 * from the first oscillator to arriive at 45 Mhz IF. Thus, it is inverted : LSB becomes USB
 * and USB becomes LSB.
 * The second IF of 12 Mhz has a ladder crystal filter. If a second oscillator is used at 
 * 57 Mhz, the signal is subtracted FROM the oscillator, inverting a second time, and arrives 
 * at the 12 Mhz ladder filter thus doouble inversion, keeps the sidebands as they originally were.
 * If the second oscillator is at 33 Mhz, the oscilaltor is subtracated from the signal, 
 * thus keeping the signal's sidebands inverted. The USB will become LSB.
 * We use this technique to switch sidebands. This is to avoid placing the lsbCarrier close to
 * 12 MHz where its fifth harmonic beats with the arduino's 16 Mhz oscillator's fourth harmonic
 */

// the second oscillator should ideally be at 57 MHz, however, the crystal filter's center frequency 
// is shifted down a little due to the loading from the impedance matching L-networks on either sides

#if UBITX_BOARD_VERSION == 5
//For Test  //45005000
  //#define SECOND_OSC_USB (56064200l)
  //#define SECOND_OSC_LSB (33945800l) 

/*
  //For Test //4500000
  #define SECOND_OSC_USB (56059200l)
  #define SECOND_OSC_LSB (33940800l) 
*/

/*
  //For Test // V1.121  44991500(LSB), 44998500 (USB), abs : 7k
  #define SECOND_OSC_USB (56057700l)
  #define SECOND_OSC_LSB (33932300l) 
*/

  //==============================================================================================================================
  //For Test // V1.200 V1.122  45002500 (LSB), 45002000 (USB) (Change Default BFO Frequency 11056xxx, adjust bfo and ifshift ), abs: 0.5k
  //Best, Test 3 uBITX V5
  //Last Value, If more data is collected, it can be changed to a better value.
  #define SECOND_OSC_USB (56058700l)
  #define SECOND_OSC_LSB (33945800l) 

  //Not used, Just comment (Default)
  #define INIT_USB_FREQ   (11056500l)
  //-----------------------------------------------------------------------------------------------------------------------------
#else
  #define SECOND_OSC_USB (56995000l)
  #define SECOND_OSC_LSB (32995000l) 
  //these are the two default USB and LSB frequencies. The best frequencies depend upon your individual taste and filter shape
  //Not used, Just comment (Default)
  #define INIT_USB_FREQ   (11996500l)
#endif
  

// limits the tuning and working range of the ubitx between 3 MHz and 30 MHz
#define LOWEST_FREQ  (3000000l)
#define HIGHEST_FREQ (30000000l)

//When the frequency is moved by the dial, the maximum value by KD8CEC
#define LOWEST_FREQ_DIAL  (3000l)
#define HIGHEST_FREQ_DIAL (60000000l)

char ritOn = 0;
char vfoActive = VFO_A;
int8_t meter_reading = 0; // a -1 on meter makes it invisible
unsigned long vfoA=7150000L, vfoB=14200000L, sideTone=800, usbCarrier, cwmCarrier;
unsigned long vfoA_eeprom, vfoB_eeprom; //for protect eeprom life
unsigned long frequency;
unsigned long ritRxFrequency, ritTxFrequency;  //frequency is the current frequency on the dial


unsigned int cwSpeed = 100; //this is actuall the dot period in milliseconds
extern int32_t calibration;

//for store the mode in eeprom
byte vfoA_mode=0, vfoB_mode = 0;          //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM
byte vfoA_mode_eeprom, vfoB_mode_eeprom;  //for protect eeprom life

//KD8CEC
//for AutoSave and protect eeprom life
byte saveIntervalSec = 10;  //second
unsigned long saveCheckTime = 0;
unsigned long saveCheckFreq = 0;

byte cwDelayTime = 60;
byte delayBeforeCWStartTime = 50;

//sideTonePitch + sideToneSub = sideTone
byte sideTonePitch=0;
byte sideToneSub = 0;

//DialLock
byte isDialLock = 0;  //000000[0]vfoB [0]vfoA 0Bit : A, 1Bit : B
byte isTxType = 0;    //000000[0 - isSplit] [0 - isTXStop]
long arTuneStep[5];
byte tuneStepIndex; //default Value 0, start Offset is 0 because of check new user

byte commonOption0 = 0;
byte displayOption1 = 0;
byte displayOption2 = 0;

//CW ADC Range
int cwAdcSTFrom = 0;
int cwAdcSTTo = 0;
int cwAdcDotFrom = 0;
int cwAdcDotTo = 0;
int cwAdcDashFrom = 0;
int cwAdcDashTo = 0;
int cwAdcBothFrom = 0;
int cwAdcBothTo = 0;
byte cwKeyType = 0; //0: straight, 1 : iambica, 2: iambicb
bool Iambic_Key = true;
#define IAMBICB 0x10 // 0 for Iambic A, 1 for Iambic B
unsigned char keyerControl = IAMBICB;

byte isShiftDisplayCWFreq = 1;  //Display Frequency 
int shiftDisplayAdjustVal = 0;  //

//Variables for auto cw mode
byte isCWAutoMode = 0;          //0 : none, 1 : CW_AutoMode_Menu_Selection, 2 : CW_AutoMode Sending
byte cwAutoTextCount = 0;       //cwAutoText Count
byte beforeCWTextIndex = 255;   //when auto cw start, always beforeCWTextIndex = 255, (for first time check)
byte cwAutoDialType = 0;        //0 : CW Text Change, 1 : Frequency Tune

#define AUTO_CW_RESERVE_MAX 3
byte autoCWSendReserv[AUTO_CW_RESERVE_MAX]; //Reserve CW Auto Send
byte autoCWSendReservCount = 0;             //Reserve CW Text Cound
byte sendingCWTextIndex = 0;                //cw auto seding Text Index

byte userCallsignLength = 0;    //7 : display callsign at system startup, 6~0 : callsign length (range : 1~18)

/**
 * Raduino needs to keep track of current state of the transceiver. These are a few variables that do it
 */
boolean txCAT = false;        //turned on if the transmitting due to a CAT command
char inTx = 0;                //it is set to 1 if in transmit mode (whatever the reason : cw, ptt or cat)
char splitOn = 0;             //working split, uses VFO B as the transmit frequency
char keyDown = 0;             //in cw mode, denotes the carrier is being transmitted
char isUSB = 0;               //upper sideband was selected, this is reset to the default for the 

char cwMode = 0;              //compatible original source, and extend mode //if cwMode == 0, mode check : isUSB, cwMode > 0, mode Check : cwMode
                              //iscwMode = 0 : ssbmode, 1 :cwl, 2 : cwu, 3 : cwn (none tx)
                              
                              //frequency when it crosses the frequency border of 10 MHz
byte menuOn = 0;              //set to 1 when the menu is being displayed, if a menu item sets it to zero, the menu is exited
unsigned long cwTimeout = 0;  //milliseconds to go before the cw transmit line is released and the radio goes back to rx mode
unsigned long dbgCount = 0;   //not used now
unsigned char txFilter = 0;   //which of the four transmit filters are in use
boolean modeCalibrate = false;//this mode of menus shows extended menus to calibrate the oscillators and choose the proper
                              //beat frequency
                              
byte advancedFreqOption1;     //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2~Bit3 : dynamic sdr frequency,  bit 7: IFTune_Value Reverse for DIY uBITX
byte attLevel = 0;            //ATT : RF Gain Control (Receive) <-- IF1 Shift, 0 : Off, ShiftValue is attLevel * 100; attLevel 150 = 15K
byte if1TuneValue = 0;        //0 : OFF, IF1 + if1TuneValue * 100; // + - 12500;
byte sdrModeOn = 0;           //SDR MODE ON / OFF
unsigned long SDR_Center_Freq; //

unsigned long beforeIdle_ProcessTime = 0; //for check Idle time
byte line2DisplayStatus = 0;  //0:Clear, 1 : menu, 1: DisplayFrom Idle, 
char lcdMeter[17];
byte sMeterLevels[9];

//Current ADC Value for S.Meter, and S Meter Level
int currentSMeter = 0;
byte scaledSMeter = 0;

byte I2C_LCD_MASTER_ADDRESS;        //0x27  //if Set I2C Address by uBITX Manager, read from EEProm
byte I2C_LCD_SECOND_ADDRESS;         //only using Dual LCD Mode

byte KeyValues[16][3];

byte isIFShift = 0;     //1 = ifShift, 2 extend
int ifShiftValue = 0;   //

byte TriggerBySW = 0;   //Action Start from Nextion LCD, Other MCU

//Use Custom Filter
//#define CUST_LPF_ENABLED 48
//#define CUST_LPF_START   49
char isCustomFilter = 0;
char isCustomFilter_A7 = 0;
char CustFilters[7][2];
                              
/**
 * Below are the basic functions that control the uBitx. Understanding the functions before 
 * you start hacking around
 */

//Ham Band
#define MAX_LIMIT_RANGE 10  //because limited eeprom size
byte useHamBandCount = 0;   //0 use full range frequency
byte tuneTXType = 0;        //0 : use full range, 1 : just Change Dial speed, 2 : just ham band change, but can general band by tune, 3 : only ham band (just support 0, 2 (0.26 version))
                          //100 : use full range but not TX on general band, 101 : just change dial speed but.. 2 : jut... but.. 3 : only ham band  (just support 100, 102 (0.26 version))
unsigned int hamBandRange[MAX_LIMIT_RANGE][2];  // =  //Khz because reduce use memory

//-1 : not found, 0 ~ 9 : Hamband index
char getIndexHambanBbyFreq(unsigned long f)
{
  f = f / 1000;
  for (byte i = 0; i < useHamBandCount; i++)
    if (hamBandRange[i][0] <= f && f < hamBandRange[i][1])
      return i;
      
  return -1;
}

//when Band change step = just hamband
//moveDirection : 1 = next, -1 : prior
void setNextHamBandFreq(unsigned long f, char moveDirection)
{
  unsigned long resultFreq = 0;
  byte loadMode = 0;
  char findedIndex = getIndexHambanBbyFreq(f);

  if (findedIndex == -1) {  //out of hamband
    f = f / 1000;
    for (byte i = 0; i < useHamBandCount -1; i++) {
      if (hamBandRange[i][1] <= f && f < hamBandRange[i + 1][0]) {
        findedIndex = i + moveDirection;
        //return (unsigned long)(hamBandRange[i + 1][0]) * 1000;
      }
    } //end of for
  }
  else if (((moveDirection == 1) && (findedIndex < useHamBandCount -1)) ||  //Next
    ((moveDirection == -1) && (findedIndex > 0)) ) {                        //Prior
    findedIndex += moveDirection;
  }
  else
    findedIndex = -1;
    
  if (findedIndex == -1)
    findedIndex = (moveDirection == 1 ? 0 : useHamBandCount -1);

  EEPROM.get(HAM_BAND_FREQS + 4 * findedIndex, resultFreq);
  
  //loadMode = (byte)(resultFreq >> 30);
  //resultFreq = resultFreq & 0x3FFFFFFF;
  loadMode = (byte)(resultFreq >> 29);
  resultFreq = resultFreq & 0x1FFFFFFF;
  
  if ((resultFreq / 1000) < hamBandRange[(unsigned char)findedIndex][0] || (resultFreq / 1000) > hamBandRange[(unsigned char)findedIndex][1])
    resultFreq = (unsigned long)(hamBandRange[(unsigned char)findedIndex][0]) * 1000;

  byteToMode(loadMode, 1);
  setFrequency(resultFreq);
}

void saveBandFreqByIndex(unsigned long f, unsigned long mode, char bandIndex) {
  if (bandIndex >= 0)
    //EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x3FFFFFFF) | (mode << 30) );
    EEPROM.put(HAM_BAND_FREQS + 4 * bandIndex, (f & 0x1FFFFFFF) | (mode << 29) );
}

/*
  KD8CEC
  When using the basic delay of the Arduino, the program freezes.
  When the delay is used, the program will generate an error because it is not communicating, 
  so Create a new delay function that can do background processing.
 */
unsigned long delayBeforeTime = 0;
byte delay_background(unsigned delayTime, byte fromType){ //fromType : 4 autoCWKey -> Check Paddle
  delayBeforeTime = millis();

  /*
   * KC4UPR - IOP review, 2020-05-03
   * 
   * I don't see anything in here that is either important to, or will adversely affect, IOP 
   * operation.  I'm not planning on using the uBITX autokeyer (since all keying will be in the
   * IOP), so neither getPaddle() nor autoSendPTTCheck() will be issues.  I do need to look into
   * overall CAT operation, in general.
   * 
   * UPDATE:  Fixed getPaddle() to be compatible.
   */

  while (millis() - delayBeforeTime <= delayTime) {

    if (fromType == 4)
    {
      //CHECK PADDLE
      if (getPaddle() != 0) //Interrupt : Stop cw Auto mode by Paddle -> Change Auto to Manual
        return 1;
        
      //Check PTT while auto Sending
      autoSendPTTCheck();
      
      Check_Cat(3);
    }
    else
    {
      //Background Work      
      Check_Cat(fromType);
    }
  }

  return 0;
}
 

/**
 * Select the properly tx harmonic filters
 * The four harmonic filters use only three relays
 * the four LPFs cover 30-21 Mhz, 18 - 14 Mhz, 7-10 MHz and 3.5 to 5 Mhz
 * Briefly, it works like this, 
 * - When KT1 is OFF, the 'off' position routes the PA output through the 30 MHz LPF
 * - When KT1 is ON, it routes the PA output to KT2. Which is why you will see that
 *   the KT1 is on for the three other cases.
 * - When the KT1 is ON and KT2 is off, the off position of KT2 routes the PA output
 *   to 18 MHz LPF (That also works for 14 Mhz) 
 * - When KT1 is On, KT2 is On, it routes the PA output to KT3
 * - KT3, when switched on selects the 7-10 Mhz filter
 * - KT3 when switched off selects the 3.5-5 Mhz filter
 * See the circuit to understand this
 */

void setTXFilters(unsigned long freq){
#ifdef USE_CUSTOM_LPF_FILTER 
  freq = freq / 1000000UL;
  for (byte i = 0; i < 7; i++) {
    if (freq >= CustFilters[i][0])
    {
      char aIn = CustFilters[i][1];
      digitalWrite(TX_LPF_A, aIn & 0x01);
      digitalWrite(TX_LPF_B, aIn & 0x02);
      digitalWrite(TX_LPF_C, aIn & 0x04);

      if (isCustomFilter_A7 == 1)
      {
        digitalWrite(10, aIn & 0x08);
        digitalWrite(11, aIn & 0x10);
        digitalWrite(12, aIn & 0x20);
        digitalWrite(13, aIn & 0x40);
      }
      return;
    }
  } //end of for
#else
  
  #if UBITX_BOARD_VERSION == 5
    if (freq > 21000000L){  // the default filter is with 35 MHz cut-off
      digitalWrite(TX_LPF_A, 0);
      digitalWrite(TX_LPF_B, 0);
      digitalWrite(TX_LPF_C, 0);
    }
    else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
      digitalWrite(TX_LPF_A, 1);
      digitalWrite(TX_LPF_B, 0);
      digitalWrite(TX_LPF_C, 0);
    }
    else if (freq > 7000000L){
      digitalWrite(TX_LPF_A, 0);
      digitalWrite(TX_LPF_B, 1);
      digitalWrite(TX_LPF_C, 0);    
    }
    else {
      digitalWrite(TX_LPF_A, 0);
      digitalWrite(TX_LPF_B, 0);
      digitalWrite(TX_LPF_C, 1);    
    }
  #else
    if (freq > 21000000L){  // the default filter is with 35 MHz cut-off
      digitalWrite(TX_LPF_A, 0);
      digitalWrite(TX_LPF_B, 0);
      digitalWrite(TX_LPF_C, 0);
    }
    else if (freq >= 14000000L){ //thrown the KT1 relay on, the 30 MHz LPF is bypassed and the 14-18 MHz LPF is allowd to go through
      digitalWrite(TX_LPF_A, 1);
      digitalWrite(TX_LPF_B, 0);
      digitalWrite(TX_LPF_C, 0);
    }
    else if (freq > 7000000L){
      digitalWrite(TX_LPF_A, 1);
      digitalWrite(TX_LPF_B, 1);
      digitalWrite(TX_LPF_C, 0);    
    }
    else {
      digitalWrite(TX_LPF_A, 1);
      digitalWrite(TX_LPF_B, 1);
      digitalWrite(TX_LPF_C, 1);    
    }
  #endif


#endif
}

/**
 * This is the most frequently called function that configures the 
 * radio to a particular frequeny, sideband and sets up the transmit filters
 * 
 * The transmit filter relays are powered up only during the tx so they dont
 * draw any current during rx. 
 * 
 * The carrier oscillator of the detector/modulator is permanently fixed at
 * uppper sideband. The sideband selection is done by placing the second oscillator
 * either 12 Mhz below or above the 45 Mhz signal thereby inverting the sidebands 
 * through mixing of the second local oscillator.
 */
 
void setFrequency(unsigned long f){
  f = (f / arTuneStep[tuneStepIndex -1]) * arTuneStep[tuneStepIndex -1];
  setTXFilters(f);

  unsigned long appliedCarrier = ((cwMode == 0 ? usbCarrier : cwmCarrier) + (isIFShift && (inTx == 0) ? ifShiftValue : 0));
  int appliedTuneValue = 0;

  //applied if tune 
  //byte advancedFreqOption1;     //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
  if ((advancedFreqOption1 & 0x01) != 0x00)
  {
    appliedTuneValue = if1TuneValue;

    //In the LSB state, the optimum reception value was found. To apply to USB, 3Khz decrease is required.
    if (sdrModeOn && (inTx == 0))
      appliedTuneValue -= 15; //decrease 1.55Khz
      
    //if (isUSB)
    if (cwMode == 2 || (cwMode == 0 && (isUSB)))    
      appliedTuneValue -= 30; //decrease 3Khz
  }

   //if1Tune RX, TX Enabled, ATT : only RX Mode
  //The IF Tune shall be measured at the LSB. Then, move the 3Khz down for USB.
  long if1AdjustValue = ((inTx == 0) ? (attLevel * 100) : 0) + (appliedTuneValue * 100); //if1Tune RX, TX Enabled, ATT : only RX Mode  //5600

  //for DIY uBITX (custom filter)
  if ((advancedFreqOption1 & 0x80) != 0x00)  //Reverse IF Tune (- Value for DIY uBITX)
    if1AdjustValue *= -1;
    
  if (sdrModeOn && (inTx == 0))  //IF SDR MODE
  {
    //Fixed Frequency SDR (Default Frequency : 32Mhz, available change sdr Frequency by uBITX Manager)
    //Dynamic Frequency is for SWL without cat

    //byte advancedFreqOption1;     //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
    long moveFrequency = 0;
    //7 6 5 4 3 2 1 0
    //        _ _     <-- SDR Freuqncy Option
    byte sdrOption = (advancedFreqOption1 >> 2) & 0x03;

    if (sdrOption == 1) // SDR Frequency + frequenc
    {
      //example : offset Freq : 20 Mhz and frequency = 7.080 => 27.080 Mhz
      //example : offset Freq : 0 Mhz and frequency = 7.080 => 7.080 Mhz
      //for available HF, SDR
      moveFrequency = f;
    }
    else if (sdrOption == 2)  //Mhz move
    {
      //Offset Frequency + Mhz, 
      //Example : Offset Frequency : 30Mhz and current Frequncy is 7.080 => 37.080Mhz
      //          Offset Frequency : 30Mhz and current Frequncy is 14.074 => 34.074Mhz
      moveFrequency = (f % 10000000);
    }
    else if (sdrOption == 3)  //Khz move
    {
      //Offset Frequency + Khz, 
      //Example : Offset Frequency : 30Mhz and current Frequncy is 7.080 => 30.080Mhz
      //          Offset Frequency : 30Mhz and current Frequncy is 14.074 => 30.074Mhz
      moveFrequency = (f % 1000000);
    }

#if UBITX_BOARD_VERSION == 5    
    si5351bx_setfreq(2, 45002000 + if1AdjustValue + f);
    si5351bx_setfreq(1, 45002000 
      + if1AdjustValue 
      + SDR_Center_Freq 
      //+ ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000))
      + moveFrequency);
      // + 2390);  //RTL-SDR Frequency Error, Do not add another SDR because the error is different. V1.3
#else
    si5351bx_setfreq(2, 44991500 + if1AdjustValue + f);
    si5351bx_setfreq(1, 44991500 
      + if1AdjustValue 
      + SDR_Center_Freq 
      //+ ((advancedFreqOption1 & 0x04) == 0x00 ? 0 : (f % 10000000))
      + moveFrequency );
      //+ 2390); Do not add another SDR because the error is different. V1.3
#endif
  }
  else
  {
    if (cwMode == 1 || (cwMode == 0 && (!isUSB))) //cwl or lsb
    {
      //CWL(cwMode == 1) or LSB (cwMode == 0 && (!isUSB))
      si5351bx_setfreq(2, SECOND_OSC_LSB + if1AdjustValue + appliedCarrier + f);
      si5351bx_setfreq(1, SECOND_OSC_LSB + if1AdjustValue);
    }
    else  //cwu or usb
    {
      //CWU (cwMode == 2) or USB (cwMode == 0 and isUSB)
      si5351bx_setfreq(2, SECOND_OSC_USB + if1AdjustValue - appliedCarrier + f);
      si5351bx_setfreq(1, SECOND_OSC_USB + if1AdjustValue);
    }
  }
  
  frequency = f;
}

/**
 * startTx is called by the PTT, cw keyer and CAT protocol to
 * put the uBitx in tx mode. It takes care of rit settings, sideband settings
 * Note: In cw mode, doesnt key the radio, only puts it in tx mode
 */
void startTx(byte txMode, byte isDisplayUpdate){
  //Check Hamband only TX //Not found Hamband index by now frequency
  if (tuneTXType >= 100 && getIndexHambanBbyFreq(ritOn ? ritTxFrequency :  frequency) == -1) {
    //no message
    return;
  }

  if ((isTxType & 0x01) != 0x01)
    digitalWrite(TX_RX, 1);

  inTx = 1;
  
  if (ritOn){
    //save the current as the rx frequency
    ritRxFrequency = frequency;
    setFrequency(ritTxFrequency);
  }
  else 
  {
    if (splitOn == 1) 
    {
      FrequencyToVFO(1);  //Save current Frequency and Mode to eeprom
      if (vfoActive == VFO_B) 
      {
        vfoActive = VFO_A;
        frequency = vfoA;
        byteToMode(vfoA_mode, 0);
      }
      else if (vfoActive == VFO_A)
      {
        vfoActive = VFO_B;
        frequency = vfoB;
        byteToMode(vfoB_mode, 0);
      }
    }

    setFrequency(frequency);
  } //end of else

  SetCarrierFreq();

  if (txMode == TX_CW){
    //turn off the second local oscillator and the bfo
    si5351bx_setfreq(0, 0);
    si5351bx_setfreq(1, 0);

    //shif the first oscillator to the tx frequency directly
    //the key up and key down will toggle the carrier unbalancing
    //the exact cw frequency is the tuned frequency + sidetone

    if (cwMode == 0)
    {
      if (isUSB)
        si5351bx_setfreq(2, frequency + sideTone);
      else
        si5351bx_setfreq(2, frequency - sideTone); 
    }
    else if (cwMode == 1) //CWL
    {
        si5351bx_setfreq(2, frequency - sideTone); 
    }
    else  //CWU
    {
        si5351bx_setfreq(2, frequency + sideTone);
    }
  }

  //reduce latency time when begin of CW mode
  if (isDisplayUpdate == 1)
    updateDisplay();
}

void stopTx(void){
  inTx = 0;

  digitalWrite(TX_RX, 0);           //turn off the tx
  SetCarrierFreq();

  if (ritOn)
    setFrequency(ritRxFrequency);
  else
  {
    if (splitOn == 1) {
      //vfo Change
      if (vfoActive == VFO_B){
        vfoActive = VFO_A;
        frequency = vfoA;
        byteToMode(vfoA_mode, 0);
      }
      else if (vfoActive == VFO_A){
        vfoActive = VFO_B;
        frequency = vfoB;
        byteToMode(vfoB_mode, 0);
      }
    }
    
    setFrequency(frequency);
  } //end of else
  
  updateDisplay();
}

/**
 * ritEnable is called with a frequency parameter that determines
 * what the tx frequency will be
 */
void ritEnable(unsigned long f){
  ritOn = 1;
  //save the non-rit frequency back into the VFO memory
  //as RIT is a temporary shift, this is not saved to EEPROM
  ritTxFrequency = f;
}

// this is called by the RIT menu routine
void ritDisable(){
  if (ritOn){
    ritOn = 0;
    setFrequency(ritTxFrequency);
    updateDisplay();
  }
}

/**
 * Basic User Interface Routines. These check the front panel for any activity
 */

/**
 * The PTT is checked only if we are not already in a cw transmit session
 * If the PTT is pressed, we shift to the ritbase if the rit was on
 * flip the T/R line to T and update the display to denote transmission
 */

void checkPTT(){  
  /*
   * KC4UPR - note that some of this is superfluous now that checkPTT() is only executed
   * in SSB mode, and cwKeyer is only executed in CW mode...
   */
  //we don't check for ptt when transmitting cw
  if (cwTimeout > 0)
    return;
    
  if (digitalRead(PTT) == 0 && inTx == 0){
    startTx(TX_SSB, 1);
    delay(50); //debounce the PTT
  }
  
  if (digitalRead(PTT) == 1 && inTx == 1)
    stopTx();
}
#ifdef EXTEND_KEY_GROUP1  
void checkButton(){
  char currentBandIndex = -1;
  
  //only if the button is pressed
  int keyStatus = getBtnStatus();
  if (keyStatus == -1)
    return;
    
  delay(50);
  keyStatus = getBtnStatus();   //will be remove 3 lines
  if (keyStatus == -1)
    return;
    
  if (keyStatus == FKEY_PRESS)  //Menu Key
  {
    //for touch screen
#ifdef USE_SW_SERIAL
    SetSWActivePage(1);
    doMenu();

    if (isCWAutoMode == 0)
          SetSWActivePage(0);
#else
    doMenu();
#endif    
  }
  else if (keyStatus <= FKEY_TYPE_MAX)  //EXTEND KEY GROUP #1
  {

    switch(keyStatus)
    {
      case FKEY_MODE :
        if (cwMode == 1)
        {
          cwMode = 2;
        }
        else if (cwMode == 2)
        {
          cwMode = 0;
          isUSB = 0;
        }
        else if (isUSB == 0)
        {
          isUSB = 1;
        }
        else
        {
          cwMode = 1;
        }
        break;
      case FKEY_BANDUP :
      case FKEY_BANDDOWN :
        //Save Band Information
        if (tuneTXType == 2 || tuneTXType == 3 || tuneTXType == 102 || tuneTXType == 103) { //only ham band move
          currentBandIndex = getIndexHambanBbyFreq(frequency);
          
          if (currentBandIndex >= 0) {
            saveBandFreqByIndex(frequency, modeToByte(), currentBandIndex);
          }
        }
        setNextHamBandFreq(frequency, keyStatus == FKEY_BANDDOWN ? -1 : 1);  //Prior Band      
        break;

      case FKEY_STEP :
        if (++tuneStepIndex > 5)
          tuneStepIndex = 1;
  
        EEPROM.put(TUNING_STEP, tuneStepIndex);
        printLine2ClearAndUpdate();
        break;

      case FKEY_VFOCHANGE :
        menuVfoToggle(1); //Vfo Toggle
        break;
      
      case FKEY_SPLIT :
        menuSplitOnOff(1);
        break;
      case  FKEY_TXOFF:
        menuTxOnOff(1, 0x01);
        break;
      case  FKEY_SDRMODE :
         menuSDROnOff(1);
        break;
      case FKEY_RIT :
        menuRitToggle(1);
        break;
    }

    FrequencyToVFO(1);
    SetCarrierFreq();
    setFrequency(frequency);
    //delay_background(delayTime, 0);
    updateDisplay();
  }
  
  //wait for the button to go up again
  while(keyStatus == getBtnStatus()) {
    delay(10);
    Check_Cat(0);
  }
  //delay(50);//debounce
}

#else
void checkButton(){
  //only if the button is pressed
  if (!btnDown())
    return;
  delay(50);
  if (!btnDown()) //debounce
    return;
 
  doMenu();
  
  //wait for the button to go up again
  while(btnDown()) {
    delay(10);
    Check_Cat(0);
  }
  //delay(50);//debounce
}
#endif

/************************************
Replace function by KD8CEC
prevent error controls
applied Threshold for reduct errors,  dial Lock, dynamic Step
 *************************************/
byte threshold = 2;  //noe action for count
unsigned long lastEncInputtime = 0;
int encodedSumValue = 0;
unsigned long lastTunetime = 0; //if continous moving, skip threshold processing
byte lastMovedirection = 0; //0 : stop, 1 : cw, 2 : ccw

//#define skipThresholdTime 70
#define encodeTimeOut 1000

void doTuningWithThresHold(){
  int s = 0;
  unsigned long prev_freq;

  if ((vfoActive == VFO_A && ((isDialLock & 0x01) == 0x01)) ||
    (vfoActive == VFO_B && ((isDialLock & 0x02) == 0x02)))
    return;

  s = enc_read();

  //if time is exceeded, it is recognized as an error,
  //ignore exists values, because of errors
  if (s == 0) {
    if (encodedSumValue != 0 && (millis() - encodeTimeOut) > lastEncInputtime)
      encodedSumValue = 0;

    lastMovedirection = 0;
    return;
  }
  lastEncInputtime = millis();

  //for check moving direction
  encodedSumValue += (s > 0 ? 1 : -1);

  //check threshold and operator actions (hold dial speed = continous moving, skip threshold check)
  //not use continues changing by Threshold
  //if ((lastTunetime < (millis() - skipThresholdTime)) && ((encodedSumValue *  encodedSumValue) <= (threshold * threshold)))
  if (((encodedSumValue *  encodedSumValue) <= (threshold * threshold)))
    return;

  lastTunetime = millis();

  //Valid Action without noise
  encodedSumValue = 0;

  prev_freq = frequency;
  //incdecValue = tuningStep * s;
  //frequency += (arTuneStep[tuneStepIndex -1] * s * (s * s < 10 ? 1 : 3));  //appield weight (s is speed)
  frequency += (arTuneStep[tuneStepIndex -1] * s);  //appield weight (s is speed) //if want need more increase size, change step size
    
  if (prev_freq < 10000000l && frequency > 10000000l)
    isUSB = true;
    
  if (prev_freq > 10000000l && frequency < 10000000l)
    isUSB = false;
    
  setFrequency(frequency);
  updateDisplay();
}

/**
 * RIT only steps back and forth by 100 hz at a time
 */
void doRIT(){
  int knob = enc_read();
  unsigned long old_freq = frequency;

  if (knob < 0)
    frequency -= (arTuneStep[tuneStepIndex -1]);  //
  else if (knob > 0)
    frequency += (arTuneStep[tuneStepIndex -1]);  //
 
  if (old_freq != frequency){
    setFrequency(frequency);
    updateDisplay();
  }
}

/*
 save Frequency and mode to eeprom for Auto Save with protected eeprom cycle, by kd8cec
 */
void storeFrequencyAndMode(byte saveType)
{
  //freqType : 0 Both (vfoA and vfoB), 1 : vfoA, 2 : vfoB
  if (saveType == 0 || saveType == 1) //vfoA
  {
      if (vfoA != vfoA_eeprom) {
        EEPROM.put(VFO_A, vfoA);
        vfoA_eeprom = vfoA;
      }
      
      if (vfoA_mode != vfoA_mode_eeprom) {
        EEPROM.put(VFO_A_MODE, vfoA_mode);
        vfoA_mode_eeprom = vfoA_mode;
      }
  }
  
  if (saveType == 0 || saveType == 2) //vfoB
  {
      if (vfoB != vfoB_eeprom) {
        EEPROM.put(VFO_B, vfoB);
        vfoB_eeprom = vfoB;
      }
      
      if (vfoB_mode != vfoB_mode_eeprom) {
          EEPROM.put(VFO_B_MODE, vfoB_mode);
          vfoB_mode_eeprom = vfoB_mode;
      }
  }
}

//calculate step size from 1 byte, compatible uBITX Manager, by KD8CEC
unsigned int byteToSteps(byte srcByte) {
    byte powerVal = (byte)(srcByte >> 6);
    unsigned int baseVal = srcByte & 0x3F;

    if (powerVal == 1)
        return baseVal * 10;
    else if (powerVal == 2)
        return baseVal * 100;
    else if (powerVal == 3)
        return baseVal * 1000;
    else
        return baseVal;
}


/**
 * The settings are read from EEPROM. The first time around, the values may not be 
 * present or out of range, in this case, some intelligent defaults are copied into the 
 * variables.
 */
void initSettings(){
  //read the settings from the eeprom and restore them
  //if the readings are off, then set defaults
  //for original source Section ===========================
  EEPROM.get(MASTER_CAL, calibration); 
  EEPROM.get(USB_CAL, usbCarrier);
  EEPROM.get(VFO_A, vfoA);
  EEPROM.get(VFO_B, vfoB);
  EEPROM.get(CW_SIDETONE, sideTone);
  EEPROM.get(CW_SPEED, cwSpeed);
  //End of original code
  
  //----------------------------------------------------------------
  //Add Lines by KD8CEC
  //for custom source Section =============================
  //ID & Version Check from EEProm 
  //if found different firmware, erase eeprom (32
  #define FIRMWAR_ID_ADDR 776 //776 : 0x59, 777 :0x58, 778 : 0x68 : Id Number, if not found id, erase eeprom(32~1023) for prevent system error.
  if (EEPROM.read(FIRMWAR_ID_ADDR) != 0x59 || 
    EEPROM.read(FIRMWAR_ID_ADDR + 1) != 0x58 || 
    EEPROM.read(FIRMWAR_ID_ADDR + 2) != 0x68 ) {
      
    printLineF(1, F("Init EEProm...")); 
      //initial all eeprom 
    for (unsigned int i = 64; i < 1024; i++) //protect Master_cal, usb_cal
      EEPROM.write(i, 0);

    //Write Firmware ID
    EEPROM.write(FIRMWAR_ID_ADDR, 0x59);
    EEPROM.write(FIRMWAR_ID_ADDR + 1, 0x58);
    EEPROM.write(FIRMWAR_ID_ADDR + 2, 0x68);
  }
  
  //Version Write for Memory Management Software
  if (EEPROM.read(VERSION_ADDRESS) != FIRMWARE_VERSION_NUM)
    EEPROM.write(VERSION_ADDRESS, FIRMWARE_VERSION_NUM);

  //SI5351 I2C Address
  //I2C_ADDR_SI5351
  SI5351BX_ADDR = EEPROM.read(I2C_ADDR_SI5351);
  if (SI5351BX_ADDR < 0x10 || SI5351BX_ADDR > 0xF0)
  {
    SI5351BX_ADDR = 0x60;
  }
  

  //Backup Calibration Setting from Factory Setup
  //Check Factory Setting Backup Y/N
  if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13) {
    EEPROM.write(FACTORY_BACKUP_YN, 0x13);  //Set Backup Y/N
    
    for (unsigned int i = 0; i < 32; i++) //factory setting range
      EEPROM.write(FACTORY_VALUES + i, EEPROM.read(i)); //0~31 => 65~96
  }

  EEPROM.get(CW_CAL, cwmCarrier);

  //for Save VFO_A_MODE to eeprom
  //0: default, 1:not use, 2:LSB, 3:USB, 4:CW, 5:AM, 6:FM
  EEPROM.get(VFO_A_MODE, vfoA_mode);
  EEPROM.get(VFO_B_MODE, vfoB_mode);

  //CW DelayTime
  EEPROM.get(CW_DELAY, cwDelayTime);

  //CW interval between TX and CW Start
  EEPROM.get(CW_START, delayBeforeCWStartTime);
  EEPROM.get(CW_KEY_TYPE, cwKeyType);
  if (cwKeyType > 2)
    cwKeyType = 0;

  if (cwKeyType == 0)
    Iambic_Key = false;
  else
  {
    Iambic_Key = true;
    if (cwKeyType == 1)
      keyerControl &= ~IAMBICB;
    else
      keyerControl |= IAMBICB;
  }

  EEPROM.get(COMMON_OPTION0, commonOption0);
  EEPROM.get(DISPLAY_OPTION1, displayOption1);
  EEPROM.get(DISPLAY_OPTION2, displayOption2);

  for (byte i = 0; i < 8; i++) {
    sMeterLevels[i + 1] = EEPROM.read(S_METER_LEVELS + i);
  }

  //KeyValues
  for (byte i = 0; i < 16; i++) {
    KeyValues[i][0] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3));        //RANGE : Start Value
    KeyValues[i][1] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3) + 1);    //RANGE : End Value
    KeyValues[i][2] = EEPROM.read(EXTENDED_KEY_RANGE + (i * 3) + 2);    //KEY TYPE 
  }

#ifdef USE_CUSTOM_LPF_FILTER 
  //Custom Filters
  EEPROM.get(CUST_LPF_ENABLED, isCustomFilter);
  if (isCustomFilter == 0x58)
  {
    isCustomFilter_A7 = 1;
  }
  isCustomFilter = (isCustomFilter == 0x58 || isCustomFilter == 0x57);
  
  for (byte i = 0; i < 7; i++) {
    CustFilters[i][0] = EEPROM.read(CUST_LPF_START + (i * 2));        //LPF (To) Mhz
    CustFilters[i][1] = EEPROM.read(CUST_LPF_START + (i * 2) + 1);    //Enabled I/O
  }
//char isCustomFilter = 0;
//char isCustomFilter_A7 = 0;
//char CustFilters[2][7];
#endif

  //User callsign information
  if (EEPROM.read(USER_CALLSIGN_KEY) == 0x59)
    userCallsignLength = EEPROM.read(USER_CALLSIGN_LEN);  //MAXIMUM 18 LENGTH

  //Ham Band Count
  EEPROM.get(HAM_BAND_COUNT, useHamBandCount);
  EEPROM.get(TX_TUNE_TYPE, tuneTXType);

  byte findedValidValueCount = 0;
    
  //Read band Information
  for (byte i = 0; i < useHamBandCount; i++) {
    unsigned int tmpReadValue = 0;
    EEPROM.get(HAM_BAND_RANGE + 4 * i, tmpReadValue);
    hamBandRange[i][0] = tmpReadValue;

    if (tmpReadValue > 1 && tmpReadValue < 55000)
      findedValidValueCount++;

    EEPROM.get(HAM_BAND_RANGE + 4 * i + 2, tmpReadValue);
    hamBandRange[i][1] = tmpReadValue;
  }

  //Check Value Range and default Set for new users
  if ((3 < tuneTXType && tuneTXType < 100) || 103 < tuneTXType || useHamBandCount < 1 || findedValidValueCount < 5)
  {
    tuneTXType = 2;
    //if empty band Information, auto insert default region 2 frequency range
    //This part is made temporary for people who have difficulty setting up, so can remove it when you run out of memory.
    useHamBandCount = 10;
    hamBandRange[0][0] = 1810; hamBandRange[0][1] = 2000; 
    hamBandRange[1][0] = 3500; hamBandRange[1][1] = 3800; 
    hamBandRange[2][0] = 5351; hamBandRange[2][1] = 5367; 
    hamBandRange[3][0] = 7000; hamBandRange[3][1] = 7300;     //region 2
    hamBandRange[4][0] = 10100; hamBandRange[4][1] = 10150; 
    hamBandRange[5][0] = 14000; hamBandRange[5][1] = 14350; 
    hamBandRange[6][0] = 18068; hamBandRange[6][1] = 18168; 
    hamBandRange[7][0] = 21000; hamBandRange[7][1] = 21450; 
    hamBandRange[8][0] = 24890; hamBandRange[8][1] = 24990; 
    hamBandRange[9][0] = 28000; hamBandRange[9][1] = 29700; 
  }
  

  //Read Tuning Step Index, and steps
  findedValidValueCount = 0;
  EEPROM.get(TUNING_STEP, tuneStepIndex);
  for (byte i = 0; i < 5; i++) {
    arTuneStep[i] = byteToSteps(EEPROM.read(TUNING_STEP + i + 1));
    if (arTuneStep[i] >= 1 && arTuneStep[i] <= 60000) //Maximum 650 for check valid Value
       findedValidValueCount++;
  }

  //Check Value Range and default Set for new users
  if (findedValidValueCount < 5)
  {
    //Default Setting
    arTuneStep[0] = 10;
    arTuneStep[1] = 50;
    arTuneStep[2] = 100;
    arTuneStep[3] = 500;
    arTuneStep[4] = 1000;
  }

  if (tuneStepIndex == 0) //New User
    tuneStepIndex = 3;

  //CW Key ADC Range ======= adjust set value for reduce cw keying error
  //by KD8CEC
  unsigned int tmpMostBits = 0;
  tmpMostBits = EEPROM.read(CW_ADC_MOST_BIT1);
  cwAdcSTFrom = EEPROM.read(CW_ADC_ST_FROM)   | ((tmpMostBits & 0x03) << 8);
  cwAdcSTTo = EEPROM.read(CW_ADC_ST_TO)       | ((tmpMostBits & 0x0C) << 6);
  cwAdcDotFrom = EEPROM.read(CW_ADC_DOT_FROM) | ((tmpMostBits & 0x30) << 4);
  cwAdcDotTo = EEPROM.read(CW_ADC_DOT_TO)     | ((tmpMostBits & 0xC0) << 2);
  
  tmpMostBits = EEPROM.read(CW_ADC_MOST_BIT2);
  cwAdcDashFrom = EEPROM.read(CW_ADC_DASH_FROM) | ((tmpMostBits & 0x03) << 8);
  cwAdcDashTo = EEPROM.read(CW_ADC_DASH_TO)     | ((tmpMostBits & 0x0C) << 6);
  cwAdcBothFrom = EEPROM.read(CW_ADC_BOTH_FROM) | ((tmpMostBits & 0x30) << 4);
  cwAdcBothTo = EEPROM.read(CW_ADC_BOTH_TO)     | ((tmpMostBits & 0xC0) << 2);

  //Display Type for CW mode
  isShiftDisplayCWFreq = EEPROM.read(CW_DISPLAY_SHIFT);

  //Enable / Diable Check for CW Display Cofiguration Group 
  if ((commonOption0 & 0x80) != 0x00)
  {
    //Adjust CW Mode Freq
    shiftDisplayAdjustVal = (isShiftDisplayCWFreq & 0x3F) * 10;
  
    //check Minus
    if ((isShiftDisplayCWFreq & 0x40) == 0x40)
      shiftDisplayAdjustVal = shiftDisplayAdjustVal * -1;
  
   //Shift Display Check (Default : 0)
    if ((isShiftDisplayCWFreq & 0x80) == 0)  //Enabled
      isShiftDisplayCWFreq = 1;
    else    //Disabled
      isShiftDisplayCWFreq = 0;
  }

   //Stored IF Shift Option
  if ((commonOption0 & 0x40) != 0x00)
  {
    EEPROM.get(IF_SHIFTVALUE, ifShiftValue);
    isIFShift = ifShiftValue != 0;
  }

  //Advanced Freq control
  EEPROM.get(ADVANCED_FREQ_OPTION1, advancedFreqOption1);

  //byte advancedFreqOption1;     //255 : Bit0: use IFTune_Value, Bit1 : use Stored enabled SDR Mode, Bit2 : dynamic sdr frequency0, Bit3 : dynamic sdr frequency1, bit 7: IFTune_Value Reverse for DIY uBITX
  if ((advancedFreqOption1 & 0x01) != 0x00)
  {
    EEPROM.get(IF1_CAL, if1TuneValue);

    //Stored Enabled SDR Mode
    if ((advancedFreqOption1 & 0x02) != 0x00)
    {
      EEPROM.get(ENABLE_SDR, sdrModeOn);
    }
  }
  
  EEPROM.get(SDR_FREQUNCY, SDR_Center_Freq);
  //if (SDR_Center_Freq == 0)
  //  SDR_Center_Freq = 32000000;

  //default Value (for original hardware)
  if (cwAdcSTFrom >= cwAdcSTTo)
  {
    cwAdcSTFrom = 0;
    cwAdcSTTo = 50;
  }

  if (cwAdcBothFrom >= cwAdcBothTo)
  {
    cwAdcBothFrom = 51;
    cwAdcBothTo = 300;
  }
  
  if (cwAdcDotFrom >= cwAdcDotTo)
  {
    cwAdcDotFrom = 301;
    cwAdcDotTo = 600;
  }
  if (cwAdcDashFrom >= cwAdcDashTo)
  {
    cwAdcDashFrom = 601;
    cwAdcDashTo = 800;
  }
  //end of CW Keying Variables
  
  if (cwDelayTime < 1 || cwDelayTime > 250)
    cwDelayTime = 60;

  if (vfoA_mode < 2)
    vfoA_mode = 2;
  
  if (vfoB_mode < 2)
    vfoB_mode = 3;


#if UBITX_BOARD_VERSION == 5
  //original code with modified by kd8cec
  if (usbCarrier > 11060000l || usbCarrier < 11048000l)
    usbCarrier = 11052000l;

  if (cwmCarrier > 11060000l || cwmCarrier < 11048000l)
    cwmCarrier = 11052000l;
#else
  //original code with modified by kd8cec
  if (usbCarrier > 12010000l || usbCarrier < 11990000l)
    usbCarrier = 11997000l;

  if (cwmCarrier > 12010000l || cwmCarrier < 11990000l)
    cwmCarrier = 11997000l;
#endif
    
  if (vfoA > 35000000l || 3500000l > vfoA) {
     vfoA = 7150000l;
     vfoA_mode = 2; //LSB
  }
  
  if (vfoB > 35000000l || 3500000l > vfoB) {
     vfoB = 14150000l;  
     vfoB_mode = 3; //USB
  }
  //end of original code section

  //for protect eeprom life by KD8CEC
  vfoA_eeprom = vfoA;
  vfoB_eeprom = vfoB;
  vfoA_mode_eeprom = vfoA_mode;
  vfoB_mode_eeprom = vfoB_mode;

  if (sideTone < 100 || 2000 < sideTone) 
    sideTone = 800;
  if (cwSpeed < 10 || 1000 < cwSpeed) 
    cwSpeed = 100;

  if (sideTone < 300 || sideTone > 1000) {
    sideTonePitch = 0;
    sideToneSub = 0;;
  }
  else{
    sideTonePitch = (sideTone - 300) / 50;
    sideToneSub = sideTone % 50;
  }
}

void initPorts(){
  analogReference(DEFAULT);

  //??
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);
  pinMode(FBUTTON, INPUT_PULLUP);
  
  //configure the function button to use the external pull-up
//  pinMode(FBUTTON, INPUT);
//  digitalWrite(FBUTTON, HIGH);

  pinMode(PTT, INPUT_PULLUP);
  pinMode(ANALOG_KEYER, INPUT_PULLUP);
  pinMode(ANALOG_SMETER, INPUT); //by KD8CEC

#ifdef USE_CUSTOM_LPF_FILTER 
  if (isCustomFilter_A7)
  {
    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);
    pinMode(12, OUTPUT);
    pinMode(13, OUTPUT);
  }
#endif  

  pinMode(CW_TONE, OUTPUT);  
  digitalWrite(CW_TONE, 0);
  
  pinMode(TX_RX,OUTPUT);
  digitalWrite(TX_RX, 0);

  pinMode(TX_LPF_A, OUTPUT);
  pinMode(TX_LPF_B, OUTPUT);
  pinMode(TX_LPF_C, OUTPUT);
  digitalWrite(TX_LPF_A, 0);
  digitalWrite(TX_LPF_B, 0);
  digitalWrite(TX_LPF_C, 0);

  pinMode(CW_KEY, OUTPUT);
  digitalWrite(CW_KEY, 0);
}

//Recovery Factory Setting Values 
void factory_Recovery()
{
  if (EEPROM.read(FACTORY_BACKUP_YN) != 0x13)
    return;

  if (digitalRead(PTT) == 0)  //Do not proceed if PTT is pressed to prevent malfunction.
    return;
    
  printLineF2(F("Factory Recovery"));
  delay(2000);
  if (!btnDown())
    return;

  printLineF2(F("IF you continue"));
  printLineF1(F("release the key"));
  delay(2000);
  if (btnDown())
    return;
  
  printLineF1(F("Press Key PTT"));
  delay(2000);
  if (digitalRead(PTT) == 0)
  {
    for (unsigned int i = 0; i < 32; i++) //factory setting range
      EEPROM.write(i, EEPROM.read(FACTORY_VALUES + i)); //65~96 => 0~31

    //printLineF2(F("CompleteRecovery"));
    printLineF1(F("Power Reset!"));
    while(1);   //Hold 
  }
}


void setup()
{
  /*
  //Init EEProm for Fault EEProm TEST and Factory Reset
  //please remove remark for others.
  //for (int i = 0; i < 1024; i++)
  for (int i = 16; i < 1024; i++) //protect Master_cal, usb_cal
    EEPROM.write(i, 0xFF);
  lcd.begin(16, 2);
  printLineF(1, F("Complete Erase")); 
  sleep(1000);
  //while(1);
  //end section of test
  */

  //Load I2C LCD Address for I2C LCD 
  //I2C LCD Parametere
#ifdef USE_I2C_LCD  
  EEPROM.get(I2C_LCD_MASTER, I2C_LCD_MASTER_ADDRESS);
  EEPROM.get(I2C_LCD_SECOND, I2C_LCD_SECOND_ADDRESS);

  if (I2C_LCD_MASTER_ADDRESS < 0x10 || I2C_LCD_MASTER_ADDRESS > 0xF0)
    I2C_LCD_MASTER_ADDRESS = I2C_LCD_MASTER_ADDRESS_DEFAULT;
    
  if (I2C_LCD_SECOND_ADDRESS < 0x10 || I2C_LCD_SECOND_ADDRESS > 0xF0)
    I2C_LCD_SECOND_ADDRESS = I2C_LCD_SECOND_ADDRESS_DEFAULT;
#endif  
  
  //Serial.begin(9600);
  LCD_Init();
  //printLineF(1, FIRMWARE_VERSION_INFO);
  DisplayVersionInfo(FIRMWARE_VERSION_INFO);

  Init_Cat(38400, SERIAL_8N1);
  initSettings();
  initPorts();     

#ifdef USE_SW_SERIAL
//  if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) 
//  {
    userCallsignLength = userCallsignLength & 0x7F;
//  }
#else
//for Chracter LCD
  if (userCallsignLength > 0 && ((userCallsignLength & 0x80) == 0x80)) 
  {
    userCallsignLength = userCallsignLength & 0x7F;
    DisplayCallsign(userCallsignLength);
  }
  else {
    printLineF(0, F("uBITX v0.20")); 
    delay(500);
    clearLine2();
  }
#endif

#ifdef FACTORY_RECOVERY_BOOTUP
  if (btnDown())
    factory_Recovery();
#endif

  byteToMode(vfoA_mode, 0);
  initOscillators();

  frequency = vfoA;
  saveCheckFreq = frequency;  //for auto save frequency
  setFrequency(vfoA);

#ifdef USE_SW_SERIAL
  SendUbitxData();
#endif
  
  updateDisplay();

#ifdef ENABLE_FACTORYALIGN
  if (btnDown())
    factory_alignment();
#endif

}

//Auto save Frequency and Mode with Protected eeprom life by KD8CEC
void checkAutoSaveFreqMode()
{
  //when tx or ritOn, disable auto save
  if (inTx || ritOn)
    return;

  //detect change frequency
  if (saveCheckFreq != frequency)
  {
    saveCheckTime = millis();
    saveCheckFreq = frequency;
  }
  else if (saveCheckTime != 0)
  {
    //check time for Frequency auto save
    if (millis() - saveCheckTime > saveIntervalSec * 1000)
    {
      FrequencyToVFO(1);
      saveCheckTime = 0;  //for reduce cpu use rate
    }
  }
}

void loop(){ 
  /*
   * KC4UPR - IOP update, 2020-05-03
   * 
   * Getting rid of the autokeyer code... not planning on using, since any autokeying
   * would actually be done by the IOP.  We'll check the PTT, but only in SSB mode
   * (same line as CW, so it would be caught by cwKeyer() in CW mode).
   * 
   * Only check the CW keyer if we are in one of the CW modes.  Why?  Because we
   * are using the same input for PTT and CW.
   */
//  if (isCWAutoMode == 0){  //when CW AutoKey Mode, disable this process
//    if (!txCAT)
//      checkPTT();
//    checkButton();
//  }
// else
//    controlAutoCW();
  // KC4UPR:  Note, implementation below leaves no manual way to abort TX due to CAT.  May
  // want to add in a way to interrupt CAT transmission with a PTT/CW event.
  if (!txCAT) {
    if (cwMode == 0)
      checkPTT();
    else
      cwKeyer();
    checkButton();    
  }
  
//cwKeyer(); 
  
  //tune only when not tranmsitting 
  if (!inTx){
    if (isCWAutoMode == 0 || cwAutoDialType == 1)
    {
      if (ritOn)
        doRIT();
      else 
        doTuningWithThresHold();
    }

    if (isCWAutoMode == 0 && beforeIdle_ProcessTime < millis() - 250) {
      idle_process();
      checkAutoSaveFreqMode();  //move here form out scope for reduce cpu use rate
      beforeIdle_ProcessTime = millis();
    }
  } //end of check TX Status

  //we check CAT after the encoder as it might put the radio into TX
  Check_Cat(inTx? 1 : 0);

  //for SEND SW Serial
  #ifdef USE_SW_SERIAL
    SWS_Process();
  #endif  
}
