// Tank Level monitor based on the Arduino Mini Pro
// INPUT  = Resistor sensor (US or EU) OR 4 (or less) non-contact sensors
// OUTPUT = Chain of 5 LEDS or DC voltage for VDO system used in Beneteaus and Jeanneaus

#define DEBUG

#ifdef DEBUG
  #define DebugPrint(x)        Serial.print(x)
  #define DebugPrintln(x)      Serial.println(x)
  #define DebugBegin(x)        Serial.begin(x)
#else
  #define DebugPrint(x)
  #define DebugPrintln(x)
  #define DebugBegin(x)  
#endif 

// define the pins for LEDs or DAC (digital to analog converter)
#define LED1_PIN   9           // most significative bit or left most          
#define LED2_PIN   8
#define LED3_PIN   12
#define LED4_PIN   11
#define LED5_PIN   10          // least significative or right most

// and the status LED on the Arduino board
#define LED_PIN   13

// input pins for the non-contact level sensors
#define TOP_PIN   4           // TOP sensor
#define MIT_PIN   5           // MIT MIddle near the Top
#define MIB_PIN   6           // MIB MIddle near the Bottom
#define BOT_PIN   7           // BOTTOM sensor

// pins to read at the start up, which define the mode of operation
// when JUMP1_PIN == HIGH, the other jumper pins have the shown meaning 
// no definition exists yet for those pins when JUMP1_PIN == LOW
#define JUMP1_PIN   1         // HIGH = normal operation; LOW = reserved for expansion (pin 1 is the TX pin)
#define JUMP2_PIN   0         // HIGH = DAC output; LOW = LEDs output (pin 0 is the RX pin) 
#define JUMP3_PIN   2         // HIGH = EU (10-180 ohms); LOW = USA (240-33 ohms) 
#define JUMP4_PIN   3         // HIGH = non-contact sensors; LOW = resistive sensor

/* 

For European 10-180 ohm sensors we have the following relation between the
state of the tank and the resistence of the sensor:

  ( 0 / 4 ) --->>> resistance = 10 ohms (empty)  <<< from specification
  ( 1 / 4 ) --->>> resistance = 52 ohms          <<< interpolated
  ( 2 / 4 ) --->>> resistance = 95 ohms          <<< from specification
  ( 3 / 4 ) --->>> resistance = 137 ohms         <<< interpolated 
  ( 4 / 4 ) --->>> resistance = 180 ohms (full)  <<< from specification

So the values of resistance that mark the changes from states are:

  ( 0 / 4 ) to ( 1 / 4 ) --->>> resistance = 31 ohms
  ( 1 / 4 ) to ( 2 / 4 ) --->>> resistance = 73 ohms
  ( 2 / 4 ) to ( 3 / 4 ) --->>> resistance = 116 ohms
  ( 3 / 4 ) to ( 4 / 4 ) --->>> resistance = 158 ohms

For American 240-33 ohm sensors we have:

  ( 0 / 4 ) --->>> resistance = 240 ohms (empty)  <<< from specification
  ( 1 / 4 ) --->>> resistance = 171 ohms          <<< interpolated
  ( 2 / 4 ) --->>> resistance = 103 ohms          <<< from specification
  ( 3 / 4 ) --->>> resistance = 68 ohms           <<< interpolated
  ( 4 / 4 ) --->>> resistance = 33 ohms (full)    <<< from specification

and the values of resistance that mark the changes from states are:

  ( 0 / 4 ) to ( 1 / 4 ) --->>> resistance = 206 ohms
  ( 1 / 4 ) to ( 2 / 4 ) --->>> resistance = 137 ohms
  ( 2 / 4 ) to ( 3 / 4 ) --->>> resistance = 86 ohms
  ( 3 / 4 ) to ( 4 / 4 ) --->>> resistance = 50 ohms
  
Because the resistance of the sensor is measured by reading its voltage
when there is a 220 ohm resistor in series with the sensor and connected
to +5V, the following integer constants are determined experimentally:  
*/

const int adc_EU_01 = 122;     // ADC reading when sensor is 31 ohm
const int adc_EU_12 = 251;     // ADC reading when sensor is 73 ohm
const int adc_EU_23 = 351;     // ADC reading when sensor is 116 ohm
const int adc_EU_34 = 425;     // ADC reading when sensor is 158 ohm

const int adc_US_01 = 495;     // ADC reading when sensor is 206 ohm
const int adc_US_12 = 390;     // ADC reading when sensor is 137 ohm
const int adc_US_23 = 282;     // ADC reading when sensor is 86 ohm
const int adc_US_34 = 187;     // ADC reading when sensor is 50 ohm

int vdo;                      // holds the value that corresponds to the state of the tank
                              // this value is written to a DAC (5-bit digital to analog converter)
                              // and the following values were experimentally measured:

const int vdo_04 = 5;          // DAC value for 0,80V ( 0 / 4 or Empty )   was 8
const int vdo_14 = 9;          // DAC value for 1,38V ( 1 / 4 ) was 14
const int vdo_24 = 13;         // DAC value for 1,96V ( 2 / 4 ) was 19
const int vdo_34 = 17;         // DAC value for 2,54V ( 3 / 4 ) was 25
const int vdo_44 = 21;         // DAC value for 3,12V ( 4 / 4 or Full )  was 30

int last_vdo = 0;             // if vdo is equal to last_vdo then no need to update the output

byte inp_mode;                // if 0xFF gets data from 10-180 ohm sensor
                              // if 0x0F gets data from 230-40 ohm sensor  
                              // if 0x00 gets data from TOP MIT MIB and BOT sensors
byte out_mode;                // if 0xFF outputs data to DAC
                              // if 0x00 outputs data to LEDs    
                              
void setup() {

  DebugBegin(115200);

  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  pinMode(LED3_PIN, OUTPUT);  
  pinMode(LED4_PIN, OUTPUT);
  pinMode(LED5_PIN, OUTPUT);
  
  pinMode(LED_PIN, OUTPUT);        // to be used on calibration procedures

  pinMode(TOP_PIN, INPUT_PULLUP);
  pinMode(MIT_PIN, INPUT_PULLUP);  
  pinMode(MIB_PIN, INPUT_PULLUP);  
  pinMode(BOT_PIN, INPUT_PULLUP);
  
  pinMode(JUMP1_PIN, INPUT_PULLUP);    
  pinMode(JUMP2_PIN, INPUT_PULLUP);    
  pinMode(JUMP3_PIN, INPUT_PULLUP);    
  pinMode(JUMP4_PIN, INPUT_PULLUP);      

  // read jumpers and set modes
  inp_mode = 0x00;
  int reading = digitalRead(JUMP4_PIN);
  if (reading == LOW ) { 
    inp_mode = 0xFF;
    reading = digitalRead(JUMP3_PIN);
    if (reading == LOW ) { 
      inp_mode = 0x0F; 
    }
  }
  DebugPrint("Monitor started with input mode = ");
  DebugPrintln(inp_mode);
  out_mode = 0x00;
  reading = digitalRead(JUMP2_PIN);
  if (reading == HIGH ) { out_mode = 0xFF; }
  DebugPrint("Monitor started with output mode = ");
  DebugPrintln(out_mode);
  
  LedsOFF();
  
}
  
void loop() {
  int sensor;
  if ( inp_mode == 0xFF ) {
    sensor = analogRead(A0); 
    vdo = EU_to_vdo(sensor);
  }
  if ( inp_mode == 0x0F ) {
    sensor = analogRead(A0); 
    vdo = US_to_vdo(sensor);
  }
  if ( inp_mode == 0x00 ) {
    vdo = TMMB_to_vdo();
  }
  if ( vdo != last_vdo ) {
    if ( out_mode == 0xFF ) { UpdateDac(); }
    if ( out_mode == 0x00 ) { UpdateLEDs(); }
    last_vdo = vdo;
  }
}

void UpdateDac() {
  int n = bitRead( vdo, 0);        // least significative bit or right most
  if ( n == 0 ) { digitalWrite(LED5_PIN, LOW); }
  else { digitalWrite(LED5_PIN, HIGH); }
  n = bitRead( vdo, 1);
  if ( n == 0 ) { digitalWrite(LED4_PIN, LOW); }
  else { digitalWrite(LED4_PIN, HIGH); }
  n = bitRead( vdo, 2);
  if ( n == 0 ) { digitalWrite(LED3_PIN, LOW); }
  else { digitalWrite(LED3_PIN, HIGH); }
  n = bitRead( vdo, 3);
  if ( n == 0 ) { digitalWrite(LED2_PIN, LOW); }
  else { digitalWrite(LED2_PIN, HIGH); }
  n = bitRead( vdo, 4);
  if ( n == 0 ) { digitalWrite(LED1_PIN, LOW); }
  else { digitalWrite(LED1_PIN, HIGH); }
}

void UpdateLEDs() {
  if ( vdo == vdo_04 ) { 
    Led04_ON(); 
    return; }
  if ( vdo == vdo_14 ) {
    Led14_ON(); 
    return; }
  if ( vdo == vdo_24 ) {      
    Led24_ON(); 
    return; }
  if ( vdo == vdo_34 ) {
    Led34_ON(); 
    return; }
  Led44_ON();    
}

int EU_to_vdo( int s ) {
  if ( s < adc_EU_01 ) { return vdo_04; }
  if ( s < adc_EU_12 ) { return vdo_14; }
  if ( s < adc_EU_23 ) { return vdo_24; }
  if ( s < adc_EU_34 ) { return vdo_34; }
  return vdo_44;  
}

int US_to_vdo( int s ) {
  if ( s > adc_US_01 ) { return vdo_04; }
  if ( s > adc_US_12 ) { return vdo_14; }
  if ( s > adc_US_23 ) { return vdo_24; }
  if ( s > adc_US_34 ) { return vdo_34; }
  return vdo_44;  
}

int TMMB_to_vdo() { 
  int reading;
  reading = digitalRead( BOT_PIN );
  if ( reading == HIGH ) { return vdo_04; }
  reading = digitalRead( MIB_PIN );
  if ( reading == HIGH ) { return vdo_14; }
  reading = digitalRead( MIT_PIN );
  if ( reading == HIGH ) { return vdo_24; }
  reading = digitalRead( TOP_PIN ); 
  if ( reading == HIGH ) { return vdo_34; }
  return vdo_44; 
}

void LedsOFF() {
  digitalWrite(LED1_PIN, LOW);
  digitalWrite(LED2_PIN, LOW);
  digitalWrite(LED3_PIN, LOW);
  digitalWrite(LED4_PIN, LOW);  
  digitalWrite(LED5_PIN, LOW);  
}

void Led04_ON() {
  digitalWrite(LED5_PIN, HIGH);
  digitalWrite(LED4_PIN, LOW);
  digitalWrite(LED3_PIN, LOW);
  digitalWrite(LED2_PIN, LOW);    
  digitalWrite(LED1_PIN, LOW);  
}

void Led14_ON() {
  digitalWrite(LED5_PIN, HIGH);
  digitalWrite(LED4_PIN, HIGH);
  digitalWrite(LED3_PIN, LOW);
  digitalWrite(LED2_PIN, LOW);    
  digitalWrite(LED1_PIN, LOW);  
}
void Led24_ON() {
  digitalWrite(LED5_PIN, HIGH);
  digitalWrite(LED4_PIN, HIGH);
  digitalWrite(LED3_PIN, HIGH);
  digitalWrite(LED2_PIN, LOW);    
  digitalWrite(LED1_PIN, LOW);  
}
void Led34_ON() {
  digitalWrite(LED5_PIN, HIGH);
  digitalWrite(LED4_PIN, HIGH);
  digitalWrite(LED3_PIN, HIGH);
  digitalWrite(LED2_PIN, HIGH);    
  digitalWrite(LED1_PIN, LOW);  
}
void Led44_ON() {
  digitalWrite(LED5_PIN, HIGH);
  digitalWrite(LED4_PIN, HIGH);
  digitalWrite(LED3_PIN, HIGH);
  digitalWrite(LED2_PIN, HIGH);    
  digitalWrite(LED1_PIN, HIGH);  
}


