//******************************************************
// Arduino Datalogger and Current Sensing, Version 0
// Author: Vinson Guov
// Date: May 2020
//******************************************************

#include <EEPROM.h>

// Channel Configuration
// analog# = Channel On/Off
// current# = Current Sensing On/Off
// dir# = Unidirectional or Bidirectional Current Sensing
// range# = 5V or 15V
bool analog0; bool range0; bool current0; bool dir0;
bool analog1; bool range1; bool current1; bool dir1;
bool analog2; bool range2;
bool analog3; bool range3;
bool analog6; bool range6;
bool analog7; bool range7;

// scale# => multiply raw ADC values with this to get voltage/current
float scale0;
float scale1;
float scale2;
float scale3;
float scale6;
float scale7;

float period; // sampling period in seconds
long duration; // datalogging duration in seconds

// ADC Channel Values
float value0; // ADC channel 0 value
float value1; // ADC channel 1 value
float value2; // ADC channel 2 value
float value3; // ADC channel 3 value
float value6; // ADC channel 6 value
float value7; // ADC channel 7 value

// User Input
char answerchar; // stores user input character
int answerint; // stores user input number
bool loaded; // true if config has been loaded from EEPROM
bool saved; // true if new config has been saved

// Datalogger Timing
float startTime; // microcontroller time when datalogging started
float currentTime; // microcontroller time during current ADC sample
float prevTime; // microcontroller time during previous ADC sample

void setup() {
  analogReference(EXTERNAL); // sets analog reference to AREF pin, connected to 5V precision reference IC
  Serial.begin(115200,SERIAL_8N1); // sets baud rate to 115200, 8 data bits, no parity bit, 1 stop bit
  Serial.setTimeout(100);

  Serial.println("Arduino Nano Datalogger and Current Sensing (Verson 0)");
  Serial.println("Channel Setup\n");
  Serial.println("Note: only channels A0 & A1 can be configured to report current from the CSENS1/CSENS2 outputs.");

  loaded = loadConfigEEPROM(); // loads the previously saved channel config if wanted by user

  if (!loaded) // skip channel config if config already loaded from EEPROM
    setupChannels(); // asks for user input to configure channels

  setupScaling(); // takes config parameters and produces voltage scale factor for each channel

  if (!loaded) // skip saving channel config if already loaded with saved config
    saveConfigEEPROM(); // saves channel config parameters into EEPROM for future use

  setupSampling(); // asks for user input to configure sampling period and duration

  //debugConfig(); // for debugging, comment out if not needed

  Serial.println("\nEnter any character to start datalogging.");
  Serial.println("Note: clear the serial monitor before datalogging for easier data export.");
  Serial.println("Each line of data will have this order: Time(seconds),A0,A1,A2,A3,A6,A7");
  waitForAnswer();

  startTime = millis()/1e3;
}

void loop() {
  while((currentTime-startTime) <= duration) { // while total datalogging time has not exceeded specified duration
    currentTime = millis()/1e3;
    while((currentTime-prevTime) < period) // wait until it is time to sample again
      currentTime = millis()/1e3;
    value0 = analogRead(A0);
    value0 = analogRead(A0);
    value1 = analogRead(A1);
    value1 = analogRead(A1);
    value2 = analogRead(A2);
    value2 = analogRead(A2);
    value3 = analogRead(A3);
    value3 = analogRead(A3);
    value6 = analogRead(A6);
    value6 = analogRead(A6);
    value7 = analogRead(A7);
    value7 = analogRead(A7);
    Serial.print(currentTime - startTime,3);
    Serial.print(",");
    if ((dir0 && current0) || !current0)
      Serial.print(value0*scale0);
    else
      Serial.print(value0*scale0-1.0);
    Serial.print(",");
    if ((dir1 && current1) || !current1)
      Serial.print(value1*scale1);
    else
      Serial.print(value1*scale1-1.0);
    Serial.print(",");
    Serial.print(value2*scale2);
    Serial.print(",");
    Serial.print(value3*scale3);
    Serial.print(",");
    Serial.print(value6*scale6);
    Serial.print(",");
    Serial.print(value7*scale7);
    Serial.println();
    prevTime = currentTime;
  }
  Serial.println("Datalogging Complete.");
  delay(10000000);
}

void waitForAnswer() {
  while (Serial.available()>0)
    Serial.read();
  while (!Serial.available()) {;}
}
void askOnOff(int ch) {
  Serial.print("\nUse Channel A");
  Serial.print(ch);
  Serial.println("? (y/n)");
}
void askMode() {
  Serial.println("Measure Voltage or Current? (v/c)");
}
void askDir() {
  Serial.println("Unidirectional(-1~1A) or Bidirectional(0~2A) Current Sensing? (u/b)");
}
void askRange() {
  Serial.println("0~5V or 0~15V Measurement Range? (5/15)");
}
void reInput() {
  Serial.println("Please enter an appropriate answer.");
}
void debugConfig() {
  Serial.println("\nDEBUG CONFIG VALUES\n");

  Serial.print("analog0: ");
  Serial.println(analog0);
  Serial.print("\tcurrent0: ");
  Serial.println(current0);
  Serial.print("\tdir0: ");
  Serial.println(dir0);
  Serial.print("\tcurrent0: ");
  Serial.println(current0);
  Serial.print("\tscale0: ");
  Serial.println(scale0,6);
  
  Serial.print("analog1: ");
  Serial.println(analog1);
  Serial.print("\tcurrent1: ");
  Serial.println(current1);
  Serial.print("\tdir1: ");
  Serial.println(dir1);
  Serial.print("\tcurrent1: ");
  Serial.println(current1);
  Serial.print("\tscale1: ");
  Serial.println(scale1,6);
  
  Serial.print("analog2: ");
  Serial.println(analog2);
  Serial.print("\tscale2: ");
  Serial.println(scale2,6);
  
  Serial.print("analog3: ");
  Serial.println(analog3);
  Serial.print("\tscale3: ");
  Serial.println(scale3,6);
  
  Serial.print("analog6: ");
  Serial.println(analog6);
  Serial.print("\tscale6: ");
  Serial.println(scale6,6);
  
  Serial.print("analog7: ");
  Serial.println(analog7);
  Serial.print("\tscale7: ");
  Serial.println(scale7,6);

  Serial.println();
  Serial.print("period: ");
  Serial.println(period);

  Serial.print("duration: ");
  Serial.println(duration);
}

void setupChannels() {
  askOnOff(0);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog0 = false;
  else {
    analog0 = true;
    askMode();
    waitForAnswer();
    answerchar = Serial.read();
    while (!(answerchar == 'V' || answerchar == 'v' || answerchar == 'C' || answerchar == 'c')) {
      reInput();
      waitForAnswer();
      answerchar = Serial.read();
    }
    Serial.println(answerchar);
    if (answerchar == 'c' || answerchar == 'C') {
      current0 = true;
      scale0 = 2.0/682;
      askDir();
      waitForAnswer();
      answerchar = Serial.read();
      while (!(answerchar == 'U' || answerchar == 'u' || answerchar == 'B' || answerchar == 'b')) {
        reInput();
        waitForAnswer();
        answerchar = Serial.read();
      }
      Serial.println(answerchar);
      if (answerchar == 'u' || answerchar == 'U')
        dir0 = true;
      else
        dir0 = false;
    }
    else {
      current0 = false;
      askRange();
      waitForAnswer();
      answerint = Serial.parseInt();
      while (!(answerint == 5 || answerint == 15)) {
        reInput();
        waitForAnswer();
        answerint = Serial.parseInt();
      }
      Serial.println(answerint);
      if (answerint == 5)
        range0 = false;
      else
        range0 = true;
    }
  }

  askOnOff(1);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog1 = false;
  else {
    analog1 = true;
    askMode();
    waitForAnswer();
    answerchar = Serial.read();
    while (!(answerchar == 'V' || answerchar == 'v' || answerchar == 'C' || answerchar == 'c')) {
      reInput();
      waitForAnswer();
      answerchar = Serial.read();
    }
    Serial.println(answerchar);
    if (answerchar == 'c' || answerchar == 'C') {
      current1 = true;
      scale1 = 2.0/682;
      askDir();
      waitForAnswer();
      answerchar = Serial.read();
      while (!(answerchar == 'U' || answerchar == 'u' || answerchar == 'B' || answerchar == 'b')) {
        reInput();
        waitForAnswer();
        answerchar = Serial.read();
      }
      Serial.println(answerchar);
      if (answerchar == 'u' || answerchar == 'U')
        dir1 = true;
      else
        dir1 = false;
    }
    else {
      current1 = false;
      askRange();
      waitForAnswer();
      answerint = Serial.parseInt();
      while (!(answerint == 5 || answerint == 15)) {
        reInput();
        waitForAnswer();
        answerint = Serial.parseInt();
      }
      Serial.println(answerint);
      if (answerint == 5)
        range1 = false;
      else
        range1 = true;
    }
  }

  askOnOff(2);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog2 = false;
  else {
    analog2 = true;
    askRange();
    waitForAnswer();
    answerint = Serial.parseInt();
    while (!(answerint == 5 || answerint == 15)) {
      reInput();
      waitForAnswer();
      answerint = Serial.parseInt();
    }
    Serial.println(answerint);
    if (answerint == 5)
      range2 = false;
    else
      range2 = true;
  }

  askOnOff(3);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog3 = false;
  else {
    analog3 = true;
    askRange();
    waitForAnswer();
    answerint = Serial.parseInt();
    while (!(answerint == 5 || answerint == 15)) {
      reInput();
      waitForAnswer();
      answerint = Serial.parseInt();
    }
    Serial.println(answerint);
    if (answerint == 5)
      range3 = false;
    else
      range3 = true;
  }

  askOnOff(6);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog6 = false;
  else {
    analog6 = true;
    askRange();
    waitForAnswer();
    answerint = Serial.parseInt();
    while (!(answerint == 5 || answerint == 15)) {
      reInput();
      waitForAnswer();
      answerint = Serial.parseInt();
    }
    Serial.println(answerint);
    if (answerint == 5)
      range6 = false;
    else
      range6 = true;
  }

  askOnOff(7);
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'n' || answerchar == 'N')
    analog7 = false;
  else {
    analog7 = true;
    askRange();
    waitForAnswer();
    answerint = Serial.parseInt();
    while (!(answerint == 5 || answerint == 15)) {
      reInput();
      waitForAnswer();
      answerint = Serial.parseInt();
    }
    Serial.println(answerint);
    if (answerint == 5)
      range7 = false;
    else
      range7 = true;
  }
}

void setupSampling() {
  Serial.println("\nConfigure Sampling Period for All Channels\n");
  Serial.println("Choose from the following rates listed below:");
  Serial.println("1 =>  10 ms");
  Serial.println("2 => 100 ms");
  Serial.println("3 =>   1 sec");
  Serial.println("4 =>  30 sec");
  Serial.println("5 =>   3 min");
  Serial.println("6 =>  15 min");
  Serial.println("7 =>   1 hr");
  waitForAnswer();
  answerint = Serial.parseInt();
  while (!(answerint == 1 || answerint == 2 || answerint == 3 
          || answerint == 4 || answerint == 5 || answerint == 6)) {
    reInput();
    waitForAnswer();
    answerint = Serial.parseInt();
  }
  Serial.println(answerint);
  switch (answerint) {
    case 1:
      period = 0.01;
      break;
    case 2:
      period = 0.1;
      break;
    case 3:
      period = 1;
      break;
    case 4:
      period = 30;
      break;
    case 5:
      period = 3*60;
      break;
    case 6:
      period = 15*60;
      break;
    case 7:
      period = 60*60;
      break;
  }

  Serial.println("\nConfigure Datalogging Duration for All Channels\n");
  Serial.println("Choose from the following durations listed below:");
  Serial.println("1 => 10 sec");
  Serial.println("2 => 30 sec");
  Serial.println("3 =>  5 min");
  Serial.println("4 => 15 min");
  Serial.println("5 => 30 min");
  Serial.println("6 =>  1 hr");
  Serial.println("7 => 12 hrs");
  Serial.println("8 => 24 hrs");
  waitForAnswer();
  answerint = Serial.parseInt();
  while (!(answerint == 1 || answerint == 2 || answerint == 3 
          || answerint == 4 || answerint == 5 || answerint == 6
            || answerint == 7 || answerint == 8 || answerint == 9)) {
    reInput();
    waitForAnswer();
    answerint = Serial.parseInt();   
  }
  Serial.println(answerint);
  switch (answerint) {
    case 1:
      duration = 10;
      break;
    case 2:
      duration = 30;
      break;
    case 3:
      duration = 300;
      break;
    case 4:
      duration = 900;
      break;
    case 5:
      duration = 1800;
      break;
    case 6:
      duration = 3600;
      break;
    case 7:
      duration = 43200;
      break;
    case 8:
      duration = 86400;
      break;
  }
}

// 5V scale factor: 5/1023 = 0.004888
// 15V scale factor: 15/1023 = 0.01466
// CSENS scale factor: 1/1023 = 0.0009775
void setupScaling() {
  if (analog0) {
    if (current0)
      scale0 = 3.0/1023;
    else {
      if (range0)
        scale0 = 15.0/1023;
      else
        scale0 = 5.0/1023;
    }
  }
  if (analog1) {
    if (current1)
      scale1 = 3.0/1023;
    else {
      if (range1)
        scale1 = 15.0/1023;
      else
        scale1 = 5.0/1023;
    }
  }
  if (analog2) {
    if (range2)
      scale2 = 15.0/1023;
    else
      scale2 = 5.0/1023;
  }
  if (analog3) {
    if (range3)
      scale3 = 15.0/1023;
    else
      scale3 = 5.0/1023;
  }
  if (analog6) {
    if (range6)
      scale6 = 15.0/1023;
    else
      scale6 = 5.0/1023;
  }
  if (analog7) {
    if (range7)
      scale7 = 15.0/1023;
    else
      scale7 = 5.0/1023;
  }
}

bool saveConfigEEPROM() {
  Serial.println("\nWould you like to save this channel configuration? (y/n)");
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'Y' or answerchar == 'y') {
    byte addr0 = 0;
    byte addr1 = 0;
    
    if (analog0)
      addr0 = addr0 + B00000001;
    if (current0)
      addr0 = addr0 + B00000010;
    if (dir0)
      addr0 = addr0 + B00000100;
    if (range0)
      addr0 = addr0 + B00001000;
    if (analog1)
      addr0 = addr0 + B00010000;
    if (current1)
      addr0 = addr0 + B00100000;
    if (dir1)
      addr0 = addr0 + B01000000;
    if (range1)
      addr0 = addr0 + B10000000;
    if (analog2)
      addr1 = addr1 + B00000001;
    if (range2)
      addr1 = addr1 + B00000010;
    if (analog3)
      addr1 = addr1 + B00000100;
    if (range3)
      addr1 = addr1 + B00001000;
    if (analog6)
      addr1 = addr1 + B00010000;
    if (range6)
      addr1 = addr1 + B00100000;
    if (analog7)
      addr1 = addr1 + B01000000;
    if (range7)
      addr1 = addr1 + B10000000;
    
    EEPROM.update(0,addr0);
    EEPROM.update(1,addr1);
    Serial.println("\nSaving to EEPROM...");
    delay(250);

    return true;
  }
  return false;
}

bool loadConfigEEPROM() {
  Serial.println("\nWould you like to load the previously saved channel configuration? (y/n)");
  waitForAnswer();
  answerchar = Serial.read();
  while (!(answerchar == 'Y' || answerchar == 'y' || answerchar == 'N' || answerchar == 'n')) {
    reInput();
    waitForAnswer();
    answerchar = Serial.read();
  }
  Serial.println(answerchar);
  if (answerchar == 'Y' or answerchar == 'y') {
    byte addr0;
    byte addr1;
    
    addr0 = EEPROM.read(0);
    addr1 = EEPROM.read(1);
    
    analog0 = bitRead(addr0,0);
    current0 = bitRead(addr0,1);
    dir0 = bitRead(addr0,2);
    range0 = bitRead(addr0,3);
    
    analog1 = bitRead(addr0,4);
    current1 = bitRead(addr0,5);
    dir1 = bitRead(addr0,6);
    range1 = bitRead(addr0,7);

    analog2 = bitRead(addr1,0);
    range2 = bitRead(addr1,1);

    analog3 = bitRead(addr1,2);
    range3 = bitRead(addr1,3);

    analog6 = bitRead(addr1,4);
    range6 = bitRead(addr1,5);

    analog7 = bitRead(addr1,6);
    range7 = bitRead(addr1,7);

    return true;
  }
  return false;
}
