#include <rgb_lcd.h>
#include <si5351.h>

static const long bandStart = 18068000;          // start of VFO range
static const long bandEnd =   18168000;          // end of VFO range
static const long bandInit =  18150000;          // where to initially set the frequency
volatile long freq = 18150000;                   // the current freq
volatile long oldfreq = 0;                      // the previous freq
volatile long rx_LSB_BFO_freq = 8998200;           // High side injection thus SB inversion.
volatile long rx_USB_BFO_freq = 9001200;           // High side injection thus SB inversion.
volatile long tx_LSB_BFO_freq = 8991200;           // High side injection thus SB inversion.
volatile long tx_USB_BFO_freq = 9001200;           // High side injection thus SB inversion.
volatile long radix = 1000;                     // How much to change the frequency by, clicking the Up Down switches
volatile long oldradix = 0;                     // the previous radix
volatile int mode = 0;                          // the current mode (0=LSB, 1=USB)
volatile int oldmode = 0;                       // the previous mode (0=LSB, 1=USB)
volatile int TX = 0;                            // 0=RX, 1=TX
volatile int oldTX = 1;                         // the old TX
unsigned int encoderA, encoderB, encoderC = 1;  // rotary encoder variables

// Rotary encoder pins and other inputs
static const int rotAPin = 3;
static const int rotBPin = 2;
static const int radixPin = 4;
static const int modePin = 8;
static const int PTTPin = 5;

// Instantiate the Objects
rgb_lcd lcd;              // 3F the address of the LCD
Si5351 si5351;


void setup()
{
  // Set up I/O pins
  pinMode(rotAPin, INPUT);
  digitalWrite(rotAPin, HIGH);                    // internal pull-up enabled
  pinMode(rotBPin, INPUT);
  digitalWrite(rotBPin, HIGH);                    // internal pull-up enabled
  pinMode(radixPin, INPUT);
  digitalWrite(radixPin, HIGH);                   // internal pull-up enabled
  pinMode(modePin, INPUT_PULLUP);
  //digitalWrite(modePin, HIGH);                    // internal pull-up enabled
  pinMode(PTTPin, INPUT_PULLUP);
  //digitalWrite(PTTPin, HIGH);                      // internal pull-up disabled

  // Initialize the display
  lcd.begin(16, 2);
//  lcd.backlight();
  lcd.noCursor();

  // Initialize the DDS
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, -30756);         // 62100 is the specific calibration factor for this Si5351 board
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_4MA);   // 2 mA for HB mixers
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_4MA);   // 2 mA for HB mixers
}

void loop()
{
  CheckEncoder();
  CheckRadixSwitch();
  CheckModeSwitch();
  CheckPTTPin();
}

void CheckEncoder()
{
  byte encoderA = digitalRead(rotAPin);
  byte encoderB = digitalRead(rotBPin);

  if ((encoderA == HIGH) && (encoderC == LOW))
  {
    if (encoderB == HIGH)
      // Decrease frequency
      freq = constrain(freq - radix, bandStart, bandEnd);
    else
      // Increase frequency
      freq = constrain(freq + radix, bandStart, bandEnd);
  }
  encoderC = encoderA;

  if (freq != oldfreq)
  {
    UpdateDisplay();
    SendFrequency();
    oldfreq = freq;
  }
}

void CheckRadixSwitch()
{
  if (digitalRead(radixPin) == 0)
  {
    radix = radix / 10;
    if (radix < 1)
      radix = 1000;
    delay(200);
  }

  if (radix != oldradix)
  {
    UpdateRadixDisplay();
    oldradix = radix;
  }
}

void CheckModeSwitch()
{
  if (digitalRead(modePin) == 0)
    mode = 0;                                 // 0=LSB
  if (digitalRead(modePin) == 1)
    mode = 1;                                 // 1=USB

  if (mode != oldmode)
  {
    UpdateDisplay();
    SendFrequency();
    oldmode = mode;
  }
}

void CheckPTTPin()
{
  if (digitalRead(PTTPin) == 0)
    TX = 1;

  if (digitalRead(PTTPin) == 1)
    TX = 0;
    
  if (TX != oldTX)
  {
    UpdateDisplay();
    SendFrequency();
    oldTX = TX;
  }
}

void UpdateDisplay()
{
  // freq
  lcd.setCursor(0, 0);
  lcd.print(freq);
  // mode
  lcd.setCursor(0, 1);
  if (mode == 0)
    lcd.print("LSB");
  else
    lcd.print("USB");
  lcd.setCursor(10, 1);
  // PTT
  lcd.setCursor(4, 1);
  if (TX == 1)
    lcd.print("TX");
  if (TX == 0)
    lcd.print("RX");
  // callsign
  lcd.setCursor(10, 1);
  lcd.print("GM4SLV");
}

void UpdateRadixDisplay()
{
  // radix
  if (radix == 1000)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(9, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
    lcd.setCursor(4, 0);
    lcd.cursor();
  }
  if (radix == 100)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(10, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
    lcd.setCursor(5, 0);
  }
  if (radix == 10)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(11, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
    lcd.setCursor(6, 0);
  }
  if (radix == 1)
  {
    lcd.setCursor(9, 0);
    lcd.print("      ");
    lcd.setCursor(12, 0);
    lcd.print(radix);
    lcd.setCursor(14, 0);
    lcd.print("Hz");
    lcd.setCursor(7, 0);
  }
}

void SendFrequency()
{
  if (mode == 0)                                                          // LSB
  {
    if (TX == 1)                                                          // Transmit
    {
      si5351.set_freq(((tx_LSB_BFO_freq + freq) * 100ULL), SI5351_CLK2);     // VFO
      si5351.set_freq((tx_LSB_BFO_freq * 100ULL), SI5351_CLK0);              // BFO
    }
    else                                                                  // Receive
    {
      si5351.set_freq(((rx_LSB_BFO_freq + freq) * 100ULL), SI5351_CLK0);     // VFO
      si5351.set_freq((rx_LSB_BFO_freq * 100ULL), SI5351_CLK2);              // BFO
    }
  }

  if (mode == 1)                                                          // USB
  {
    if (TX == 1)                                                          // Transmit
    {
      si5351.set_freq(((tx_USB_BFO_freq + freq) * 100ULL), SI5351_CLK2);     // VFO
      si5351.set_freq((tx_USB_BFO_freq * 100ULL), SI5351_CLK0);              // BFO
    }
    else                                                                  // Receive
    {
      si5351.set_freq(((rx_USB_BFO_freq + freq) * 100ULL), SI5351_CLK0);     // VFO
      si5351.set_freq((rx_USB_BFO_freq * 100ULL), SI5351_CLK2);              // BFO
    }
  }
}
