/*************************************************************************************************************************************************************************
  Nitin William - VU3GAO September/2021 - Version 1.5
  Easy Bitx HF Mono Band Rigs(80m, 40m, 20m) VFO / BFO sketch with Si5351 and Arduino Nano with 10Mhz IF.
  Hardware VU3SUA VFO/BFO Board with 16X2 LCD
  Code derived from J. CesarSound - ver 1.0 - Dec/2020.
  https://groups.io/g/BITX20/topic/bitx_40_dds_vfo/4104090?p=
  https://blog.wokwi.com/5-ways-to-blink-an-led-with-arduino/
  https://maxpromer.github.io/LCD-Character-Creator/
  https://www.riyas.org/2016/12/a-simple-si5351-vfo-and-bfo-with-s-meter.html
 *************************************************************************************************************************************************************************/

//Libraries
#include <Wire.h>                 //Arduino library
#include <EEPROMex.h>             //https://thijs.elenbaas.net/2012/07/extended-eeprom-library-for-arduino
#include <Rotary.h>               //Ben Buxton https://github.com/brianlow/Rotary
#include <si5351.h>               //Etherkit https://github.com/etherkit/Si5351Arduino
#include <EasyButton.h>           //https://easybtn.earias.me/docs/introduction
#include <LiquidCrystal.h>        //Arduino library
#include <elapsedMillis.h>        //https://github.com/pfeerick/elapsedMillis/wiki


#define fMax  7200000UL           //7200000UL / 14500000UL
#define fMin  7000000UL           //7000000UL / 14000000UL
#define ddsCal 359000
#define LCD_RS  5
#define LCD_E   6
#define LCD_D4  7
#define LCD_D5  8
#define LCD_D6  9
#define LCD_D7  10
#define encoderButtonPin 11
#define bfoAddress 8              //EEPROM location for BFO value
#define vfoAddress 12             //EEPROM for saving vfo


unsigned long vfo = 7050000UL;    //Enter your initial frequency at startup, ex: 7000000 = 7MHz, 10000000 = 10MHz
unsigned long bfo = 9996850UL;    //BFO frequency for 10Mhz filter Bw = 3.2Khz
unsigned long opsfreq = 0UL;      //Operating freq to be generated by Si5351
unsigned long offset = 3200UL;    //IF Offset
unsigned long freqold, fstep;
unsigned long bfoold;
int stp;                          //Frequency step counter
byte tower[8] =
{
  B11111,
  B10101,
  B01110,
  B00100,
  B00100,
  B00100,
  B00100,
  B00000
};
byte blank[8] =
{
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000,
  B00000
};
bool setupmode = false;
bool memTag = false;


Rotary r = Rotary(3, 2);          // Encoder defined for Interrupt Pin 2,3
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
Si5351 si5351(0x60);              //Si5351 I2C Address 0x60
EasyButton encoderButton (encoderButtonPin, true); //Create encoderButton Object as input with internal pullup resistors
elapsedMillis memTimer;


ISR(PCINT2_vect)
{
  char result = r.process();
  if (result == DIR_CW)
    {
      if (! setupmode )
      set_frequency(1);
      else
      set_bfofrequency(1);
    }
  else if (result == DIR_CCW)
    {
      if (! setupmode )
      set_frequency(-1);
      else
      set_bfofrequency(-1);
    }
}


void set_frequency(short dir)
{
  if (dir == 1) vfo = vfo + fstep;
  if (vfo >= fMax) vfo = fMax;                            //Upper tuning limit
  if (dir == -1) vfo = vfo - fstep;
  if (vfo < fMin) vfo = fMin;                             //lower tuning limit
}


void set_bfofrequency(short dir)
{
  if (dir == 1) bfo = bfo + 50;
  if (bfo >= 10003500) bfo = 10003500;                            
  if (dir == -1) bfo = bfo - 50;
  if (bfo < 9996500) bfo = 9996500;
}


void setup()
{
  Serial.begin(57600);
  encoderButton.begin();
  Wire.begin();
  r.begin(true);                                         //Enable the Arduino's internal weak pull-ups for the rotary's pins external pullup
  lcd.begin(16, 2);
  delay (20);
  lcd.createChar(0, tower);
  lcd.createChar(1, blank);
  lcd.clear();
  si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, ddsCal);       //Initialize Si5351, with 25Mhz Xtal
  si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
  si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_8MA);  //Output current 2MA, 4MA, 6MA or 8MA
  si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_8MA);  //Output current 2MA, 4MA, 6MA or 8MA
  si5351.output_enable(SI5351_CLK0, 1);                  //1 - Enable / 0 - Disable CLK
  si5351.output_enable(SI5351_CLK1, 0);
  si5351.output_enable(SI5351_CLK2, 1);
  si5351.update_status();
  cli();
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();                                                // Enable all interrupts
  display_banner();
  bfoold = EEPROM.readLong(bfoAddress);
  if (bfoold < 9996500 || bfoold > 10003500 )
    {
      EEPROM.updateLong (bfoAddress,bfo);
    }
  else
    {
      bfo = bfoold;
    }
  freqold = EEPROM.readLong(vfoAddress);
  if (freqold < fMin || freqold > fMax )
    {
      EEPROM.updateLong (vfoAddress,vfo);
    }
   else
    {
      vfo = freqold;
    }
  si5351.set_freq(bfo * 100, SI5351_CLK2);              //Set bfo
  stp = 4;
  setsteps();
  encoderButton.onPressed(setsteps);
  encoderButton.onPressedFor(2000, set_bfo);
  tunegen();
  display_freq();
  display_radix();
}


void loop()
{
  encoderButton.read();
  if (freqold != vfo)
  {
    memTimer = 0;
    memTag = true;
    tunegen();
    freqold = vfo;
  }
  if (memTimer > 60000) save_vfo();
  display_freq();
  display_radix();
  flash_heart(); 
}


void setsteps()
{
  switch (stp)
  {
    case 1: stp = 2; fstep = 1; break;
    case 2: stp = 3; fstep = 10; break;
    case 3: stp = 4; fstep = 100; break;
    case 4: stp = 5; fstep = 1000; break;
    case 5: stp = 1; fstep = 10000; break;
  }
}


void tunegen()
{
  if (vfo >= 14000000 )
  {
    lcd.setCursor(2, 1);
    lcd.print("USB");
    opsfreq =  vfo - bfo;                                //Low side LO injection, no side band inversion
    si5351.set_freq(opsfreq * 100, SI5351_CLK0);         //Update operating frequency
  }
  else
  {
    lcd.setCursor(2, 1);
    lcd.print("LSB");
    opsfreq =  vfo + bfo;                                //High side LO injection, Side band inversion
    si5351.set_freq(opsfreq * 100, SI5351_CLK0);         //Update operating frequency
  }
}


void display_freq()
{
  uint16_t f;
  lcd.setCursor(2, 0);
  f = vfo / 1000000;
  if (f < 10)
    lcd.print('0');
  lcd.print(f);
  lcd.print('.');
  f = (vfo % 1000000) / 1000;
  if (f < 100)
    lcd.print('0');
  if (f < 10)
    lcd.print('0');
  lcd.print(f);
  lcd.print('.');
  f = vfo % 1000;
  if (f < 100)
    lcd.print('0');
  if (f < 10)
    lcd.print('0');
  lcd.print(f);
  lcd.print("Hz");
}


void display_radix()
{
  lcd.setCursor(9, 1);
  if (stp == 2) lcd.print("001Hz"); if (stp == 3) lcd.print("010Hz"); if (stp == 4) lcd.print("100Hz");
  if (stp == 5) lcd.print("01KHz"); if (stp == 1) lcd.print("10KHz");
  lcd.setCursor(2, 1);
}


void set_bfo(short dir)
{
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("BFO  SETUP");
  delay (1000);
  do 
    {
      setupmode = true;
      encoderButton.read();
      lcd.setCursor(4, 1);
      if (bfo < 10000000 )lcd.print("0");
      lcd.print(bfo);
      si5351.set_freq(bfo * 100, SI5351_CLK2);
    } while (!encoderButton.isPressed());                             
  si5351.set_freq(bfo * 100, SI5351_CLK2);
  lcd.setCursor(3, 0);
  lcd.print("Saving BFO");
  EEPROM.updateLong (bfoAddress,bfo);
  delay (2000);
  lcd.clear();
  if ( stp == 1 ) stp = 6;
  stp = stp - 1;
  setupmode = false;
}


void display_banner()
{
  lcd.setCursor (3, 0);
  lcd.print("Easy Bitx");
  lcd.setCursor (3, 1);
  if (fMin >= 3000000 && fMin <= 4000000)
    lcd.print("80m  Band");
  if (fMin >= 7000000 && fMin <= 7500000)
    lcd.print("40m  Band");
  if (fMin >= 14000000 && fMin <= 14500000)
    lcd.print("20m  Band");
  delay (3000);
  lcd.clear();
}


void flash_heart()
{
  lcd.setCursor (0, 0);
  lcd.write (byte((millis() / 1000) % 2 ));        //Logic give 0 or 1 as paramtere to function byte()
}


void save_vfo()
{
  memTimer = 0;
  if (memTag) 
  {
    memTag = false;
    EEPROM.writeLong (vfoAddress,vfo);
  } 
}