/*
Splash controller V3
Alex Pikkert
December 2020
The original design: https://photobuilds.co.uk/arduino-drop-controller/
Changes I made: 
* 4 drops instead of 3 drops.
* start message "splash controller V3".
* LCD 1602 I2C instead of LCD 1602 keypad shield. (only 4 wires to the LCD).
* Separate keypad with resistor chain on A1 and different Mosfet design integrated on the PCB. 
* EEPROM instructions GET/PUT instead of read/write to store INT numbers >255 (these numbers need 2 bytes per number)
* "Clear valve" routine added (press btnDOWN durung startup, press btnSELECT to stop). 
* This opens the valve continuously.
* "Test droplet" routine added (press btnUP during startup, press btnSELECT to stop). 
* This drops a droplet every 2 seconds for testing camera focus setting. No camera action or flash.
*/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27,16,2); 

int lcd_key     = 0;
int adc_key_in  = 0;
int lastkey     = 5;                                                   // initialise to no button pressed
boolean entry_mode  = false;                                           // entry_mode false = line select, true = value select
int tValve = 2;
int tFlash = 3;
int tCamera = 11;

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

String MessageArray[] = {"No of Drops  ","Drop 1 Size  ","Gap 1 Length ","Drop 2 Size  ","Gap 2 Length ","Drop 3 Size  ","Gap 3 Length ","Drop 4 Size  ","Flash Delay  ", "Fire Flash!     "};
int ParamValues[10] = {1,10,100,20,200,30,300,30,300,0};               // only needed for initial startup (for first EEPROM GET/PUT)  
int ParamMax[10] = {4,99,999,99,999,99,999,99,999,1};
int msg_point = 0;
int msg_point2 = 1;
int x_pos = 13;                                                        // cursor position
int valstep = 1;                                                       // size of step to increment/decrement by

void setup(){
  pinMode(tValve,OUTPUT);                                              // valve control pin as output
  pinMode(tFlash,OUTPUT);                                              // set flash trigger pin to be output
  pinMode(tCamera,OUTPUT);                                             // set camera trigger pin as output
  digitalWrite(tValve,LOW);                                            // set them all low to start
  digitalWrite(tFlash,LOW);  
  digitalWrite(tCamera,LOW);
  
  for (int i=0; i<=8; i++) {
     EEPROM.get(i*2,ParamValues[i]);                                   // read last settings used from EEPROM
  }

  lcd.init();
   
  lcd.clear();
  lcd.backlight(); 
  lcd.setCursor(0,0);
  lcd.print("*splash control*");
  lcd.setCursor(0,1);
  lcd.print(" ***** V3 ***** ");
  delay(2000);

lcd_key = read_keypad_buttons();  
if (lcd_key == btnDOWN) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("* clear  valve *");
  lcd.setCursor(0,1);
  lcd.print("****************");
  digitalWrite(tValve,HIGH); 
  while (lcd_key != btnSELECT) {
  lcd_key = read_keypad_buttons();  
  if (lcd_key == btnSELECT) {break;}
  } 
  digitalWrite(tValve,LOW); 
  }

lcd_key = read_keypad_buttons();  
if (lcd_key == btnUP) {
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("* test droplet *");
  lcd.setCursor(0,1);
  lcd.print("****************");
  
  while (lcd_key != btnSELECT) {
  lcd_key = read_keypad_buttons();  
  digitalWrite(tValve,HIGH); 
  delay(15);
  digitalWrite(tValve,LOW); 
  delay(2000);
  if (lcd_key == btnSELECT) {break;}
  } 
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(MessageArray[0]);
  lcd.setCursor(15,0);
  lcd.print(ParamValues[0]);
  lcd.setCursor(0,1);
  lcd.print(MessageArray[1]);
  if (ParamValues[1] < 8) lcd.setCursor(15,1);
  else lcd.setCursor(14,1);
  lcd.print(ParamValues[1]);
}

void loop() {

  lcd.backlight();                                                     // turn display backlight on

  lcd_key = read_keypad_buttons();                                     // read if the last keypress has been used
  if (lcd_key != lastkey) {
    lastkey = lcd_key;  
  
      switch (lcd_key){                                                // depending on which button was pushed, we perform an action
      case btnDOWN:{                                                   // push button "RIGHT" and show the word on the screen
        if (entry_mode==false) {
          msg_point = msg_point + 1;
          if (msg_point==10) {
            msg_point =0; 
          }
          break;
        }
        else {
          ParamValues[msg_point] = ParamValues[msg_point] - valstep;
          if (ParamValues[msg_point]<1) ParamValues[msg_point]=1;
          lcd.setCursor(13,0);
          lcd.print("   ");                                            // clear the value area of top display line
          if (ParamValues[msg_point] < 10) lcd.setCursor(15,0);
          else if (ParamValues[msg_point] < 100) lcd.setCursor(14,0);
          else lcd.setCursor(13,0);
          lcd.print(ParamValues[msg_point]);
          lcd.setCursor(x_pos,0);
          lcd.blink();
          break;
        }
      }
      case btnUP:{
        if (entry_mode==false) {
          msg_point = msg_point-1;
          if (msg_point<0) {
            msg_point =9; 
          }
          break;
        }
        else {
          ParamValues[msg_point] = ParamValues[msg_point] + valstep;
          if (ParamValues[msg_point] > ParamMax[msg_point]) {          // trap for maximum values
            ParamValues[msg_point] = ParamMax[msg_point];              // if greater, limit to maximum value
          }
          lcd.setCursor(13,0);
          lcd.print("   ");                                            // clear the value area of top display line
          if (ParamValues[msg_point] < 10) lcd.setCursor(15,0);
          else if (ParamValues[msg_point] < 100) lcd.setCursor(14,0);
          else lcd.setCursor(13,0);

          lcd.print(ParamValues[msg_point]);
          lcd.setCursor(x_pos,0);
          lcd.blink();
          break; 
        }
      }    
      case btnRIGHT:{
        if (entry_mode ==true) {
          x_pos = x_pos + 1;
          if (x_pos == 16) x_pos = 15;
          if (x_pos==13) valstep = 100;
          else if (x_pos==14) valstep = 10;
          else valstep = 1;
        
          lcd.setCursor(x_pos,0);
          lcd.blink();
         }
        break;
      }
      case btnLEFT:{
        if (entry_mode==true) {
          x_pos = x_pos - 1;
          if (msg_point==1 || msg_point==3 || msg_point==5 || msg_point==7){          // only allowing two digits for drop sizes
            if (x_pos < 14) x_pos=14;
          }
          else if (msg_point==2 || msg_point==4 || msg_point==6 || msg_point==8) {     // allowing three digits for gaps and flash delay
            if (x_pos < 13) x_pos=13;
          }
          else {
            if (x_pos < 15) x_pos=15;                                                  // and a single digit for number of drops
          }
          if (x_pos==13) valstep = 100;
          else if (x_pos==14) valstep = 10;
          else valstep = 1;

          lcd.setCursor(x_pos,0);
          lcd.blink();
          delay(100);
        }
        break;
      }
      case btnSELECT:{
        entry_mode = !entry_mode;
        if (entry_mode==true) {
          if (msg_point < 9) {
            lcd.setCursor(0,0);
            lcd.print(MessageArray[msg_point]);
            if (ParamValues[msg_point] < 10) lcd.setCursor(15,0);
            else if (ParamValues[msg_point] < 100) lcd.setCursor(14,0);
            else lcd.setCursor(13,0);
            lcd.print(ParamValues[msg_point]);
            lcd.setCursor(0,1);
            lcd.print(MessageArray[msg_point2]);
            if (ParamValues[msg_point2] < 10) lcd.setCursor(15,1);
            else if (ParamValues[msg_point2] < 100) lcd.setCursor(14,1);
            else lcd.setCursor(13,1);
            lcd.print(ParamValues[msg_point2]);       

            valstep = 1;                                               // set step to 1 and column to units for new selection
            x_pos = 15;
            lcd.setCursor(x_pos,0);
            lcd.blink();
          }
          else {
            for (int i=0; i<=8; i++) {
              EEPROM.put(i*2, ParamValues[i]);                         // write the current values to EEPROM
            }
            TriggerValve();                                            // trigger the valve
            entry_mode = !entry_mode;                                  // then come straight back out of entry mode
          }
        }
        else {
          lcd.noBlink();
        }
        break;
      }
      case btnNONE:{
        break;                                                         // do nothing
      }
    }
    if (entry_mode==false) {
        lcd.setCursor(0,0);
        lcd.print("                ");                                 // clear the top display line
        lcd.setCursor(0,0);
        lcd.print(MessageArray[msg_point]);
        if (ParamValues[msg_point] < 10) lcd.setCursor(15,0);
        else if (ParamValues[msg_point] < 100) lcd.setCursor(14,0);
        else lcd.setCursor(13,0);
        lcd.print(ParamValues[msg_point]);
        lcd.setCursor(0,1);
        if (msg_point==9) {
          msg_point2 =0; 
        }
        else {
          msg_point2 = msg_point+1;
        }
        lcd.print("                ");                                 // clear the bottom display line
        lcd.setCursor(0,1);
        lcd.print(MessageArray[msg_point2]);
        if (ParamValues[msg_point2] < 10) lcd.setCursor(15,1);
        else if (ParamValues[msg_point2] < 100) lcd.setCursor(14,1);
        else lcd.setCursor(13,1);
        lcd.print(ParamValues[msg_point2]);
      }
    }
  }


void TriggerValve() {

int stepup = 0;
  lcd.noBacklight();
  delay(250);                                                          // wait quarter second after pressing the button
  digitalWrite(tCamera,HIGH);                                          // trigger camera
  delay(100);                                                          // give the camera time to settle after triggering
  for ( int dropcnt=1; dropcnt<=ParamValues[0]; dropcnt++) {
    digitalWrite(tValve,HIGH);                                         // open valve
    delay(ParamValues[dropcnt+stepup]);
    digitalWrite(tValve,LOW);                                          // close valve
    if (dropcnt < ParamValues[0]) {
      delay(ParamValues[dropcnt+stepup+1]);                            // delay between drops
      stepup=stepup+1;
    }
  }
  delay(ParamValues[8]);                                               // delay before flash trigger 
  digitalWrite(tFlash,HIGH);                                           // trigger flash
  delay(15);                                                           // hold high long enough to actually trigger the flash
  digitalWrite(tFlash,LOW);
  
  digitalWrite(tCamera,LOW);                                                            
  lcd.backlight();

}

int read_keypad_buttons(){                                             // read the buttons
    adc_key_in = analogRead(A1);                                       // read the value from the sensor
    delay(200);                                                        // delay to avoid multiple readings per key press
    if (adc_key_in > 1000) return btnNONE;
    if (adc_key_in < 350) return btnRIGHT;  
    if (adc_key_in < 550) return btnUP; 
    if (adc_key_in < 700) return btnDOWN; 
    if (adc_key_in < 800) return btnLEFT; 
    if (adc_key_in < 950) return btnSELECT;  
    return btnNONE;
}
