/////////////////////////////////////////
// HXbot HXpower firmware               /
// EoF 2016 EoF@itphx.ru                /
/////////////////////////////////////////

#include <Wire.h>

// DEBUG
#define DEBUG 0

// DEFINE
#define SLAVE_ADDRESS 0x05
#define XOR_SEQ 0xFF
#define EXT_COM 0xAA

#define AIN_PIN A9
#define ADC_PIN A8
#define AHX_PIN A7
#define ABA_PIN A6

#define VIN_PIN A5
#define VDC_PIN A4
#define VBT_PIN A3
#define VBA_PIN A2
#define VZU_PIN A1
#define VIS_PIN A0

#define R_BL 11
#define R_PF 5
#define R_HX 1
#define R_IS 7
#define RED_PIN 10
#define GRN_PIN 12
#define BTN_PIN 0
#define LED_PIN 13

#define REP_COUNT 5

#define TIMEOUT 5000
#define BUTTON_REACTION_TIME 5000

// COMMANDS
// VOLTMETERS
#define COM_GET_VIN 0x01
#define COM_GET_VDC 0x02
#define COM_GET_VBT 0x03
#define COM_GET_VBA 0x04
#define COM_GET_VIS 0x05
#define COM_GET_VZU 0x06
#define COM_GET_VCC 0x07

// AMPERMETERS
#define COM_GET_AHX 0x21
#define COM_GET_ABA 0x22
#define COM_GET_AIN 0x23
#define COM_GET_ADC 0x24

// STATUS
#define COM_GET_STAT1 0x08
#define COM_GET_STAT2 0x09
#define COM_GET_STAT3 0x20

// TEMP
#define COM_GET_TEMP 0x10

// RELAYS
#define COM_POWER_ON_BL  0x11  // 17
#define COM_POWER_ON_PF  0x12  // 18
#define COM_SWITCH_TO_IS 0x13  // 19
#define COM_SWITCH_TO_IN 0x14  // 20

#define COM_SHUTDOWN_BL  0x15  // 21
#define COM_SHUTDOWN_PF  0x16  // 22
#define COM_SWITCH_TO_LR 0x17  // 23
#define COM_SWITCH_TO_BA 0x18  // 24

#define COM_ENABLE_PF  0x1B  // 27
#define COM_DISABLE_PF 0x1C  // 28

#define COM_ENABLE_BL  0x1D  // 29
#define COM_DISABLE_BL 0x1E  // 30

#define OK_RSP  0x00
#define NO_RSP  0xFF
#define ERR_RSP 0x01
#define BLK_RSP 0x02
#define CSE_RSP 0x03
#define IOE_RSP 0x04
#define TMO_RSP 0x05

// CONST
//#define DELAY_TIME 0
#define BLINK_DELAY 300

#define BA_TIMEOUT 5000
#define BA_BLOCK_TIMEOUT 30000
#define BA_FULL_CHECK_CYCLES 20
#define V_CHECK_CYCLES 10
#define MAX_VBT_FAIL 15
#define AREF 5

#define VIN_MIN 10.0
#define VIN_MAX 14.0

#define VDC_MIN 4.8
#define VDC_MAX 5.5

#define VBT_MIN 3.65
#define VBT_MAX 4.25

#define VBA_MIN 6.0
#define VBA_MAX 8.6

#define VIS_MIN 4.8
#define VIS_MAX 5.5

#define VZU_MIN 5.0
#define VZU_MAX 6.3

#define VCC_MIN 4.3
#define VCC_MAX 5.3

#define AHX_MIN 0.1
#define AHX_MAX 5.0

#define ABA_MIN 0.1
#define ABA_MAX 5.0

#define AIN_MIN 0.1
#define AIN_MAX 5.0

#define ADC_MIN 0.1
#define ADC_MAX 5.0

// VAR
// резисторы делителя напряжения
const float r1_1 = 20000;  // 20K
const float r1_2 = 10000;  // 10K
const float r2_1 = 10380;  // 10K
const float r2_2 = 10000;  // 10K
const float r3_1 = 10000;  // 10K
const float r3_2 = 10000;  // 10K
const float r4_1 = 20330;  // 20K
const float r4_2 = 10000;  // 10K
const float r5_1 = 1060;   // 1K
const float r5_2 = 1000;   // 1K
const float r6_1 = 1000;   // 1K
const float r6_2 = 1000;   // 1K

// эту константу (typVbg) необходимо откалибровать индивидуально
const float typVbg = 1.08; // 1.0 -- 1.2

float vcc = 0.0, tcc;
float kin, kdc, kbt, kba, kis, kzu;
float vin, vdc, vbt, vba, vis, vzu;
float tin, tdc, tbt, tba, tis, tzu;
float ahx, aba, ain, adc;
float uhx, uba, uin, udc;

byte cmd = 0x00;
byte ext = 0x00;
byte sum = 0x00;

unsigned long lastCommandTime = 0;
byte autoResponse = 0x00;

byte byteResponse = 0x00;
boolean needByte = false;
boolean needSumm = false;

float floatResponse = 0.0;
boolean needFloat = false;
byte floatByte = 0;

byte ba_count = 0;
unsigned long ba_time;
boolean ba_blocked = false;
byte ba_full_count = 0;
boolean ba_charge_blocked = false;

boolean is_requested = false;

unsigned long button_time;
//byte button_count = 0;

boolean vin_ok = false;
byte vin_count = 0;

boolean vdc_ok = false;
byte vdc_count = 0;

boolean vbt_ok = false, vbt_fail = false;
byte vbt_count = 0, vbt_fail_count = 0;

boolean vba_ok = false;
byte vba_count = 0;

boolean vis_ok = false;
byte vis_count = 0;

boolean vzu_ok = false;
byte vzu_count = 0;

boolean vcc_ok = false;
byte vcc_count = 0;

boolean ahx_ok = false;
byte ahx_count = 0;

boolean aba_ok = false;
byte aba_count = 0;

boolean ain_ok = false;
byte ain_count = 0;

boolean adc_ok = false;
byte adc_count = 0;

boolean ba_enabled = true;
boolean lr_enabled = true;
boolean pf_enabled = false;
boolean bl_enabled = false;

boolean bl_powered = false;
boolean bl_error = false;
boolean ba_full = false;
boolean ba_charge = false;

byte status_1 = 0x00;
byte status_2 = 0x00;
byte status_3 = 0x00;

// Blink detection
  // 0 - LOW
  // 1 - HIGH
  // 2 - BLINK
byte red_state = 0, grn_state = 0;
byte red_array[3] = {0, 0, 0}, grn_array[3] = {0, 0, 0};
byte blink_i;
unsigned long blink_time = 0;

// Loop delay
//int delay_time = DELAY_TIME;

/****************************************************************************
 * Главная программа
 ****************************************************************************/

void setup() {
  if (DEBUG) {
    Serial.begin(9600);
    Serial.println("---");
    delay(1000);
  }
  
  // определение опорного напряжения
  analogReference(DEFAULT);  // DEFAULT INTERNAL использовать vcc как AREF
  
  kin = r1_2 / (r1_1 + r1_2);
  kdc = r2_2 / (r2_1 + r2_2);
  kbt = r3_2 / (r3_1 + r3_2);
  kba = r4_2 / (r4_1 + r4_2);
  kis = r5_2 / (r5_1 + r5_2);
  kzu = r6_2 / (r6_1 + r6_2);
  
  if (DEBUG) {
    Serial.print("Vcc= ");
    Serial.println(vcc);
    Serial.println("---");
  }
  
  // Initialize i2c as slave
  Wire.begin(SLAVE_ADDRESS);
 
  // Define callbacks for i2c communication
  Wire.onReceive(receiveData);
  Wire.onRequest(answer);
  
  pinMode(R_BL, OUTPUT);
  pinMode(R_PF, OUTPUT);
  pinMode(R_HX, OUTPUT);
  pinMode(R_IS, OUTPUT);
  pinMode(R_IS, OUTPUT);
  
  pinMode(VIN_PIN, INPUT);
  pinMode(VDC_PIN, INPUT);
  pinMode(VBT_PIN, INPUT);
  pinMode(VBA_PIN, INPUT);
  pinMode(VIS_PIN, INPUT);
  pinMode(VZU_PIN, INPUT);
  
  pinMode(AHX_PIN, INPUT);
  pinMode(ABA_PIN, INPUT);
  pinMode(AIN_PIN, INPUT);
  pinMode(ADC_PIN, INPUT);

  pinMode(BTN_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
  pinMode(RED_PIN, INPUT);
  pinMode(GRN_PIN, INPUT);
    
  ba_time = millis();
}

void loop() {
  // Timeout protection
  if ((lastCommandTime + TIMEOUT < millis())) {
    floatResponse = 0.0;
    floatByte = 0;
    needFloat = false;
    autoResponse = TMO_RSP;
  }

  // BLOCK OF MAGIC 
  readvcc();
  analogRead(VIN_PIN);
  readvcc();
  analogRead(VIN_PIN);
  readvcc();
  analogRead(VIN_PIN);
  // MAGIC

  // Get voltages
  tin = 0.0;
  tdc = 0.0;
  tbt = 0.0;
  tba = 0.0;
  tis = 0.0;
  tzu = 0.0;
  
  //for (byte i = 0; i < REP_COUNT; i++) {
    tin += readvcc() * analogRead(VIN_PIN);
    tdc += readvcc() * analogRead(VDC_PIN);
    tbt += readvcc() * analogRead(VBT_PIN);
    tba += readvcc() * analogRead(VBA_PIN);
    tis += readvcc() * analogRead(VIS_PIN);
    tzu += readvcc() * analogRead(VZU_PIN);
    //delay(10);
  //}

  tcc = readvcc();
  tin = tin / 1024.0 / kin;// / REP_COUNT;
  tdc = tdc / 1024.0 / kdc;// / REP_COUNT;
  tbt = tbt / 1024.0 / kbt;// / REP_COUNT;
  tba = tba / 1024.0 / kba;// / REP_COUNT;
  tis = tis / 1024.0 / kis;// / REP_COUNT;
  tzu = tzu / 1024.0 / kzu;// / REP_COUNT;

  if (tcc > VCC_MIN) vcc = tcc;
  vin = tin;
  vdc = tdc;
  vbt = tbt;
  if (ba_enabled) vba = tba;
  vis = tis;
  vzu = tzu;  
  
  // Get currents
  ahx = (analogRead(AHX_PIN) - 512) * 0.0264;
  if (ahx < 0) ahx *= -1;
  aba = (analogRead(ABA_PIN) - 512) * 0.0264;
  if (aba < 0) aba *= -1;
  ain = (analogRead(AIN_PIN) - 512) * 0.0264;
  if (ain < 0) ain *= -1;
  adc = (analogRead(ADC_PIN) - 512) * 0.0264;
  if (adc < 0) adc *= -1;

  if (DEBUG) {
    Serial.print("Vcc: ");
    Serial.println(vcc);
    Serial.print("Vin: ");
    Serial.println(vin);
    Serial.print("Vdc: ");
    Serial.println(vdc);
    Serial.print("Vbt: ");
    Serial.println(vbt);
    Serial.print("Vba: ");
    Serial.println(vba);
    Serial.print("Vis: ");
    Serial.println(vis);
    Serial.print("Vzu: ");
    Serial.println(vzu);
    Serial.println("---");
  }
  
  // Vin check
  if (vin >= VIN_MIN && vin <= VIN_MAX) {
    if (! vin_ok) vin_count++;
  }
  else {
    vin_count = 0;
    vin_ok = false;
  }
  
  if (vin_count >= V_CHECK_CYCLES) vin_ok = true;
  
  // Vdc check
  if (vdc >= VDC_MIN && vdc <= VDC_MAX) {
    if (! vdc_ok) vdc_count++;
  }
  else {
    vdc_count = 0;
    vdc_ok = false;
  }
  
  if (vdc_count >= V_CHECK_CYCLES) vdc_ok = true;
  
  // Vbt check
  if (vbt >= VBT_MIN && vbt <= VBT_MAX) {
    if (! vbt_ok) vbt_count++;
  }
  else {
    vbt_count = 0;
    vbt_ok = false;
  }
  
  if (vbt_count >= V_CHECK_CYCLES) vbt_ok = true;
  
  // Vba check
  if (ba_enabled) {
    if (vba >= VBA_MIN && vba <= VBA_MAX) {
      if (! vba_ok) vba_count++;
    }
    else {
      vba_count = 0;
      vba_ok = false;
    }
    
    if (vba_count >= V_CHECK_CYCLES) vba_ok = true;
  }
  else {
    vba_ok = false;
  }

  // Vis check
  if (vis >= VIS_MIN && vis <= VIS_MAX) {
    if (! vis_ok) vis_count++;
  }
  else {
    vis_count = 0;
    vis_ok = false;
  }
  
  if (vis_count >= V_CHECK_CYCLES) vis_ok = true;

  // Vzu check
  if (vzu >= VZU_MIN && vzu <= VZU_MAX) {
    if (! vzu_ok) vzu_count++;
  }
  else {
    vzu_count = 0;
    vzu_ok = false;
  }
  
  if (vzu_count >= V_CHECK_CYCLES) vzu_ok = true;
  
  // Vcc check
  if (vcc >= VCC_MIN && vcc <= VCC_MAX) {
    if (! vcc_ok) vcc_count++;
  }
  else {
    vcc_count = 0;
    vcc_ok = false;
  }
  
  if (vcc_count >= V_CHECK_CYCLES) vcc_ok = true;

  // Ahx check
  if (ahx >= AHX_MIN && ahx <= AHX_MAX) {
    if (! ahx_ok) ahx_count++;
  }
  else {
    ahx_count = 0;
    ahx_ok = false;
  }
  
  if (ahx_count >= V_CHECK_CYCLES) ahx_ok = true;

  // Aba check
  if (aba >= ABA_MIN && aba <= ABA_MAX) {
    if (! aba_ok) aba_count++;
  }
  else {
    aba_count = 0;
    aba_ok = false;
  }
  
  if (aba_count >= V_CHECK_CYCLES) aba_ok = true;

  // Ain check
  if (ain >= AIN_MIN && ain <= AIN_MAX) {
    if (! ain_ok) ain_count++;
  }
  else {
    ain_count = 0;
    ain_ok = false;
  }
  
  if (ain_count >= V_CHECK_CYCLES) ain_ok = true;

  // Adc check
  if (adc >= ADC_MIN && adc <= ADC_MAX) {
    if (! adc_ok) adc_count++;
  }
  else {
    adc_count = 0;
    adc_ok = false;
  }
  
  if (adc_count >= V_CHECK_CYCLES) adc_ok = true;
  
  // Auto disable IN
  if (! vin_ok || ! in_connected()) {
    switchToBA();
  }

  // Auto disable IS
  if (! vis_ok || ((! vin_ok || ! in_connected()) && ! is_requested)) {
    switchToLR();
  }
    
  // BL status  
  bl_powered = false;
  bl_error = false;
  if (! ba_charge_blocked) ba_full = false;
  ba_charge = false;

  if (bl_enabled) {
    detectBlink();
    
    switch (grn_state) {
      case 0:
        ba_full = false;
        ba_charge = false;
        break;
      case 1:
        ba_full = true;
        ba_charge = false;
        break;
      case 2:
        ba_full = false;
        ba_charge = true;
        break;
    }
    
    switch (red_state) {
      case 0:
        bl_powered = false;
        bl_error = false;
        break;
      case 1:
        bl_powered = true;
        bl_error = false;
        break;
      case 2:
        bl_powered = false;
        bl_error = true;
        break;    
    }

    if (ba_full) {
      if (! ba_charge_blocked) ba_full_count++;
    }
    else {
      ba_full_count = 0;
    }

    if (ba_full_count >= BA_FULL_CHECK_CYCLES) ba_charge_blocked = true;
  }

  // Button reaction
//  if (digitalRead(BTN_PIN) == LOW) {
//    button_time = millis();
//  }
//  else {
//    if (button_time + BUTTON_REACTION_TIME < millis()) {
//      switchToBA();        
//    }
//  }
  
  // Disable BA block
  if (ba_blocked && ba_time + BA_BLOCK_TIMEOUT < millis()) {
    ba_blocked = false;
  }
  
  // Auto switch to IN
  if (vin_ok && in_connected() && ba_enabled && ! ba_blocked && ba_time + BA_TIMEOUT < millis()) {
    switchToIN();
  }

  // Auto enable IS
  if (vis_ok && ! ba_enabled && vin_ok) {
    switchToIS();
  }
  
  // Auto enable BL
  if (! pf_enabled && ! ba_enabled && vin_ok && in_connected() && ! ba_charge_blocked) {
  //if (! pf_enabled && ! ba_enabled && vin_ok && ! ba_charge_blocked) {
    enableBL();
  }

  // Auto disable BL
  if ((bl_enabled && ba_charge_blocked) || ! in_connected()) {
    disableBL();
  }

  // Auto enable IS on BT fail
//  if (! vbt_ok) {
//    if (! vbt_fail) vbt_fail_count++;
//  }  
//  else {
//    vbt_fail_count = 0;
//    vbt_fail = false;
//  }

//  if (vbt_fail_count >= MAX_VBT_FAIL && vis_ok) switchToIS();

  // Compile status
  status_1 = 0x00;
  if (vcc_ok)     status_1 |= 0x01;
  if (vin_ok)     status_1 |= 0x02;
  if (vdc_ok)     status_1 |= 0x04;
  if (vbt_ok)     status_1 |= 0x08;
  if (vba_ok)     status_1 |= 0x10;
  if (lr_enabled) status_1 |= 0x20;
  if (ba_enabled) status_1 |= 0x40;
  if (ba_blocked) status_1 |= 0x80;
  
  status_2 = 0x00;
  if (bl_powered) status_2 |= 0x01;
  if (bl_error)   status_2 |= 0x02;
  if (ba_full)    status_2 |= 0x04;
  if (ba_charge)  status_2 |= 0x08;
  if (vis_ok)     status_2 |= 0x10;
  if (vzu_ok)     status_2 |= 0x20;
  if (pf_enabled) status_2 |= 0x40;
  if (bl_enabled) status_2 |= 0x80;

  status_3 = 0x00;
  if (ahx_ok)     status_3 |= 0x01;
  if (aba_ok)     status_3 |= 0x02;
  if (ain_ok)     status_3 |= 0x04;
  if (adc_ok)     status_3 |= 0x08;
}

/****************************************************************************
 * Функции
 ****************************************************************************/

float readvcc() {
  byte i;
  float result = 0.0;
  float tmp = 0.0;

  //for (i = 0; i < REP_COUNT; i++) {
    // Read 1.1V reference against Avcc
    // set the reference to vcc and the measurement to the internal 1.1V reference
    #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
        ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
        ADMUX = _BV(MUX5) | _BV(MUX0);
    #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
        ADMUX = _BV(MUX3) | _BV(MUX2);
    #else
        // works on an Arduino 168 or 328
        ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #endif

    delay(3); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Start conversion
    while (bit_is_set(ADCSRA,ADSC)); // measuring

    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
    uint8_t high = ADCH; // unlocks both

    tmp = (high<<8) | low;
    tmp = (typVbg * 1023.0) / tmp;
    //result = result + tmp;
  //  delay(5);
  //}

  //result = result / REP_COUNT;
  return tmp;//result;
}

// Callback for received data
void receiveData(int byteCount) {  
  
  while(Wire.available()) {
    // Get command
    ext = Wire.read();
    if (ext == EXT_COM && byteCount == 3) {
      cmd = 0x00;
      sum = 0x00;
      
      if (Wire.available()) sum = Wire.read();
      if (Wire.available()) cmd = Wire.read();
      
      if ((cmd ^ XOR_SEQ) != sum) {
        autoResponse = CSE_RSP;
        return;
      }
    }
    else {
      autoResponse = ERR_RSP;
      while(Wire.available()) {
        ext = Wire.read();
      }
      return;
    }
    
    // Process command
    switch (cmd) {
      case COM_GET_VIN:
        sendFloat(vin);
        break;
      case COM_GET_VDC:
        sendFloat(vdc);
        break;
      case COM_GET_VBT:
        sendFloat(vbt);
        break;
      case COM_GET_VBA:
        sendFloat(vba);
        break;
      case COM_GET_VIS:
        sendFloat(vis);
        break;
      case COM_GET_VZU:
        sendFloat(vzu);
        break;
      case COM_GET_VCC:
        sendFloat(vcc);
        break;

      case COM_GET_AHX:
        sendFloat(ahx);
        break;
      case COM_GET_ABA:
        sendFloat(aba);
        break;
      case COM_GET_AIN:
        sendFloat(ain);
        break;
      case COM_GET_ADC:
        sendFloat(adc);
        break;
          
      case COM_GET_TEMP:
        sendFloat(getInternalTemp());
        break;
      case COM_GET_STAT1:
        sendByte(status_1);
        break;
      case COM_GET_STAT2:
        sendByte(status_2);
        break;     
      case COM_GET_STAT3:
        sendByte(status_3);
        break; 
        
      case COM_POWER_ON_BL:
        digitalWrite(R_BL, HIGH);
        bl_enabled = true;
        commandResponse();
        break;
      case COM_SHUTDOWN_BL:
        digitalWrite(R_BL, LOW);
        bl_enabled = false;
        commandResponse();
        break;
        
      case COM_POWER_ON_PF:
        digitalWrite(R_PF, HIGH);
        pf_enabled = true;
        commandResponse();
        break;
      case COM_SHUTDOWN_PF:
        digitalWrite(R_PF, LOW);
        pf_enabled = false;
        commandResponse();
        break;
      
      case COM_SWITCH_TO_IS:
        is_requested = true;
        commandResponse(switchToIS());
        break;
      case COM_SWITCH_TO_LR:
        is_requested = false;
        commandResponse(switchToLR());
        break;
      
      case COM_SWITCH_TO_IN:
        commandResponse(switchToIN());       
        break;
      case COM_SWITCH_TO_BA:
        commandResponse(switchToBA());
        break;
        
      case COM_ENABLE_PF:
        enablePF();
        commandResponse();
        break;
        
      case COM_DISABLE_PF:
        disablePF();
        commandResponse();
        break;
        
      case COM_ENABLE_BL:
        enableBL();
        commandResponse();
        break;
        
      case COM_DISABLE_BL:
        disableBL();
        commandResponse();
        break;
        
      default:
        autoResponse = ERR_RSP;
        break;
    }
  }
}

void commandResponse() {
  lastCommandTime = millis();
  autoResponse = OK_RSP;
}

void commandResponse(byte response) {
  lastCommandTime = millis();
  autoResponse = response;  
}

void sendByte(byte value) {
  lastCommandTime = millis();
  byteResponse = value;
  needSumm = false;
  needByte = true;
}

void sendFloat(float value) {
  lastCommandTime = millis();
  floatResponse = value;
  floatByte = 0;
  needFloat = true;
}

byte cSum(byte value) {
  return value ^ XOR_SEQ; 
}

byte cSum(byte *data, byte dataSize) {
  byte tmp = 0x00;
  for (byte i = 0; i < dataSize; i++) {
    tmp = tmp ^ data[i];
  }
  return tmp ^ XOR_SEQ; 
}

void answer() {
  // Want float value?
  if (needFloat) {
    // Get access to the float as a byte-array:
    byte *data = (byte *) &floatResponse;
    
    if (floatByte < sizeof(floatResponse)) {
      // Send byte
      Wire.write(data[floatByte]);
      floatByte++;
    }
    else {
      // Send control sum
      Wire.write(cSum(data, sizeof(floatResponse)));
      needFloat = false;
    }
  }
  else {
    // Want byte value?
    if (needByte) {
      if (!needSumm) {
        // Send byte
        Wire.write(byteResponse);
        needSumm = true;
      }
      else {
        // Send control sum
        Wire.write(cSum(byteResponse));
        needSumm = false;
        needByte = false;
      }
    }
    else {
      // Want something else?
      Wire.write(autoResponse);
    }
  }
  // Nothing more to send
  autoResponse = NO_RSP;
}

byte switchToLR() {
  if (vbt_ok) {
    digitalWrite(R_HX, LOW);
    lr_enabled = true;
    
    return OK_RSP;
  }
  else return BLK_RSP;
}

byte switchToIS() {
  digitalWrite(R_HX, HIGH);
  lr_enabled = false;

  return OK_RSP;
}

byte switchToBA() {
  if (! ba_enabled) {

    // Disable BL
    if (bl_enabled) {
      digitalWrite(R_BL, LOW);
      bl_enabled = false;
      //delay(500);
    }
    
    digitalWrite(R_IS, LOW);
    ba_enabled = true;
    
    digitalWrite(LED_PIN, LOW);
    ba_time = millis();
    ba_blocked = true;
    ba_charge_blocked = false;
  }
  
  return OK_RSP;
}

byte switchToIN() {
  if (vin_ok && ba_enabled && in_connected()) {
    digitalWrite(R_IS, HIGH);
    ba_enabled = false;
    
    digitalWrite(LED_PIN, HIGH);
    
    return OK_RSP;
  }
  else return BLK_RSP;
}

void enablePF() {
  if (bl_enabled) {
    digitalWrite(R_BL, LOW);
    bl_enabled = false;
    //delay(500);
  }
  digitalWrite(R_PF, HIGH);
  pf_enabled = true;
  ba_charge_blocked = false;
}

void disablePF() {
  digitalWrite(R_PF, LOW);
  pf_enabled = false;
}

byte enableBL() {
  if (! ba_enabled && ! pf_enabled && in_connected()) {
    digitalWrite(R_BL, HIGH);
    bl_enabled = true;
    
    return OK_RSP;
  }
  else return BLK_RSP;
}

void disableBL() {
  digitalWrite(R_BL, LOW);
  bl_enabled = false;
}

// Get the internal temperature
float getInternalTemp() {
  unsigned int wADC;
  float t;
  ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
  ADCSRA |= _BV(ADEN); // enable the ADC
  delay(20); // wait for voltages to become stable.
  ADCSRA |= _BV(ADSC); // Start the ADC
  while (bit_is_set(ADCSRA,ADSC));
  wADC = ADCW;
  t = (wADC - 324.31 ) / 1.22;
  return(t);
}

void detectBlink() {
    // 0 - LOW
    // 1 - HIGH
    // 2 - BLINK
  byte red_low_count = 0, grn_low_count = 0;
  byte red_high_count = 0, grn_high_count = 0;
  byte pin_level;

  if (millis() - blink_time < BLINK_DELAY) return;
  
  red_array[blink_i] = digitalRead(RED_PIN);
  grn_array[blink_i] = digitalRead(GRN_PIN);
  
  blink_i++;

  if (blink_i == 3) {    
    for (byte i = 0; i < 3; i++) {
      if (red_array[i] == HIGH) red_high_count++;
      if (red_array[i] == LOW) red_low_count++;
      if (grn_array[i] == HIGH) grn_high_count++;
      if (grn_array[i] == LOW) grn_low_count++;
    }
    
    if (red_low_count > 0 && red_high_count == 0)
      red_state = 0;
    else
      if (red_high_count > 0 && red_low_count == 0)
        red_state = 1;
      else
        red_state = 2;

    if (grn_low_count > 0 && grn_high_count == 0)
      grn_state = 0;
    else
      if (grn_high_count > 0 && grn_low_count == 0)
        grn_state = 1;
      else
        grn_state = 2;

    blink_i = 0;
  }
  
  blink_time = millis();
}

bool in_connected() {
  if (digitalRead(BTN_PIN) == HIGH) {
    return true;
  } else {
    return false;
  }
}

