Problème code pour LCD

Bonjour cherif27

Ça devrait être possible, l'essentiel de refreshDisplay est un concaténation de LCD_screen et l'accès au bus i2C.
Je fais un essai, dans la matinée avec un LCD 20x4.

Cordialement
jpbbricole

Bonjour jpbbricole
effectivement à un moment je me suis dit refaire un autre code pour cette partie car j'ai l'impression que tt le système ne continue pas le procès tant qu il n y a pas d'affichage sur LCD .... ou bien je n'ai pas bien compris le code , je suis débutant :slight_smile:

Bonjour cherif27

Peut-être ça bloque dans la partie Wire (i2c) du fait du manque de l'affichage, je ne peut pas certifier.
Je fais un essai d'intégration du LCD 2004.

A+
Cordialement
jpbbricole

merci bien pour ton aide
cordialement

Bonjour Cherif27

Je crois que c'est tout bon, au retour du marché, je poste ça.

A+
Cordialement
jpbbricole

Le code cité utilise une profusion de String. Il serait étonnant que cela fonctionne très longtemps.

Bonjour hbachetti

Moi qui ne suis pas du tout "du sérail", comme on dit, mais un Arduiniste convaincu, je m'étonne de cette aversion, de certaines personnes envers, la classe String. Classe qui simplifie grandement les choses et est surtout largement prônée par "l'équipe" Arduino. Se seraient ils trompés à ce point là et, depuis le temps, pourquoi n'ont il pas corrigé le tir?
Plein de questions sans réponses.

Cordialement
jpbbricole

Bonjour Cherif27

Voilà les correctifs

Chaque ligne est dans la section où elle doit être
refreshDisplay() est complète et doit remplacer entièrement ton actuelle.
La bibliothèque utilisée est celle-ci. Suivant les versions, l'initialisation s'écrit

lcd2004.init();
ou
lcd2004.begin();

L'adresse de l'affichage est 0x27

Comme je n'ai pas l'environnement complet, j'ai du faire "comme si"
Problème que tu risques d'avoir est la mise en page, mais là, il n'y a que toi qui peut le dire.
Si jamais, dis moi ce qu'il doit y avoir sur la ligne 1, ligne 2... je t'indiquerai comment faire

#include <LiquidCrystal_I2C.h>     // https://www.arduino.cc/reference/en/libraries/liquidcrystal-i2c/

LiquidCrystal_I2C lcd2004(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

void setup()
{
	Serial.begin(115200);
	lcd2004.init();
	lcd2004.backlight();
	
}

void loop()
{
}

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));
  }

lcd2004.clear();
lcd2004.home();
lcd2004.print(LCD_string);
}

A+
Cordialement
jpbbricole

Je pense que tu n'as pas lu mon article, ou de travers. Il y a un exemple de code simple, exécuté sur NANO et MEGA. Il suffit d'essayer.

La classe String est largement déconseillée par bon nombre de membres du forum, et cela ne date pas d'hier.
On voit beaucoup de tutoriels utilisant la classe String, écrits par des néophytes qui ne connaissent pas la fragmentation mémoire.
Sur ESP8266 et ESP32 je ne m'en prive pas, mais sur ARDUINO il est préférable de s'en passer.

je viens de tester le code, malheureusement j'ai beaucoup d'erreur lors de la compil concernant "wire" (error: 'Wire' does not name a type)

l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:325:3: error: 'Wire' does not name a type
   Wire.beginTransmission(0x28);  //Start condition at 0x28 (LCD) I2C address or Mark the beginning of a data packet.
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:326:3: error: 'Wire' does not name a type
   Wire.write(0x80);  //Initiate I2C Display Commands
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:327:3: error: 'Wire' does not name a type
   Wire.write(0x0c);  //Clear display and go to first line
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:328:3: error: expected unqualified-id before 'for'
   for (i=0;i<20;i++) {
   ^~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:328:12: error: 'i' does not name a type
   for (i=0;i<20;i++) {
            ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:328:17: error: 'i' does not name a type
   for (i=0;i<20;i++) {
                 ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:331:3: error: 'Wire' does not name a type
   Wire.endTransmission(true);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:332:3: error: 'Wire' does not name a type
   Wire.beginTransmission(0x28);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:333:3: error: 'Wire' does not name a type
   Wire.write(0x80);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:334:3: error: expected unqualified-id before 'for'
   for (i=20;i<40;i++) {
   ^~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:334:13: error: 'i' does not name a type
   for (i=20;i<40;i++) {
             ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:334:18: error: 'i' does not name a type
   for (i=20;i<40;i++) {
                  ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:338:3: error: 'Wire' does not name a type
   Wire.endTransmission(true);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:339:3: error: 'Wire' does not name a type
   Wire.beginTransmission(0x28);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:340:3: error: 'Wire' does not name a type
   Wire.write(0x80);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:341:3: error: expected unqualified-id before 'for'
   for (i=40;i<60;i++) {
   ^~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:341:13: error: 'i' does not name a type
   for (i=40;i<60;i++) {
             ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:341:18: error: 'i' does not name a type
   for (i=40;i<60;i++) {
                  ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:345:3: error: 'Wire' does not name a type
   Wire.endTransmission(true);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:346:3: error: 'Wire' does not name a type
   Wire.beginTransmission(0x28);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:347:3: error: 'Wire' does not name a type
   Wire.write(0x80);
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:348:3: error: expected unqualified-id before 'for'
   for (i=60;i<80;i++) {
   ^~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:348:13: error: 'i' does not name a type
   for (i=60;i<80;i++) {
             ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:348:18: error: 'i' does not name a type
   for (i=60;i<80;i++) {
                  ^
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:353:3: error: 'Wire' does not name a type
   Wire.endTransmission(false); // gracefully closing 12c bus
   ^~~~
l:\Telechargements\S6_bioreactor_2.5 v or\S6_bioreactor_2.5 v or.ino:354:1: error: expected declaration before '}' token
 }
 ^
Multiple libraries were found for "DallasTemperature.h"
 Used: H:\Mes documents\Arduino\libraries\DallasTemperature
 Not used: H:\Mes documents\Arduino\libraries\MAX31850_DallasTemp
Multiple libraries were found for "DS1307RTC.h"
 Used: H:\Mes documents\Arduino\libraries\DS1307RTC
 Not used: H:\Mes documents\Arduino\libraries\DS1307RTC-master
Multiple libraries were found for "TimeLib.h"
 Used: H:\Mes documents\Arduino\libraries\Time-master
 Not used: H:\Mes documents\Arduino\libraries\Time
Multiple libraries were found for "OneWire.h"
 Used: H:\Mes documents\Arduino\libraries\OneWire
 Not used: H:\Mes documents\Arduino\libraries\MAX31850_OneWire
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\A1_Duinoedu_OneWire
Multiple libraries were found for "LiquidCrystal_I2C.h"
 Used: H:\Mes documents\Arduino\libraries\LiquidCrystal_I2C
 Not used: H:\Mes documents\Arduino\libraries\Arduino-LiquidCrystal-I2C-library-master
 Not used: H:\Mes documents\Arduino\libraries\New-LiquidCrystal-master
Multiple libraries were found for "Dhcp.h"
 Used: H:\Mes documents\Arduino\libraries\Ethernet2
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\Ethernet
Multiple libraries were found for "SD.h"
 Used: H:\Mes documents\Arduino\libraries\SD
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\SD
Compilation error: Error: 2 UNKNOWN: exit status 1

Bonsoir cherif27

Je pense que tu as un problème d'intégration des modifications que je t'ai proposeés.
Je te prépare une versuin complète.

A+
Cordialement
jpbbricole

merci . sinon histoire d'apprendre comme je suis débutant, j'aimerai savoir à quoi renvois cette erreur ? es que j'ai oublié un crochet ou un point-virgule...

Bonsoircherif27

Comme je n'ai pas l'environnement nécessaire, il m'est assez difficile d'intégrer les modifications et de tester l'entier du programme, voici la version complète, essaies et passes-moi les erreurs éventuelles.
Il peut avoir une erreur à l'initialisation de l'affichage, il y a 2 modes dépendant de la bibliothèque utilisée:
lcd2004.init();
ou
lcd2004.begin();

L'adresse de l'affichage est 0x27

Le programme complet:`
/*
*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 <LiquidCrystal_I2C.h> // LiquidCrystal I2C - Arduino Reference
#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;

LiquidCrystal_I2C lcd2004(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display

// 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));
}

lcd2004.clear();
lcd2004.home();
lcd2004.print(LCD_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);

lcd2004.init();
lcd2004.backlight();

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.

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 :frowning: 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_MINUTES60000)) {
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_MINUTES60000)) {
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_VOLUME1000 / 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_VOLUME1000 / 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("");
client.println(web_string); // send actual process values to web client
delay(1);
// close the connection:
client.stop();
Serial.println("Web client disconnected");
}
}
}
//}
}
`
Je crois les doigts :blush:

A+
Cordialement
jpbbricole

bon changement d'erreur :smiley: je crois qu il point le Shield Ethernet ,

h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:70:6: error: scalar object 'mac' requires one element in initializer
byte mac = {0x90, 0xA2, 0xDA, 0x10, 0xDE, 0xE4};
     ^~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:73:6: error: scalar object 'ip' requires one element in initializer
byte ip = {10, 5, 5, 171}; // to be used if DHCP fails
     ^~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino: In function 'void loop()':
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:733:60: error: 'MIN_GROWING_MINUTES60000' was not declared in this scope
if ((od_over_1 == 10) and (millis() - stage_milliseconds > MIN_GROWING_MINUTES60000)) {
                                                           ^~~~~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:733:60: note: suggested alternative: 'MIN_GROWING_MINUTES'
if ((od_over_1 == 10) and (millis() - stage_milliseconds > MIN_GROWING_MINUTES60000)) {
                                                           ^~~~~~~~~~~~~~~~~~~~~~~~
                                                           MIN_GROWING_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:752:57: error: 'IPTG_PUMPING_SECONDS1000' was not declared in this scope
if ((mo_IPTG == 1) and (millis() - stage_milliseconds > IPTG_PUMPING_SECONDS1000)){
                                                        ^~~~~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:752:57: note: suggested alternative: 'IPTG_PUMPING_SECONDS'
if ((mo_IPTG == 1) and (millis() - stage_milliseconds > IPTG_PUMPING_SECONDS1000)){
                                                        ^~~~~~~~~~~~~~~~~~~~~~~~
                                                        IPTG_PUMPING_SECONDS
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:756:60: error: 'MIN_IPTG_MINUTES60000' was not declared in this scope
if ((od_over_2 == 10) and (millis() - stage_milliseconds > MIN_IPTG_MINUTES60000)) {
                                                           ^~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:756:60: note: suggested alternative: 'MIN_IPTG_MINUTES'
if ((od_over_2 == 10) and (millis() - stage_milliseconds > MIN_IPTG_MINUTES60000)) {
                                                           ^~~~~~~~~~~~~~~~~~~~~
                                                           MIN_IPTG_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:776:37: error: 'HARVEST_MINUTES60000' was not declared in this scope
if (millis() - stage_milliseconds > HARVEST_MINUTES60000){
                                    ^~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:776:37: note: suggested alternative: 'HARVEST_MINUTES'
if (millis() - stage_milliseconds > HARVEST_MINUTES60000){
                                    ^~~~~~~~~~~~~~~~~~~~
                                    HARVEST_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:804:18: error: 'OH_DROP_VOLUME1000' was not declared in this scope
oh_log += String(OH_DROP_VOLUME1000 / OH_FLOW, DEC);
                 ^~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:804:18: note: suggested alternative: 'OH_DROP_VOLUME'
oh_log += String(OH_DROP_VOLUME1000 / OH_FLOW, DEC);
                 ^~~~~~~~~~~~~~~~~~
                 OH_DROP_VOLUME
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:823:57: error: 'OH_DROP_VOLUME1000' was not declared in this scope
if (m4_OH_PUMP == 1 and millis() - oh_on_milliseconds > OH_DROP_VOLUME1000 / OH_FLOW ){
                                                        ^~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:823:57: note: suggested alternative: 'OH_DROP_VOLUME'
if (m4_OH_PUMP == 1 and millis() - oh_on_milliseconds > OH_DROP_VOLUME1000 / OH_FLOW ){
                                                        ^~~~~~~~~~~~~~~~~~
                                                        OH_DROP_VOLUME
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:832:16: error: 'OH_DROP_VOLUME2' was not declared in this scope
OH_DROP_VOLUME=OH_DROP_VOLUME2; //addaptive OH compensation (if default OH drop volume fails to rise pH after 10 drops, we double the drop volume)
               ^~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:832:16: note: suggested alternative: 'OH_DROP_VOLUME'
OH_DROP_VOLUME=OH_DROP_VOLUME2; //addaptive OH compensation (if default OH drop volume fails to rise pH after 10 drops, we double the drop volume)
               ^~~~~~~~~~~~~~~
               OH_DROP_VOLUME
Multiple libraries were found for "TimeLib.h"
Used: H:\Mes documents\Arduino\libraries\Time-master
Not used: H:\Mes documents\Arduino\libraries\Time
Multiple libraries were found for "SD.h"
Used: H:\Mes documents\Arduino\libraries\SD
Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\SD
Multiple libraries were found for "OneWire.h"
Used: H:\Mes documents\Arduino\libraries\OneWire
Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\A1_Duinoedu_OneWire
Multiple libraries were found for "Dhcp.h"
Used: H:\Mes documents\Arduino\libraries\Ethernet2
Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\Ethernet
Multiple libraries were found for "LiquidCrystal_I2C.h"
Used: H:\Mes documents\Arduino\libraries\LiquidCrystal_I2C
Not used: H:\Mes documents\Arduino\libraries\New-LiquidCrystal-master
Not used: H:\Mes documents\Arduino\libraries\Arduino-LiquidCrystal-I2C-library-master
Compilation error: Error: 2 UNKNOWN: exit status 1

Si tu charges le programme d'origine, que se passe-t-il?

j'ai tenté ca "[ ]" l'erreur a disparu mais j'ai toujours ca :

h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino: In function 'void loop()':
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:733:60: error: 'MIN_GROWING_MINUTES60000' was not declared in this scope
 if ((od_over_1 == 10) and (millis() - stage_milliseconds > MIN_GROWING_MINUTES60000)) {
                                                            ^~~~~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:733:60: note: suggested alternative: 'MIN_GROWING_MINUTES'
 if ((od_over_1 == 10) and (millis() - stage_milliseconds > MIN_GROWING_MINUTES60000)) {
                                                            ^~~~~~~~~~~~~~~~~~~~~~~~
                                                            MIN_GROWING_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:752:57: error: 'IPTG_PUMPING_SECONDS1000' was not declared in this scope
 if ((mo_IPTG == 1) and (millis() - stage_milliseconds > IPTG_PUMPING_SECONDS1000)){
                                                         ^~~~~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:752:57: note: suggested alternative: 'IPTG_PUMPING_SECONDS'
 if ((mo_IPTG == 1) and (millis() - stage_milliseconds > IPTG_PUMPING_SECONDS1000)){
                                                         ^~~~~~~~~~~~~~~~~~~~~~~~
                                                         IPTG_PUMPING_SECONDS
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:756:60: error: 'MIN_IPTG_MINUTES60000' was not declared in this scope
 if ((od_over_2 == 10) and (millis() - stage_milliseconds > MIN_IPTG_MINUTES60000)) {
                                                            ^~~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:756:60: note: suggested alternative: 'MIN_IPTG_MINUTES'
 if ((od_over_2 == 10) and (millis() - stage_milliseconds > MIN_IPTG_MINUTES60000)) {
                                                            ^~~~~~~~~~~~~~~~~~~~~
                                                            MIN_IPTG_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:776:37: error: 'HARVEST_MINUTES60000' was not declared in this scope
 if (millis() - stage_milliseconds > HARVEST_MINUTES60000){
                                     ^~~~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:776:37: note: suggested alternative: 'HARVEST_MINUTES'
 if (millis() - stage_milliseconds > HARVEST_MINUTES60000){
                                     ^~~~~~~~~~~~~~~~~~~~
                                     HARVEST_MINUTES
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:804:18: error: 'OH_DROP_VOLUME1000' was not declared in this scope
 oh_log += String(OH_DROP_VOLUME1000 / OH_FLOW, DEC);
                  ^~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:804:18: note: suggested alternative: 'OH_DROP_VOLUME'
 oh_log += String(OH_DROP_VOLUME1000 / OH_FLOW, DEC);
                  ^~~~~~~~~~~~~~~~~~
                  OH_DROP_VOLUME
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:823:57: error: 'OH_DROP_VOLUME1000' was not declared in this scope
 if (m4_OH_PUMP == 1 and millis() - oh_on_milliseconds > OH_DROP_VOLUME1000 / OH_FLOW ){
                                                         ^~~~~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:823:57: note: suggested alternative: 'OH_DROP_VOLUME'
 if (m4_OH_PUMP == 1 and millis() - oh_on_milliseconds > OH_DROP_VOLUME1000 / OH_FLOW ){
                                                         ^~~~~~~~~~~~~~~~~~
                                                         OH_DROP_VOLUME
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:832:16: error: 'OH_DROP_VOLUME2' was not declared in this scope
 OH_DROP_VOLUME=OH_DROP_VOLUME2; //addaptive OH compensation (if default OH drop volume fails to rise pH after 10 drops, we double the drop volume)
                ^~~~~~~~~~~~~~~
h:\Mes documents\Arduino\test bioR corr\test bioR corr.ino:832:16: note: suggested alternative: 'OH_DROP_VOLUME'
 OH_DROP_VOLUME=OH_DROP_VOLUME2; //addaptive OH compensation (if default OH drop volume fails to rise pH after 10 drops, we double the drop volume)
                ^~~~~~~~~~~~~~~
                OH_DROP_VOLUME
Multiple libraries were found for "LiquidCrystal_I2C.h"
 Used: H:\Mes documents\Arduino\libraries\LiquidCrystal_I2C
 Not used: H:\Mes documents\Arduino\libraries\New-LiquidCrystal-master
 Not used: H:\Mes documents\Arduino\libraries\Arduino-LiquidCrystal-I2C-library-master
Multiple libraries were found for "Dhcp.h"
 Used: H:\Mes documents\Arduino\libraries\Ethernet2
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\Ethernet
Multiple libraries were found for "TimeLib.h"
 Used: H:\Mes documents\Arduino\libraries\Time-master
 Not used: H:\Mes documents\Arduino\libraries\Time
Multiple libraries were found for "SD.h"
 Used: H:\Mes documents\Arduino\libraries\SD
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\SD
Multiple libraries were found for "OneWire.h"
 Used: H:\Mes documents\Arduino\libraries\OneWire
 Not used: L:\ARDUINO\ArduinoAug_0.70.16_EXP\libraries\A1_Duinoedu_OneWire
Compilation error: Error: 2 UNKNOWN: exit status 1

Tu as encore des erreurs alors que dans ton premier post, tu avais pu charger la version d'origine?

oui la version d'origine se charge tt a fait normal j ai meme eu un message sur le moniteur série pour le réglage heur RTC mais rien de plus

Parce que L’équipe arduino ne peut pas corriger le tir.

Il n’y a pas de magie, l’allocation/deallocation dynamique sur un petit microcontroller qui ne dispose pas de ramasse miette (pas moyen de regrouper des petits bouts de mémoire pour refaire un plus gros bloc) est à déconseiller si vous voulez être sûr de ce qu’il se passe au niveau de la mémoire, surtout si votre code a vocation à tourner longtemps sans planter.

La classe String a été développée comme vous le dites parce qu’elle simplifie la vie des développeurs et que la majorité des programmes sont des petits bouts de code qui ne vont pas fonctionner longtemps et puis s’ils plantent on reboot et tout repart comme avant..

Il y a moyen d’éviter une partie des problèmes liés à cette classe en respectant un certain nombres de règles comme utiliser reserve() pour les chaînes qui ont une vie longue, ne pas faire de la concaténation de plusieurs chaînes (génération de chaînes intermédiaires) en utilisant + ni appeler String() pour transformer quelque chose en chaîne, ou encore utiliser toujours le passage par référence…
Bref c’est compliqué de maîtriser tout ce qui peut mal se passer, donc c’est pour cela que les cStrings au final ne sont pas plus compliquées.

Cela dit pour un bout de code simple qui ne va pas tourner longtemps, c’est une solution de facilité

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"); 
          }
      }
    }
  //}
 }