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

char version[] = "v0.2";

const long bandStart = 5000000;    
const long bandEnd = 6000000;
const long txcio = 9001200;
 
const long cwoffset = 600;
const long filteroffset = 500;

volatile long freq = 5450000;  //      
volatile long radix = 1000;  
         
volatile long oldfreq = 0;


volatile long currentfreq = 0;
volatile int updatedisplay = 0;

// Rotary encoder pins and other inputs
static const int rotAPin = 2;
static const int rotBPin = 3;
static const int pushSwPin = 4;

static const int memChPin = 8;

static const int txRLA1Pin = 7;
static const int txRLA2Pin = 6;

//static const int modRLAPin = 9;

static const int modeSWPin = 11;



volatile int mod = 0;
volatile int oldmod = 0;
volatile int mem = 0;
volatile int oldmem = 0;

volatile int mode = 1; // 1 = USB/LSB, 0 = CW 
volatile int oldmode = 0;

// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;


int digit1 = 0;
int digit2 = 0;
int digit3 = 0;
int digit4 = 0;   
int digit5 = 0;
int digit6 = 0;
int digit7 = 0;
//int digit8 = 0;

// Instantiate the Objects
rgb_lcd lcd;
Si5351 si5351;

void setup()
{
  // Set up frequency and radix switches
  pinMode(rotAPin, INPUT_PULLUP);
  pinMode(rotBPin, INPUT_PULLUP);
  pinMode(pushSwPin, INPUT_PULLUP);

  pinMode(memChPin, INPUT_PULLUP);
  
  pinMode(modeSWPin, INPUT_PULLUP);
  pinMode(txRLA1Pin, OUTPUT);
  pinMode(txRLA2Pin, OUTPUT);
  //pinMode(modRLAPin, OUTPUT);
 
  digitalWrite(txRLA1Pin, LOW);
  digitalWrite(txRLA2Pin, LOW);
  //digitalWrite(modRLAPin, LOW);

  // Set up interrupt pins
  attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
  attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);

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

  lcd.cursor();

  // Initialize the Si5351

  //si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, -31200);         // 
  //si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);   // 2 mA for HB mixers
  //si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);   // 2 mA for HB mixers
  
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
  si5351.set_correction(-31200, SI5351_PLL_INPUT_XO);
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);

  
  // Update display and send start frequency
  UpdateDisplay();
  SendFrequency();
}


void loop()
{




  

  
  CheckModeSwitch();

  CheckMemChSwitch();
  
  // Check to see if the freq has changed
  currentfreq = getfreq();                      // Interrupt safe method to get the current frequency
  
  if (currentfreq != oldfreq)
  {
   
    
//    txvfo = currentfreq + txcio;
    oldfreq = currentfreq;
    
    digit1 = (freq%10);
    digit2 = ((freq/10)%10);
    digit3 = ((freq/100)%10);
    digit4 = ((freq/1000)%10);   
    digit5 = ((freq/10000)%10);
    digit6 = ((freq/100000)%10);
    digit7 = ((freq/1000000)%10);
    //digit8 = (freq/10000000);
    
    UpdateDisplay();
    SendFrequency();

  
  }

  // Check the rotary encoder (radix) swith
  if (digitalRead(pushSwPin) == LOW)           // Read the rotary encoder switch
  {
    delay(10);
    while (digitalRead(pushSwPin) == LOW)
    {
      if (updatedisplay == 1)
      {
        UpdateDisplay();
        updatedisplay = 0;
      }
    }
    delay(50);
  }

  
}



long getfreq()
{
  long temp_freq;
  cli();
  temp_freq = freq;
  sei();
  return temp_freq;
}



void CheckModeSwitch()
{
  if (digitalRead(modeSWPin) == 0)
      mode = 0;                             // 0 = CW
  else if (digitalRead(modeSWPin) == 1)
      mode = 1;                                 // 1=SSB
    
    
  if (mode != oldmode)
  {
    if ( mode == 0)
      {
        
      }
    else
      {
       
      } 
    UpdateDisplay();
    SendFrequency();
    oldmode = mode;
  }
}


void CheckMemChSwitch()
{
  if (digitalRead(memChPin) == 0)
    mem = 1;
   else if (digitalRead(memChPin) == 1)
    mem = 0;

    if (mem != oldmem)
    {
      if ( mem == 1 )
      
      freq = 5450000;
      UpdateDisplay();
      SendFrequency();
      oldmem = mem;
    }
    

}
// Interrupt routines
void ISRrotAChange()
{
  if (digitalRead(rotAPin))
  {
    rotAval = 1;
    UpdateRot();
  }
  else
  {
    rotAval = 0;
    UpdateRot();
  }
}


void ISRrotBChange()
{
  if (digitalRead(rotBPin))
  {
    rotBval = 1;
    UpdateRot();
  }
  else
  {
    rotBval = 0;
    UpdateRot();
  }
}


// Determine which way the rotary encoder is rotating and action as required
void UpdateRot()
{
  switch (rotState)
  {
    case 0:                                         // Idle state, look for direction
      if (!rotBval)
        rotState = 1;                               // CW 1
      if (!rotAval)
        rotState = 11;                              // CCW 1
      break;

    case 1:                                         // CW, wait for A low while B is low
      if (!rotBval)
      {
        if (!rotAval)
        {
          // either increment the radix or freq
          if (digitalRead(pushSwPin) == LOW)
          {
            updatedisplay = 1;
            if (radix == 1000000)
              radix = 100000;
            else if (radix == 100000)
              radix = 10000;
            else if (radix == 10000)
              radix = 1000;
            else if (radix == 1000)
              radix = 500;
            else if (radix == 500)
              radix = 100;
            else if (radix == 100)
              radix = 10;
              
            //else if (radix == 10)
            //  radix = 1;
            else
              radix = 1000000;
          }
          else
          {
            freq = (freq + radix);
            
            if (freq > bandEnd)
              freq = bandEnd;
          }
          rotState = 2;                             // CW 2
        }
      }
      else if (rotAval)
        rotState = 0;                               // It was just a glitch on B, go back to start
      break;

    case 2:                                         // CW, wait for B high
      if (rotBval)
        rotState = 3;                               // CW 3
      break;

    case 3:                                         // CW, wait for A high
      if (rotAval)
        rotState = 0;                               // back to idle (detent) state
      break;

    case 11:                                        // CCW, wait for B low while A is low
      if (!rotAval)
      {
        if (!rotBval)
        {
          // either decrement the radix or freq
          if (digitalRead(pushSwPin) == LOW)
          {
            updatedisplay = 1;
            //if (radix == 1)
            //  radix = 10;
            //else if (radix == 10)
            
             if (radix == 10) // new LIMIT TO 1Hz
              radix = 100;
            
            else if (radix == 100)
              radix = 500;
            else if (radix == 500)
              radix = 1000;
            else if (radix == 1000)
              radix = 10000;
            else if (radix == 10000)
              radix = 100000;
            else if (radix == 100000)
              radix = 1000000;
            //else if (radix == 1000000)
            //  radix = 10000000;
            else
              //radix = 1;

              radix = 10; // NEW
          }
          else
          {
            freq = (freq - radix);
            
           
            
            if (freq < bandStart)
              freq = bandStart;
          }
          rotState = 12;                            // CCW 2
        }
      }
      else if (rotBval)
        rotState = 0;                               // It was just a glitch on A, go back to start
      break;

    case 12:                                        // CCW, wait for A high
      if (rotAval)
        rotState = 13;                              // CCW 3
      break;

    case 13:                                        // CCW, wait for B high
      if (rotBval)
        rotState = 0;                               // back to idle (detent) state
      break;
  }
}


void UpdateDisplay()
{

  lcd.setCursor(0,0);
  //lcd.print(digit8);
  //lcd.setCursor(1,0);
  lcd.print(digit7);
  lcd.setCursor(1,0);
  lcd.print(",");
  lcd.setCursor(2,0);
  lcd.print(digit6);
  lcd.setCursor(3,0);
  lcd.print(digit5);
  lcd.setCursor(4,0);
  lcd.print(digit4);
  lcd.setCursor(5,0);
  lcd.print(".");
  lcd.setCursor(6,0);
  lcd.print(digit3);
  lcd.setCursor(7,0);
  lcd.print(digit2);
  //lcd.setCursor(8,0);
  //lcd.print(digit1);

  //lcd.setCursor(9,0);
  
  lcd.setCursor(8,0);
  lcd.print("kHz");
 
  

  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  if ((freq >= 5258500 && freq < 5264000) || 
  ( freq >= 5276000 && freq < 5284000 ) || 
  ( freq >= 5288500 && freq < 5292000 ) ||
  ( freq >= 5298000 && freq < 5307000 ) ||
  ( freq >= 5313000 && freq < 5323000 ) ||
  ( freq >= 5333000 && freq < 5338000 ) ||
  ( freq >= 5354000 && freq < 5358000 ) ||
  ( freq >= 5362000 && freq < 5374000 ) ||
  ( freq >= 5378000 && freq < 5382000 ) ||
  ( freq >= 5395000 && freq < 5401500 ) ||
  ( freq >= 5403500 && freq < 5406500 ))
  {
    lcd.print("UK");
  }
 
  
 if (freq == 5287200 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(4,1);
  lcd.print("WSPR1");
 }

 if (freq == 5364700 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("WSPR2");
 }

 if (freq == 5450000 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("RAF");
 }

 if (freq >= 5505000 && freq < 5505200 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("Shannon");
 }

if (freq == 5317000 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("VMARS");
 }

 if (freq == 5398500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UKCall");
 }
 // lcd.print(version);
if (freq == 5366500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FK");
 }

if (freq == 5258500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FA");
 }

if (freq == 5278500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FB");
 }

if (freq == 5288500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FC");
 }

if (freq == 5371500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FL");
 }
 
if (freq == 5398500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FE");
 }

 if (freq == 5403500 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("UK FM");
 }
if (freq == 5195000 )
 {
  lcd.setCursor(0,1);
  lcd.print("       ");
  lcd.setCursor(0,1);
  lcd.print("DRA5 ");
 }


  lcd.setCursor(14,0);

    lcd.print("RX");
 


  lcd.setCursor(7,1);
  if (mode == 1)
  {
    lcd.print(" SSB");
  }
  if (mode == 0)
  {
    lcd.print("  CW");
  }
 
  lcd.setCursor(12,1);
  lcd.print(version);

 if (radix == 1)
    lcd.setCursor(8, 0);
  if (radix == 10)
    lcd.setCursor(7, 0);
  if (radix == 100)
    lcd.setCursor(6, 0);
  if (radix == 500)
    lcd.setCursor(5,0);
  if (radix == 1000)
    lcd.setCursor(4, 0);
  if (radix == 10000)
    lcd.setCursor(3, 0);
  if (radix == 100000)
    lcd.setCursor(2, 0);
  if (radix == 1000000)
    lcd.setCursor(0, 0);
  //if (radix == 10000000)
  //  lcd.setCursor(0, 0);
}

void SendFrequency()
{

if (mode == 1 ) // SSB

{
 si5351.set_freq(((txcio + freq) * 100ULL), SI5351_CLK0);     // VFO
 si5351.set_freq((txcio * 100ULL), SI5351_CLK2);              // BFO

}

else if (mode == 0) // CW

{
 si5351.set_freq(((txcio + freq - cwoffset - filteroffset) * 100ULL), SI5351_CLK0);     // VFO
 si5351.set_freq(((txcio  - filteroffset) * 100ULL), SI5351_CLK2);              // BFO
}
  
}
