// LC meter based on Neil Heckt oscillator and RC/LC time constant measurer for high values
// four reed relays control mode
// Separate functions for HI/LO C/L ranges
// Algorithm for choosing best results OK- sometimes chooses Hi C as Lo L and vv
// working- collates data and outputs
// 20x4 I2C LCD connected
// when reading is stable, continues to measure and average
// repeated recalibration to account for drift

#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>

LiquidCrystal_PCF8574 lcd(0x27);  //default address is 0x27, could be 0x20-0x27

#define OHMSSYMBOL 0xF4

#define RELAY1 7
#define RELAY2 6
#define RELAY3 9
#define RELAY4 8

// relay combination modes
#define MODEHILC 0
#define MODECALNONE 2
#define MODECALC2 3
#define MODECTEST 6
#define MODELTEST 8

#define NOTHING 0
#define CAPACITOR 1
#define INDUCTOR 2

#include <FreqCount.h>

#define R1PIN A1
#define R2PIN A2
#define ASENSEPIN A3
#define LTHRESHOLD 138
#define R1VACT A0
#define NOTHING 0
#define CAPACITORHI 1
#define CAPACITORLO 2
#define INDUCTORHI 3
#define INDUCTORLO 4
#define CALDELAY 10000

//these ones are cal values that should be stored in eeprom
float r1value=130.0;      //resistor 1 (R12)
float r2value=1300.0;     //resistor 2 (R11)
float C2VAL=1.0e-9;       //C2
float L1VAL=100.0e-6;     //L1
float Cpara=0;            //stray capacitance
float Lpara=0;            //stray inductance

//general use variables
float C1VAL=1.0e-9;          //not a calibration value, set by frequency
float F1=0;
float F2=0;
float FCmin=10000.0;         //based on component value range
float FLmin=100.0;          //seems to be stable
float F3C,F3L;

#define BUFSIZE 20
char buf[BUFSIZE+1]="";
char unit=0;

char eepromid[]="SC04106181";
char checkid[12]="";
unsigned long lastcalt;

void setup() {
  relaySetup();
  Wire.begin();
  lcd.begin(20, 4);
  lcd.clear();
  lcd.setBacklight(255);
  Serial.begin(115200);
  loadFromEEPROM();
  showCurrent();      //display calibration values
  DIDR0=0xFF;         //disable digital input buffers on analog pins. By default is 0x00, unchanged by Arduino code
  doCalibration(2000);  
  lastcalt=millis();
}

void loop(){
  float Clo,Chi,Llo,Lhi,val;
  byte mode=NOTHING;
  static float lastval=0;
  static byte lastmode=0; 
  static bool isStable=0;          //flag that we've got a stable component to do a rolling average reading
  static float valSum=0;           //keep track of rolling average
  static unsigned long valCount=0;  
  //do periodic recalibation
  if((millis()-lastcalt>CALDELAY)&&(!isStable)){      //only recalibrate if it's been >10s and we're not reading a component
    doCalibration(1000);  
    lastcalt=millis();    
  }
  //run through four test modes
  Clo=testClo(100);         //do fast test for C
  Chi=testChi();            //RC method for C
  Llo=testLlo(100);         //do fast test for L
  Lhi=testLhi();            //RL method for L
  if((F3L>=FLmin)&&(F3L<=F1)){      //probably an inductor
    mode=INDUCTOR;
    if(Lhi<100e-3){                 //valid range for oscillator method
      mode=INDUCTORLO;
      val=offset(Llo,Lpara);
    }else{
      mode=INDUCTORHI;
      val=offset(Lhi,Lpara);
    }
  }else{                            //probably a capacitor
    if(Chi<2.0e-6){                 //valid range for oscillator method
      mode=CAPACITORLO;
      val=offset(Clo,Cpara);
    }else{
      mode=CAPACITORHI;
      val=offset(Chi,Cpara);
    }
  }
  //is component stable?
  if((abs(val-lastval)*20<lastval)&&(mode==lastmode)){    //if within 5% of last value and same mode
    isStable=1;
  }else{
    valSum=0;
    valCount=0;
    isStable=0;
  }
  //do subsamples if stable
  if(isStable){
    switch(mode){
      case CAPACITORLO:valSum+=offset(testClo(1000),Cpara);valCount++;break;
      case CAPACITORHI:valSum+=offset(testChi(),Cpara);valCount++;break;
      case INDUCTORLO:valSum+=offset(testLlo(1000),Lpara);valCount++;break;
      case INDUCTORHI:valSum+=offset(testLhi(),Lpara);valCount++;break;
    }
  }
  lastval=val;
  lastmode=mode;
  //LCD output
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("L:"));
  if(Lhi<100e-3){                 //valid range for oscillator
    printSIlcd(offset(Llo,Lpara));
  }else{
    printSIlcd(offset(Lhi,Lpara));
  }
  lcd.print(F("H"));
  lcd.setCursor(0,1);
  lcd.print(F("C:"));
  if(Chi<2.0e-6){                 //valid range for oscillator
    printSIlcd(offset(Clo,Cpara));
  }else{
    printSIlcd(offset(Chi,Cpara));
  }
  lcd.print(F("F"));
  lcd.setCursor(0,2);
  if((mode==INDUCTORHI)||(mode==INDUCTORLO)){lcd.print(F("--> Inductor"));}
  if((mode==CAPACITORHI)||(mode==CAPACITORLO)){lcd.print(F("--> Capacitor"));}
  lcd.setCursor(11,0);
  lcd.print(F3L,0);
  lcd.print(F("Hz"));
  lcd.setCursor(11,1);
  lcd.print(F3C,0);
  lcd.print(F("Hz"));
  if(valCount>0){
    lcd.setCursor(0,3);    
    if((mode==INDUCTORHI)||(mode==INDUCTORLO)){
      lcd.print(F("L="));
      printSIlcd(valSum/valCount,5);
      lcd.print(F("H "));
    }
    if((mode==CAPACITORHI)||(mode==CAPACITORLO)){
      lcd.print(F("C="));
      printSIlcd(valSum/valCount,5);
      lcd.print(F("F "));
    }
    if(valCount<10000){
      lcd.print(valCount);
    }else{
      lcd.print(F("9999"));
    }
    lcd.print(F(" samp"));
  }  
  //check for command to enter calibration mode
  while(Serial.available()){
    if(Serial.read()=='C'){doCals();}
  }
}

void relaySetup(){
  pinMode(RELAY1,OUTPUT);           //set to outputs, turn off
  pinMode(RELAY2,OUTPUT);
  pinMode(RELAY3,OUTPUT);
  pinMode(RELAY4,OUTPUT);
  relays(0);
}

float testClo(int n){               //use oscillator to test for C
  float F3;
  pinMode(R1PIN,INPUT);             //turn off digital pins
  pinMode(R2PIN,INPUT);
  relays(MODECTEST);                //set relays for capacitor on test pins
  delay(500);                       //let oscillator stabilise
  F3=GetFrequency(n);               //get F3
  F3C=F3;
  return getCfromF3(F1,F3,C1VAL);
}

float testLlo(int n){               //use oscillator to test for L
  float F3;
  pinMode(R1PIN,INPUT);             //turn off digital pins
  pinMode(R2PIN,INPUT);
  relays(MODELTEST);                //set relays for inductor on test pins
  delay(500);                       //let oscillator stabilise
  F3=GetFrequency(n);               //get F3
  F3L=F3;
  return getCfromF3(F1,F3,L1VAL);
}

float testChi(){                    //use RC method for C
  int a1,a2,a3;
  unsigned long t0,t1;
  float c=0;                        //default to zero if we can't read
  relays(MODEHILC);
  digitalWrite(R1PIN,LOW);                      //use R1 to discharge
  pinMode(R1PIN,OUTPUT);
  delay(500);
  pinMode(R1PIN,INPUT);
  digitalWrite(R2PIN,LOW);
  pinMode(R2PIN,OUTPUT);
  if(analogRead(ASENSEPIN)>300){delay(500);}    //if not discharged, wait a bit more
  a1=analogRead(ASENSEPIN);
  a1=analogRead(ASENSEPIN);                     //double read to avoid glitches
  digitalWrite(R2PIN,HIGH);                     //start charging
  t0=micros();                                  //start timing
  a2=analogRead(ASENSEPIN);                     //record value
  delay(1);
  while((micros()-t0<5000000)&&(analogRead(ASENSEPIN)<300)){delay(1);}   //wait for reasonable rise and reasonable time
  t1=micros();                                  //stop timing
  a3=analogRead(ASENSEPIN);                     //record value
  digitalWrite(R2PIN,LOW);                      //back to discharging for next time
  if((a3>a2)&&(a3<1022)){                       //log doesn't like negatives, low values will be unreliable
    c=((t1-t0)/log((1023.0-a2)/(1023.0-a3))/(r2value));
  }
  return c/1000000;                             //because t is counted in us, c is in uF
}

float testLhi(){                                        // use RL method for L, time for about 2 tc's
  byte saveADCSRA=ADCSRA;                               // save default value of ADCSRA, as we use faster prescaler for L calculations
  int vact,vesr;                                        // actual pin output due to sagging, voltage due to ESR
  unsigned long t,tnew=0;                               // timing variables
  float resr,kconst;                                    // for L calculation
  float l=0;                                            // default to 0
  relays(MODEHILC);
  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
  ADCSRA |= bit (ADPS1);                                // prescaler of 4 for faster sampling (2 works on some boards)
  digitalWrite(R1PIN,LOW);                              // ensure no current is flowing
  pinMode(R1PIN,OUTPUT);
  delay(300);                                           // wait a bit
  analogRead(ASENSEPIN);                                // let ADC settle
  digitalWrite(R1PIN,HIGH);                             // start pulse
  t=micros();                                           // start timer
  tnew=t;
  while((analogRead(ASENSEPIN)>LTHRESHOLD)&&(micros()-t<5000)){tnew=micros();}//timeout in case of open circuit  
  t=tnew-t;
  analogRead(R1VACT);                                   // do two readings to let it settle
  vact=analogRead(R1VACT);
  analogRead(ASENSEPIN);
  analogRead(ASENSEPIN);
  delay(200);                                           // allow inductive effects to pass to measure ESR
  vesr=analogRead(ASENSEPIN);                           // terminal V
  resr=r1value*vesr/vact;                               // terminal R
  digitalWrite(R1PIN,LOW);                              // ensure no current is flowing
  if(vact!=vesr){
    kconst=LTHRESHOLD/(float(vact-vesr));               // adjusted time constant
  }
  if(kconst>0){                                         // valid tc
    l=(r1value+resr)*t*1023/(-log(kconst))/vact;        // calculate L
  }
  delay(200);
  ADCSRA=saveADCSRA;                                    // restore ADCSRA to defaults
  return l/1000000;                                     // result in uH as t is in us
}

void relays(int n){                                     //set relays from bitwise pattern
  digitalWrite(RELAY1,(n&1)?HIGH:LOW);
  digitalWrite(RELAY2,(n&2)?HIGH:LOW);
  digitalWrite(RELAY3,(n&4)?HIGH:LOW);
  digitalWrite(RELAY4,(n&8)?HIGH:LOW);
}

long GetFrequency(long n) {                   //count pulses for n milliseconds (allows varying accuracies)
  long Fcount;
  if(n<1){n=1;}                               //no dividing by zero or negatives
  FreqCount.begin(n);
  while(!FreqCount.available()) {}
  Fcount=FreqCount.read();
  FreqCount.end();
  return Fcount*1000.0/n;
}


float getF(float C, float L){   //calculate F from C and L
  return 1/(TWO_PI*sqrt(C*L));
}

float getC(float F, float L){   //calculate C from F and L (is actually the same as getL(F,C))
  return 1/(TWO_PI*TWO_PI*F*F*L);
}

float getCfromF3(float F1,float F3, float C1){    //get C based on Frequency change, can be used for L
  if(F3<5){return 0;}                             //don't divide by zero
  return C1*((F1*F1)/(F3*F3)-1);
}

void printSI(Stream &stream,float n){                //handle default case
  printSI(stream,n,4);
}

void printSI(Stream &stream,float n,int s){            //print s sig figs with SI multiplier (do better with print to array and then format?)
  float nmod=n;
  char SImult=0;
  if(n>1.0e9){nmod=n*1.0e-9;SImult='G';}
  else if(n>1.0e6){nmod=n*1.0e-6;SImult='M';}
  else if(n>1.0e3){nmod=n*1.0e-3;SImult='k';}
  else if(n>1.0){nmod=n;SImult=' ';}
  else if(n>1.0e-3){nmod=n*1.0e3;SImult='m';}
  else if(n>1.0e-6){nmod=n*1.0e6;SImult='u';}
  else if(n>1.0e-9){nmod=n*1.0e9;SImult='n';}
  else if(n>1.0e-15){nmod=n*1.0e12;SImult='p';}     //show less than 1pico
  else {nmod=n;SImult=' ';}         //n is close enough to 0
  if(nmod>=100){stream.print(nmod,s<3?0:s-3);stream.write(SImult);}
  else if(nmod>=10){stream.print(nmod,s<2?0:s-2);stream.write(SImult);}
  else{stream.print(nmod,s<1?0:s-1);stream.write(SImult);}
}

void printSIlcd(float n){
  printSIlcd(n,4);      //default case
}

void printSIlcd(float n,int s){
  float nmod=n;
  char SImult=0;
  if(n>1.0e9){nmod=n*1.0e-9;SImult='G';}
  else if(n>1.0e6){nmod=n*1.0e-6;SImult='M';}
  else if(n>1.0e3){nmod=n*1.0e-3;SImult='k';}
  else if(n>1.0){nmod=n;SImult=' ';}
  else if(n>1.0e-3){nmod=n*1.0e3;SImult='m';}
  else if(n>1.0e-6){nmod=n*1.0e6;SImult='u';}
  else if(n>1.0e-9){nmod=n*1.0e9;SImult='n';}
  else if(n>1.0e-15){nmod=n*1.0e12;SImult='p';}     //show less than 1pico
  else {nmod=n;SImult=' ';}         //n is close enough to 0
  if(nmod>=100){lcd.print(nmod,s<3?0:s-3);lcd.write(SImult);}
  else if(nmod>=10){lcd.print(nmod,s<2?0:s-2);lcd.write(SImult);}
  else{lcd.print(nmod,s<1?0:s-1);lcd.write(SImult);}  
}

void doCals(){              //this does the thorough setup cals
  bool calsDone=0;
  int m;
  float n=0;  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("CALIBRATION MODE:"));
  lcd.setCursor(0,1);
  lcd.print(F("See serial monitor"));
  Serial.println();
  Serial.println(F("Calibration Mode:"));
  delay(10);
  while(Serial.available()){Serial.read();}     //empty buffer for trailing CR/LF
  while(!calsDone){
    m=doMenu();  
    if(m<32){m='X';}     //no valid input detected, exit
    Serial.write(m);
    Serial.println(F(" selected."));
    lcd.setCursor(0,3);
    lcd.write(m);
    switch(m){
      case 'A':
        n=getEntry();
        if((n>0)&&((unit=='R')||(unit==0))){     //valid value and suitable units
          r1value=n;
          Serial.print(F("130 Ohm changed to "));
          printSI(Serial,r1value,7);
          Serial.println(F("Ohms"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'B':
        n=getEntry();
        if((n>0)&&((unit=='R')||(unit==0))){     //valid value and suitable units
          r2value=n;
          Serial.print(F("1.3kOhm changed to "));
          printSI(Serial,r2value,7);
          Serial.println(F("Ohms"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'C':
        n=getEntry();
        if((n>0)&&((unit=='F')||(unit==0))){     //valid value and suitable units
          C2VAL=n;
          C1VAL=(C2VAL*F2*F2)/(F1*F1-F2*F2);  //calculate C1 from new C2 value
          Serial.print(F("C2 changed to "));
          printSI(Serial,C2VAL,7);
          Serial.println(F("F"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'D':
        n=getEntry();
        if((n>0)&&((unit=='H')||(unit==0))){     //valid value and suitable units
          L1VAL=n;
          Serial.print(F("L1 changed to "));
          printSI(Serial,L1VAL,7);
          Serial.println(F("H"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'E':
        n=getEntry();
        if((n>=0)&&((unit=='F')||(unit==0))){     //valid value and suitable units
          Cpara=n;
          Serial.print(F("Cpara changed to "));
          printSI(Serial,Cpara,7);
          Serial.println(F("F"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'F':
        n=getEntry();
        if((n>=0)&&((unit=='H')||(unit==0))){     //valid value and suitable units
          Lpara=n;
          Serial.print(F("Lpara changed to "));
          printSI(Serial,Lpara,7);
          Serial.println(F("H"));          
        }else{
          Serial.println(F("Check value and units."));
        }
        break;
      case 'G':
        n=testClo(1000);
        printSI(Serial,n);
        Serial.println(F("F"));
        if(confirm()){
          Cpara=n;
          Serial.print(F("Cpara changed to "));
          printSI(Serial,Cpara,7);
          Serial.println(F("F"));                    
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print(F("Cpara updated"));
        }else{
          Serial.println(F("No change made"));
        }
        break;
      case 'H':
        n=testLlo(1000);
        printSI(Serial,n);
        Serial.println(F("H"));
        if(confirm()){
          Lpara=n;
          Serial.print(F("Lpara changed to "));
          printSI(Serial,Lpara,7);
          Serial.println(F("H"));                    
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print(F("Lpara updated"));
        }else{
          Serial.println(F("No change made"));
        }
        break;
      case 'L':
        setDefaults();
        C1VAL=(C2VAL*F2*F2)/(F1*F1-F2*F2);  //calculate C1 from new C2 value
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print(F("Loaded from defaults"));
        Serial.println(F("Loaded from defaults"));
        break;
      case 'P':showCurrent();break;
      case 'S':
        saveToEEPROM();
        lcd.clear();
        lcd.setCursor(0,0);
        lcd.print(F("Saved to EEPROM"));
        Serial.println(F("Saved to EEPROM"));        
        break;
      default:
      case 'X':
        calsDone=1;
        break;            
    }
  }
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("Calibration done."));
  Serial.println(F("Calibration finished."));
}

bool confirm(){     //ask Y/N on serial monitor, return true for Y
  bool ret=0;
  Serial.println(F("Proceed with change (Y/N)?"));
  while(!Serial.available()){}
  if(Serial.read()=='Y'){ret=1;}                  //only on Y, not y
  delay(10);
  while(Serial.available()){Serial.read();}       //clear trailing CR/LF from buffer
  return ret;
}

int doMenu(){       //displays menu and returns choice
  int d,r=-1;
  Serial.println();
  Serial.println(F("A:Enter 130 Ohm value"));
  Serial.println(F("B:Enter 1.3kOhm value"));
  Serial.println(F("C:Enter C2 value"));
  Serial.println(F("D:Enter L1 value"));
  Serial.println(F("E:Enter Cparasitic value"));
  Serial.println(F("F:Enter Lparasitic value"));
  Serial.println(F("G:Auto detect Cparasitic (leave terminals open circuit)"));
  Serial.println(F("H:Auto detect Lparasitic (short circuit leads)"));
  Serial.println(F("L:Load defaults"));
  Serial.println(F("P:Print current values"));
  Serial.println(F("S:Save to EEPROM"));
  Serial.println(F("X:Exit calibration"));
  Serial.println(F("Choose an option"));
  while(!Serial.available()){}
  delay(10);                          //purge any extra characters like CR/LF
  while(Serial.available()){
    d=Serial.read();
    if(d>31){r=toUpperCase(d);}           //flag character
  }
  return r;
}

float getEntry(){     //awaits for a number to be entered
  float f,m;
  int d;
  bool entryDone=0;
  Serial.println(F("Enter a value:"));
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("Enter a value:"));
  while(!entryDone){
    while(Serial.available()){
      d=Serial.read();
      if(d==13){        //enter pressed
        byte p=0;
        entryDone=1;
        m=1;
        while((m==1)&&(buf[p]!=0)){
          switch(buf[p]){
            case 'p': m=1.0e-12;break;
            case 'n': m=1.0e-9;break;
            case 'u': m=1.0e-6;break;
            case 'm': m=1.0e-3;break;
            case 'k': m=1.0e3;break;
            case 'M': m=1.0e6;break;
            case 'G': m=1.0e9;break;
          }
          p++;
        }
        unit=0;
        if(m==1){p=0;}
        while((unit==0)&&(buf[p]!=0)){        
          switch(buf[p]){
            case 'f':
            case 'F': unit='F';break;
            case 'h':
            case 'H': unit='H';break;
            case 'r':
            case 'R': unit='R';break;                            
          }
          p++;
        }      
        f=atof(buf);
        Serial.print(f*m,13);
        Serial.write((byte)unit);
        Serial.println();        
        lcd.setCursor(0,1);
        lcd.print(f*m,13);
        if(unit){lcd.write((byte)unit);}
        buf[0]=0;
      }
      if(d>31){                       //only ascii
        buf[strlen(buf)+1]=0;
        buf[strlen(buf)]=d;
        buf[BUFSIZE]=0;               //keep inside buffer
      }
    }    
  }
  return f*m;
}

float offset(float n, float a){       //adjusts n (down) by a, if result is negative, returns 0
  n=n-a;
  if(n<0){n=0;}
  return n;
}

void loadFromEEPROM(){
  bool eepromOK=1;              //flag if there is a problem  
  readEEPROMid(checkid,strlen(eepromid));       //read id flag
  if(!strmatch(checkid,eepromid)){
    Serial.println(F("EEPROM signature not found, loading and saving defaults"));
    lcd.setCursor(0,0);
    lcd.print(F("Set EEPROM to init"));
    setDefaults();                    //load defaults
    saveToEEPROM();                   //save these values
    writeEEPROMid(eepromid);          //set id flag in EEPROM
  }else{
    Serial.println(F("EEPROM signature found"));
    lcd.setCursor(0,0);
    lcd.print(F("EEPROM ID OK"));    
  }
  delay(300);
  EEPROM.get(1,r1value);        //start at 1 in case of errant EEPROM writing
  EEPROM.get(5,r2value);        //floats are 4 bytes
  EEPROM.get(9,C2VAL);
  EEPROM.get(13,L1VAL);
  EEPROM.get(17,Cpara);
  EEPROM.get(21,Lpara);
  if(!(r1value>0)){eepromOK=0;}   //use !sense to catch NAN etc.
  if(!(r2value>0)){eepromOK=0;}
  if(!(C2VAL>0)){eepromOK=0;}
  if(!(L1VAL>0)){eepromOK=0;}
  if(!(Cpara>=0)){eepromOK=0;}
  if(!(Lpara>=0)){eepromOK=0;}
  if(!eepromOK){
    Serial.println(F("EEPROM Error, defaults loaded but not saved"));
    lcd.setCursor(0,0);
    lcd.print(F("EEPROM Error:"));
    setDefaults();
  }else{
    Serial.println(F("EEPROM loaded OK"));
  }
}

void saveToEEPROM(){
  EEPROM.put(1,r1value);        //start at 1 in case of errant EEPROM writing
  EEPROM.put(5,r2value);        //floats are 4 bytes
  EEPROM.put(9,C2VAL);
  EEPROM.put(13,L1VAL);
  EEPROM.put(17,Cpara);
  EEPROM.put(21,Lpara);  
}

void setDefaults(){
  r1value=130.0;      //resistor 1 (R12)
  r2value=1300.0;     //resistor 2 (R11)
  C2VAL=1.0e-9;       //C2
  L1VAL=100.0e-6;     //L1
  Cpara=0;            //stray capacitance
  Lpara=0;            //stray inductance
}

void showCurrent(){   //display current values
  //output to serial monitor
  Serial.println();
  Serial.print(F("R12:"));
  printSI(Serial,r1value);
  Serial.println(F("Ohms"));
  Serial.print(F("R11:"));
  printSI(Serial,r2value);
  Serial.println(F("Ohms"));
  Serial.print(F("C2:"));
  printSI(Serial,C2VAL);
  Serial.println(F("F"));
  Serial.print(F("L1:"));
  printSI(Serial,L1VAL);
  Serial.println(F("H"));
  Serial.print(F("Cp:"));
  printSI(Serial,Cpara);
  Serial.println(F("F"));
  Serial.print(F("Lp:"));
  printSI(Serial,Lpara);
  Serial.println(F("H"));
//output to LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(F("Calibration vals:"));
  lcd.setCursor(0,1);
  lcd.print(F("R12:"));
  printSIlcd(r1value,2);
  lcd.write(OHMSSYMBOL);
  lcd.setCursor(10,1);
  lcd.print(F("R11:"));
  printSIlcd(r2value,2);
  lcd.write(OHMSSYMBOL);
  lcd.setCursor(0,2);
  lcd.print(F("C2:"));
  printSIlcd(C2VAL,3);
  lcd.write('F');
  lcd.setCursor(10,2);
  lcd.print(F("L1:"));
  printSIlcd(L1VAL,3);
  lcd.write('H');
  lcd.setCursor(0,3);
  lcd.print(F("Cp:"));
  printSIlcd(Cpara,3);
  lcd.write('F');
  lcd.setCursor(10,3);
  lcd.print(F("Lp:"));
  printSIlcd(Lpara,3);
  lcd.write('H');
}

void writeEEPROMid(char* id){                       //write id to end of eeprom
  int i;
  unsigned int s=EEPROM.length();
  unsigned int n=strlen(id);
  if(n>s-2){return;}                //fail if it won't fit
  for(i=0;i<n+1;i++){
    EEPROM.write(s-n-1+i,id[i]);    
  }  
}

void readEEPROMid(char* id,unsigned int idsize){      //read idsize+null bytes into id buffer
  unsigned int i=0;
  byte b=1;                 //to start in while loop
  unsigned int s=EEPROM.length();
  unsigned int p=s-idsize-1;
  if(p<0){return;}
  while(b&&(p<s)){
    b=EEPROM.read(p);
    id[i]=b;
    i++;
    p++;
  }  
}

bool strmatch(char* x, char* y){   //true if strings match
  unsigned int n;
  n=strlen(x);
  if(n!=strlen(y)){return 0;}       //can't match if different lengths
  for(int i=0;i<n;i++){
    if(x[i]!=y[i]){return 0;}      //different at some point
  }
  return 1;                        //all match
}

void doCalibration(unsigned int t){
  lcd.setCursor(0,0);
  lcd.print(F("Self-calibrating    "));  
  relays(MODECALNONE);
  delay(1000);
  F1=GetFrequency(t);              //find F1 frequency
  Serial.print(F("F1:"));
  Serial.println(F1);
  relays(MODECALC2);
  delay(1000);
  F2=GetFrequency(t);              //find F2 frequency
  Serial.print(F("F2:"));
  Serial.println(F2);
  C1VAL=(C2VAL*F2*F2)/(F1*F1-F2*F2);  //calculate C1
  Serial.print(F("C1:"));
  printSI(Serial,C1VAL);
  Serial.println(F("F"));
}