Ok on va y aller pas à pas, essaies cette version:
/*
 *Peripherals  connections
 *b0 - pin 28               //start - stop button
 *mo_IPTG pin 30            //IPTG pump
 *m1_HARVEST pin 31         //Harvest pump
 *m2_STIRRER pin 32         //Stirrer motor
 *m3_OD600 pin 33           //Flow pump for external sensor (eg optical density sensor)
 *lt - pin A8               //Light sensor for optical density measurement analog input
 *ph - pin A9               //PH sensor analog input
 *h_HEATER - pin 35         //Heater controll output
 *m4_OH_PUMP - pin 36       //OH pump output (for PH correction)
 *flow sensor pin 18        //To make sure the flow pump for external sensors is working
 *temperature sensor pin 29 //Digital temperature sensor
 */
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet2.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <Wire.h>
#include <DS1307RTC.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <SPI.h>
#include <EthernetUdp2.h>
#include <SD.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <util.h>
//hardware implementation specific constants
#define OD600 8 // OD600 analog sensor connected to A8 
#define PH 9 // ph anaolg sensor connected to A9
#define ONE_WIRE_BUS 29  // Temperature Data wire is connected to pin 29
   
#define M0_PIN 30
#define M1_PIN 31
#define M2_PIN 32
#define M3_PIN 33
#define H_PIN 35
#define OH_PUMP_PIN 36
#define AIR_PIN 37
#define B0_PIN 28
// process parameters and installed sensors calibration constants;
unsigned int l0 = 427; // light intensity reading through 1 cm path of LB medium (no bacteria present!) 
unsigned char l0_saved = 0;
float OD_IPTG = 0.7; 
float OD_MAX = 4 ;
unsigned char HARVEST_MINUTES = 10;
unsigned char  MIN_GROWING_MINUTES = 2;
unsigned char  MIN_IPTG_MINUTES = 2;
unsigned int  IPTG_PUMPING_SECONDS = 60;
unsigned int SD_LOG_FREQ_SEC = 600;
// linear regresion ph sensor calibration curve : ph_instant = PH_A * ph_adc +PH_B!
float PH_A = 0.0335768807;
float PH_B = -5.9885198198;
float PH_MIN = 7.0;
float PH_MAX = 7.4;
unsigned int OH_FLOW = 438; //438 uL/s, pump constant.
unsigned int OH_DROP_VOLUME = 20; //uL 5N NaOH
unsigned int OH_ON_OFF_MILISEC = 60000;
OneWire oneWire(ONE_WIRE_BUS);// Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire);// Pass our oneWire reference to Dallas Temperature
// Enter a MAC address for your controller bellow.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {0x90, 0xA2, 0xDA, 0x10, 0xDE, 0xE4};
EthernetClient client;
EthernetServer server(80);
byte ip[] = {10, 5, 5, 171};  // to be used if DHCP fails
IPAddress subnet(255, 255, 255, 0);
IPAddress gateway(10, 5, 5, 1);
IPAddress dnServer(8,8,8,8);
IPAddress timeServer(80,96,120,253); // NTP server, to be used if dns resolve fails.
IPAddress ntpIP = IPAddress (0,0,0,0);
unsigned int localPort = 8888; // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// timeZoneOffset = (Time Zone) * 3600L eg. (+2) * 3600L = 7200L for Romania
const long timeZoneOffset = 7200L; 
// sync to NTP server every "ntpSyncTime" seconds, set to 1 hour or more to be reasonable
unsigned long ntpSyncTime = 3600;
// adjust the sync latency with computer NTP client in seconds 
unsigned int syncLatency = 2;
// sd card variables
File file; 
//const uint8_t SD_CS = 53; // SD chip select for ethernet shield v1
const uint8_t SD_CS = 4; // SD chip select for ethernet shield v2
String file_name = "bioreactor.log"; //generic default name for log file
String fn = ""; 
int i=0;
int displayAtSecond = 0;
time_t t;
unsigned char mo_IPTG=0;
unsigned char m1_HARVEST=0;
unsigned char m2_STIRER=0;
unsigned char m3_OD600=0;
unsigned char m4_OH_PUMP=0;
unsigned char h_HEATER=0;
unsigned char b0=0;
unsigned char od_over_1 = 0;
unsigned char od_over_2 = 0;
char t0=0;
float od = 0.00;
float lt = 0.0;
unsigned char stage = 0;
unsigned long stage_uptime = 0;
unsigned long stage_milliseconds = 0;
unsigned long oh_on_milliseconds = 0;
unsigned long oh_off_milliseconds = 0;
unsigned long log_seconds = 0;
unsigned long total_OH_uL = 0;
unsigned int consecutive_OH_pulses = 0;
float ph_instant = 0;
unsigned long main_loop_millis = 0;
unsigned long old_millis = 0;
unsigned long current_millis = 0;
String LCD_string = "";
String serial_string = "";
String web_string = "";
//------------------------------------------------------------------------------
//FlowMeter variables
volatile int  flow_frequency;  // Measures flow meter pulses
unsigned int  l_hour;          // flow in litres per hour                    
unsigned char flowmeter_pin = 18;  // Flow Meter Pin number
unsigned long currentTime;
unsigned long cloopTime;
//-------------------------------------------------------------------------------
/*
 Udp NTP Client 
 Get the time from a Network Time Protocol (NTP) time server
 
 created 4 Sep 2010
 by Michael Margolis
 modified 9 Apr 2012
 by Tom Igoe
 modified 02 Sept 2015
 by Arturo Guadalupi
 addapted by Catalin Marinescu
 */
// send an NTP request to the time server at the given address, hoping to get the time!
unsigned long sendNTPpacket(IPAddress& address) {
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  packetBuffer[0] = 0b11100011; // LI, Version, Mode
  packetBuffer[1] = 0; // Stratum, or type of clodk
  packetBuffer[2] = 6; // Polling Interval
  packetBuffer[3] = 0xEC; // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;
  // all NTP fields have been initialized,
  // send a packet requesting a timestamp:
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}
// Real Time Clock functions:
void ntpSyncDS1307() {
 ntpIP = timeServer;
 Serial.print("NTP IP: "); 
 Serial.println(ntpIP);
 sendNTPpacket(ntpIP); // send an NTP packet to a time server
 // wait to see if a replay is available
 delay(1000);
 if (Udp.parsePacket()) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    // the timstamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, extract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900)
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800L;
    // substract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + timeZoneOffset + syncLatency;
    setTime(epoch);
    RTC.set(epoch);
    // print time and "Sync OK" message
    Serial.print(year());
    Serial.print('-');
    Serial.print(month());
    Serial.print('-');
    Serial.print(day());
    Serial.print(' ');
    Serial.print(hour());
    Serial.print(':');
    Serial.print(minute());
    Serial.print(':');
    Serial.print(second());
    Serial.print(' ');
    Serial.println("Sync OK");
  }
  else {
    Serial.println("UDP parse failed"); 
  }
}
//FlowMeter Interrupt function
void flow ()                  
{ 
   flow_frequency++;
} 
void refreshDisplay() //update LCD display
{
  LCD_string = String(year(t), DEC);
  LCD_string += ".";
  if (month(t) < 10) {
      LCD_string = String(LCD_string + '0' + String(month(t), DEC));
    }
    else {
    LCD_string = String(LCD_string +String(month(t), DEC));
    }
  LCD_string += ".";
  if (day(t) < 10) {
      LCD_string = String(LCD_string + '0' + String(day(t), DEC));
    }
    else {
    LCD_string = String(LCD_string +String(day(t), DEC));
    }
  LCD_string += " ";
  if (hour(t) < 10) {
      LCD_string = String(LCD_string + '0' + String(hour(t), DEC));
    }
    else {
    LCD_string = String(LCD_string +String(hour(t), DEC));
    }
  LCD_string += ":";
  if (minute(t) < 10) {
      LCD_string = String(LCD_string + '0' + String(minute(t), DEC));
    }
    else {
    LCD_string = String(LCD_string +String(minute(t), DEC));
    }
  LCD_string += ":";  
  if (second(t) < 10) {
      LCD_string = String(LCD_string + '0' + String(second(t), DEC));
    }
    else {
    LCD_string = String(LCD_string +String(second(t), DEC));
    }
    LCD_string +=" "; //spacer to fill the first LCD line
    LCD_string +="S"; 
    LCD_string = String(LCD_string +String(stage, DEC)); 
    switch (stage) {
      case 0:
        LCD_string +=":Ready  ";
        break;
      case 1:
        LCD_string +=":Growing";
        break;
      case 2:
        LCD_string +=":IPTG ON";
        break;
      case 3:
        LCD_string +=":Harvest";
        break;
      case 4:
        LCD_string +=":Done ! ";
        break;
    }
   LCD_string +=" ";  
   stage_uptime = (millis()-stage_milliseconds)/3600000;
   if (stage_uptime <10) LCD_string +="0";
   LCD_string = String(LCD_string +String(stage_uptime, DEC));
   LCD_string +=":";
   stage_uptime = ((millis()-stage_milliseconds)/60000)%60;
   if (stage_uptime <10) LCD_string +="0";
   LCD_string = String(LCD_string +String(stage_uptime, DEC));
   LCD_string +=":";
   stage_uptime = ((millis()-stage_milliseconds)/1000)%60;
   if (stage_uptime <10) LCD_string +="0";
   LCD_string = String(LCD_string +String(stage_uptime, DEC));
   serial_string = String(LCD_string);  //we use this part of LCD string also for serial port log.
   LCD_string +=" OD600 H Tc Lt  PH   ";
   if (od >0) LCD_string +=" ";
   LCD_string = String(LCD_string +String(od,2));
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(h_HEATER, DEC));
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(t0, DEC));
   LCD_string +=" ";
   LCD_string = String(LCD_string + String(lt,0)); 
   LCD_string +=" ";
   LCD_string = String(LCD_string +String(ph_instant, 2));
   LCD_string +="    ";
   if (second(t) %10 == 0) {  // log once every 10 seconds on the serial port!
     serial_string += String(" Sensors_flow:"+String(l_hour,DEC)+" Light:"+String(lt,0)+" OD600:"+String(od,2)+" Heater:"+String(h_HEATER, DEC)+" Tc:"+String(t0, DEC)+" pH:"+String(ph_instant, 2)+" OHuL:"+String(total_OH_uL,DEC));
     web_string = serial_string;
     Serial.println(String(serial_string)); 
   } 
}
void sdlog(){
  String log_string = ""; //start constructing a csv log file
  // timestamp,stage,light sensor adc,od600,ph,heater,Tc,total OH uL,ext. sensors flow L/h
  log_string = String(log_string +String(t, DEC));
  log_string +=",";
  log_string = String(log_string +String(stage, DEC));
  log_string +=",";
  log_string = String(log_string + String(lt,0));
  log_string +=",";
  log_string = String (log_string +String(od,2));
  log_string +=",";
  log_string = String(log_string +String(ph_instant, 2));
  log_string +=",";
  log_string = String(log_string +String(h_HEATER, DEC));
  log_string +=",";
  log_string = String(log_string +String(t0, DEC));
  log_string +=",";
  log_string = String(log_string +String(total_OH_uL, DEC));
  log_string +=",";
  log_string = String(log_string +String(l_hour, DEC));
  
  File file = SD.open(fn, FILE_WRITE);
  if (file) {
    file.println(log_string);
    file.close();
    Serial.print("log file updated: ");
    Serial.println(fn);
    }
    else {
      Serial.print("error opening ");
      Serial.println(fn);
      Serial.println(log_string);
    }
}
  // Saving & Loading Settings on SD Card with Arduino. Addapted from: 
  // http://overskill.alexshu.com/saving-loading-settings-on-sd-card-with-arduino/
  // initial version written by Alex Shu
void deleteSDnvmemory(){
  if (SD.remove("nvmemory.txt")) {
    Serial.println("nvmemory.txt removed from SD card.");
  }
  else {
    Serial.println("file delete failed: nvmemory.txt does not exist or SD card error.");
  }
}
void writeSDsettings(){  
  if (SD.remove("nvmemory.txt")) {
    Serial.println("Old nvmemory.txt found and removed");
  }
  else {
    Serial.println("nvmemory.txt not found on SD card. Trying to create it");
  }
  File pFile = SD.open("nvmemory.txt", FILE_WRITE);
  if (pFile) {
    Serial.println("File nvmemory.txt created");
    unsigned int fwbcount = 0;
    fwbcount += pFile.print("["); //we store the necessary startup sensors readings to be used in case of controller reset. In this case the initial optical density (l0);
    fwbcount += pFile.print("l0=");  // reference light intensity reading of optical density sensor
    fwbcount += pFile.print(l0);
    fwbcount += pFile.println("]");
    fwbcount += pFile.print("["); 
	fwbcount += pFile.print("l0_saved=");  // current process stage
    fwbcount += pFile.print(l0_saved);
    fwbcount += pFile.println("]");
    Serial.println(String(fwbcount,DEC)+" bytes written");
    pFile.close();
  }
  else Serial.println("Unable to create nvmemory.txt file on SD card !");
}
// converting string to Float
 float toFloat(String settingValue){
 char floatbuf[settingValue.length()+1];
 settingValue.toCharArray(floatbuf, sizeof(floatbuf));
 float f = atof(floatbuf);
 return f;
 }
 
 long toLong(String settingValue){
 char longbuf[settingValue.length()+1];
 settingValue.toCharArray(longbuf, sizeof(longbuf));
 long l = atol(longbuf);
 return l;
 }
 
void applySetting(String settingName, String settingValue) {
 if(settingName == "l0") {
      l0=settingValue.toInt();
  }
 if(settingName == "l0_saved") {
      l0_saved=settingValue.toInt();
  } 
 if(settingName == "OD_IPTG") {
      OD_IPTG=toFloat(settingValue);
  }
 if(settingName == "OD_MAX") {
      OD_MAX=toFloat(settingValue);
 }
 if(settingName == "PH_MIN"){
 PH_MIN=toFloat(settingValue);
 }
 if(settingName == "PH_MAX") {
 PH_MAX=toFloat(settingValue);
 }
 if(settingName == "PH_A") {
 PH_A=toFloat(settingValue);
 }
 if(settingName == "PH_B") {
 PH_B=toFloat(settingValue);
 }
 if(settingName == "OH_FLOW") {
 OH_FLOW=settingValue.toInt();
 }
 if(settingName == "OH_DROP_VOLUME") {
 OH_DROP_VOLUME=settingValue.toInt();
 }
 if(settingName == "OH_ON_OFF_MILISEC") {
 OH_ON_OFF_MILISEC=settingValue.toInt();
 }
 if(settingName == "HARVEST_MINUTES") {
 HARVEST_MINUTES=settingValue.toInt();
 }
 if(settingName == "MIN_GROWING_MINUTES") {
 MIN_GROWING_MINUTES=settingValue.toInt();
 }
 if(settingName == "MIN_IPTG_MINUTES") {
 MIN_IPTG_MINUTES=settingValue.toInt();
 }
 if(settingName == "IPTG_PUMPING_SECONDS") {
 IPTG_PUMPING_SECONDS=settingValue.toInt();
 }
 if(settingName == "SD_LOG_FREQ_SEC") {
 SD_LOG_FREQ_SEC=settingValue.toInt();
 }
 }
void readSDSettings(const char *filetoread){
 char character;
 String settingName;
 String settingValue;
 //File myFile = SD.open("nvmemory.txt");
 File myFile = SD.open(filetoread);
 if (myFile) {
 Serial.println(String(filetoread) +" variables:");
 while (myFile.available()) {
 character = myFile.read();
 while((myFile.available()) && (character != '[')){
 character = myFile.read();
 }
 character = myFile.read();
 while((myFile.available()) && (character != '=')){
 settingName = settingName + character;
 character = myFile.read();
 }
 character = myFile.read();
 while((myFile.available()) && (character != ']')){
 settingValue = settingValue + character;
 character = myFile.read();
 }
 if(character == ']'){
 //Serial.print("Name:");
 Serial.print(settingName);
 Serial.print("=");
 Serial.println(settingValue);
  
 // Apply the value to the parameter
 applySetting(settingName,settingValue);
 // Reset Strings
 settingName = "";
 settingValue = "";
 }
 }
 // close the file:
 myFile.close();
 } else {
 // if the file didn't open, print an error:
 Serial.println("error opening "+ String(filetoread));
 }
}
void setup()
{
  Serial.begin(9600);
  pinMode(B0_PIN, INPUT_PULLUP);
  pinMode(M0_PIN, OUTPUT);
  pinMode(M1_PIN, OUTPUT);
  pinMode(M2_PIN, OUTPUT);
  pinMode(M3_PIN, OUTPUT);
  pinMode(H_PIN, OUTPUT);
  pinMode(OH_PUMP_PIN, OUTPUT);
  pinMode(AIR_PIN, OUTPUT);
  pinMode(SD_CS, OUTPUT);
  digitalWrite(H_PIN, HIGH);
  digitalWrite(SD_CS, HIGH);
  digitalWrite(OH_PUMP_PIN, HIGH);
  m4_OH_PUMP = 0;
  digitalWrite(AIR_PIN, HIGH);
  m3_OD600 = 0;
  digitalWrite(M3_PIN, HIGH);
  m2_STIRER = 0;
  digitalWrite(M2_PIN, HIGH);
  m1_HARVEST = 0;
  digitalWrite(M1_PIN, HIGH);
  mo_IPTG = 0;
  digitalWrite(M0_PIN, HIGH);
  // try to avoid a known ethernet shield problem!
   // disable SD SPI
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
  // disable w5100 SPI
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  // try to avoid a known ethernet shield problem!
  sensors.begin(); // for temp onewire sensors. IC Default 9 bit. If you have troubles consider setting it to 12. 
// LCD2S by Modtronix on i2c bus 
  Wire.begin();  //Join bus as master
  Wire.beginTransmission(0x28);  //Start condition at the given I2C address or Mark the beginning of a data packet, change 0x28 to your device's I2C address
  Wire.write(0x80);  //Initiate Display Commands
  Wire.write(0x0c);  //Clear display and go to first line
  Wire.write("Bioreactor starting");  //Write message
  Wire.endTransmission(false);  //End condition or Mark the end of data packet and send to LCD and hold bus connection
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  if (timeStatus() != timeSet){
    Serial.println("RTC time error");
  }
  else{
    Serial.println("Time set by RTC");
  }
 delay(200);
 if (Ethernet.begin(mac) == 0) {
   Serial.println("Failed to configure Ethernet using DHCP...using default ip settings");
   Ethernet.begin(mac, ip, dnServer, gateway, subnet);
  }
  else {
    Serial.println("Ethernet configured by DHCP");
    }
 // print local IP address:
 Serial.print("My IP address: ");
 for (byte thisByte = 0; thisByte < 4; thisByte++) {
   // print the value of each byte of the IP address:
   Serial.print(Ethernet.localIP()[thisByte], DEC);
   Serial.print(".");
 }
 Serial.println();
 /*
  * // DNS is buggy...!
 DNSClient dns;
 dnServer = Ethernet.dnsServerIP();
 if (dnServer == 0) {
    Serial.print("DNS by DHCP failed, using 8.8.8.8");
    dnServer = (8,8,8,8);
 }
// dns.begin(Ethernet.dnsServerIP());
 dns.begin(dnServer);
 Serial.print("My DNS address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
   // print the value of each byte of the DNS address:
   Serial.print(dnServer[thisByte], DEC);
   Serial.print(".");
 }
 Serial.println();
 dns.getHostByName("pool.ntp.org",ntpIP);
 if (ntpIP!= (0,0,0,0)) {
    Serial.print("DNS: pool.ntp.org: ");
 }
 else {
    ntpIP = timeServer;
    Serial.print("DNS failed, using default NTP IP: ");
  }
 */ 
 ntpIP = timeServer;
 Serial.print("NTP IP: ");
 Serial.println(ntpIP);
  Udp.begin(localPort); 
  Serial.println("connecting to ntp server");
  Serial.print(year());
  Serial.print('-');
  Serial.print(month());
  Serial.print('-');
  Serial.print(day());
  Serial.print(' ');
  Serial.print(hour());
  Serial.print(':');
  Serial.print(minute());
  Serial.print(':');
  Serial.print(second());
  Serial.println(" --- RTC Time");
    ntpSyncDS1307();
  // create the alarms 
  Alarm.alarmRepeat(7,30,0, ntpSyncDS1307);  // 7:30am every day
  Alarm.alarmRepeat(19,30,0, ntpSyncDS1307);  // 7:30pm every day 
  
 //web server init
  server.begin();
  Serial.print("web server is at ");
  Serial.println(Ethernet.localIP());
  
 //FlowMeter
   pinMode(flowmeter_pin, INPUT_PULLUP);
   attachInterrupt(5,flow,RISING); // Setup Interrupt 
                                     // see http://arduino.cc/en/Reference/attachInterrupt
   sei();                            // Enable interrupts  
   currentTime = millis();
   cloopTime = currentTime;
    // set date time callback function
 //SdFile::dateTimeCallback(dateTime);  
 // log csv file on SD card gets a unique name from startup timestamp
  t = now();
  file_name = String(t,DEC);
  fn = String(file_name.substring(2) + ".CSV");
  Serial.print("File Name: ");
  Serial.println(fn);
  
  if (!SD.begin(SD_CS)) {
  Serial.println("SD init failed");
  }
  else Serial.println("SD init ok");
  File file = SD.open(fn, FILE_WRITE);
  if (file) {
    file.println("timestamp,stage,Lt,OD600,PH,Heater,Tc,OHuL,l_hour");
    file.close();
    Serial.println("log file header written");
  }
  else Serial.println("Error writing log file header");
  readSDSettings("setup.txt"); //read the setup variables if exist on SD card
  readSDSettings("nvmemory.txt"); //read the non volatile memory variables if exist on SD card
//watchDog does not work on this device, seams to be buggy :( so we don't use it
//wdt_enable(10000);
}
void loop(){
 //wdt_reset(); //watchdog is disabled, so we don't need to reset it.
  t = now();
  old_millis = current_millis;
  current_millis = millis();
  main_loop_millis = current_millis - old_millis;
  t0 = int(sensors.getTempCByIndex(0));
  lt = analogRead(OD600);
  //ph_instant = analogRead(PH);
  ph_instant = ( analogRead(PH)* PH_A) + PH_B; //regression function resulted from ph probe calibration !
  od = -log(lt/l0); // calculate optical density based of light intensity value 
  if (digitalRead(B0_PIN) == LOW) { 
    b0 = 1;
  }
  else {
    b0 = 0;
  }
  if (timeStatus() != timeSet) setSyncProvider(RTC.get); // the function to get the time from the RTC
  if (timeStatus() != timeSet) Serial.println("Unable to sync with the RTC");
  sensors.requestTemperatures(); // Send the command to get temperatures
//FlowMeterCode
   currentTime = millis();
   // Every second, calculate and print litres/hour
   if(currentTime >= (cloopTime + 1000)){     
      cloopTime = currentTime;              // Updates cloopTime
            l_hour = (flow_frequency * 60 / 7.5); // (Pulse frequency x 60 min) / 7.5Q = flow rate in L/hour 
      flow_frequency = 0;                   // Reset Counter
//      Serial.print(l_hour, DEC);            // debug print L/hour
//      Serial.println(" L/hour");
   }
  if ((od > OD_IPTG) ) {  
    if (od_over_1 <10) od_over_1++; // making sure we get stable light sensor reading
  }
  else {
    od_over_1 = 0;
  }
  if (od > OD_MAX)  {
    if (od_over_2 <10) od_over_2++;
  }
  else {
    od_over_2 = 0;
  }   
  if (b0 == 0) { //if button is OFF, stop all the motors/pumps and go to ready state.
    h_HEATER = 0;
    digitalWrite(H_PIN, HIGH);
    digitalWrite(AIR_PIN, HIGH);
    m3_OD600 = 0;
    digitalWrite(M3_PIN, HIGH);
    //Mo3->run(RELEASE);
    m2_STIRER = 0;
    digitalWrite(M2_PIN, HIGH);
    m1_HARVEST = 0;
    digitalWrite(M1_PIN, HIGH);
    digitalWrite(OH_PUMP_PIN, HIGH);
    if (mo_IPTG == 1){
      digitalWrite(M0_PIN, HIGH);
      mo_IPTG = 0;
    }
    if (stage >0) deleteSDnvmemory();
    stage = 0;
    l0_saved = 0;
  }
  else switch (stage) {
    case 0:
      stage = 1;
      if (l0_saved==0){
      l0_saved=1;
      l0 = lt; // zero the OD600 sensor.
      writeSDsettings(); //write on the sd card as non volatile (reset persistent) memory.
      } 
      stage_milliseconds = millis();
      h_HEATER = 1;
      digitalWrite(H_PIN, LOW);
      digitalWrite(AIR_PIN, LOW);
      if (m3_OD600 == 0) { 
             m3_OD600 = 1;
             digitalWrite(M3_PIN, LOW);
      }        
      m2_STIRER = 1;
      digitalWrite(M2_PIN, LOW);
      break;
    case 1:
        if ((od_over_1 == 10) and  (millis() - stage_milliseconds > MIN_GROWING_MINUTES*60000))  { 
        Serial.print("millis:");
        Serial.print(millis());
        Serial.print("-Stage miliseconds:");
        Serial.print(stage_milliseconds);
        Serial.print(">120000:");
        Serial.println(120000);
        stage = 2;
		writeSDsettings();
        stage_milliseconds = millis();
        if (mo_IPTG == 0) {
              mo_IPTG = 1;
              digitalWrite(M0_PIN, LOW);    
              //Mo0->run(FORWARD);         
              Serial.println("IPTG pump ON"); 
          }
      }
      break;
    case 2:
      if ((mo_IPTG == 1) and (millis() - stage_milliseconds > IPTG_PUMPING_SECONDS*1000)){
        mo_IPTG = 0;
        digitalWrite(M0_PIN, HIGH);     
      }
      if ((od_over_2 == 10) and (millis() - stage_milliseconds > MIN_IPTG_MINUTES*60000)) {
        mo_IPTG = 0;
        digitalWrite(M0_PIN, HIGH); // iptg pump off
        stage = 3; //Harvest
		writeSDsettings();
        stage_milliseconds = millis();
        h_HEATER = 0;
        digitalWrite(H_PIN, HIGH);
        digitalWrite(AIR_PIN, HIGH);
        if (m3_OD600 == 1) {
            m3_OD600 = 0;
            digitalWrite(M3_PIN, HIGH);
        }
        m2_STIRER = 0;
        digitalWrite(M2_PIN, HIGH);
        m1_HARVEST = 1;
        digitalWrite(M1_PIN, LOW);
      }
      break;
     case 3:
      if (millis() - stage_milliseconds > HARVEST_MINUTES*60000){
        stage = 4; //Done!
		writeSDsettings();
        stage_milliseconds = millis();
        m1_HARVEST = 0;
        digitalWrite(M1_PIN, HIGH);
      }
      break;
  }
//WRITE LOG EVERY "SD_LOG_FREQ_SEC" SECONDS
if (t - log_seconds > SD_LOG_FREQ_SEC){
  sdlog();
  log_seconds = t;
}
//DISPLAY REFRESH EVERY SECOND
  if (displayAtSecond != second(t)) {
    refreshDisplay(); 
    displayAtSecond = second(t);
  }
 
// OH pump control code, for ph adjustment!
 String oh_log = "";
  if ((stage >0) and (stage <3) and (ph_instant < PH_MIN) and (millis() - oh_on_milliseconds > OH_ON_OFF_MILISEC)) {  
       oh_log = "Pumping ";
       oh_log += String(OH_DROP_VOLUME, DEC);
       oh_log += "uL OH in ";
       oh_log += String(OH_DROP_VOLUME*1000 / OH_FLOW, DEC);
       oh_log += " milliseconds";
       Serial.println(oh_log);
       total_OH_uL += OH_DROP_VOLUME;
       consecutive_OH_pulses +=1;
       oh_log = "Main loop takes ";
       Serial.print(oh_log);
       Serial.print(main_loop_millis);
       oh_log = " milliseconds";
       Serial.println(oh_log);
       oh_log = "OH Pump ON ";
       Serial.println(oh_log);
       m4_OH_PUMP = 1;
       oh_on_milliseconds = millis();
       oh_off_milliseconds = 0;
       Serial.println(millis());
       digitalWrite(OH_PUMP_PIN, LOW);
       delay((OH_DROP_VOLUME*1000 / OH_FLOW) % main_loop_millis); // compensate for main loop delay, useful if calculated pumping time is shorter than main loop delay!
  }
  if (m4_OH_PUMP == 1 and millis() - oh_on_milliseconds > OH_DROP_VOLUME*1000 / OH_FLOW ){
       Serial.println(millis());
       digitalWrite(OH_PUMP_PIN, HIGH);
       oh_log = "OH Pump OFF ";
       Serial.println(oh_log);
       m4_OH_PUMP = 0;
       oh_off_milliseconds = millis();  
  }
if ((consecutive_OH_pulses > 10) and (ph_instant < PH_MIN)) {   
  OH_DROP_VOLUME=OH_DROP_VOLUME*2; //addaptive OH compensation (if default OH drop volume fails to rise pH after 10 drops, we double the drop volume)
  consecutive_OH_pulses = 0;
}
//webserver code
//----------------------------------------------------------------------------------------------
   // listen for incoming clients
  EthernetClient client = server.available();
 // if (client) {
    if  (client.connected()) {
     // Serial.println("new Web client connected");
      if (client.available()) {
          char c1 = client.read();
          char c2 = client.read();    //standard http request ends with a blank line.
          if (c1 == '\r' && c2 == '\n'){
           // send a standard http response header
            Serial.write("Sending data to Web client \n");
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: close");  // the connection will be closed after completion of the response
            client.println("Refresh: 60");  // refresh the page automatically every 60 sec
            client.println();
            client.println("<!DOCTYPE HTML>");
            client.println(web_string);     // send actual process values to web client
            delay(1);
          // close the connection:
            client.stop();
            Serial.println("Web client disconnected"); 
          }
      }
    }
  //}
 }