Problème code pour LCD

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

  


j ai finis par faire passer le code d'avant en éliminant certains chiffre du genre "H_DROP_VOLUME2" par "H_DROP_VOLUME"
du coup c'est compilé sans erreur l'écran s'allume mais rien de plus pas d'écriture rien que l'éclairage du fond
je vais testé ton deuxième code

ok le deuxième code passe très bien sans erreur mais l'écran n'écrit rien juste l'éclairage du fond

Essaies encore cette version, qui devrait avoir les m^mes effets que la précédente


/*
 *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. 

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

  


Apparemment certains continuent à ignorer les recommandations des développeurs chevronnés et se cantonnent dans le bricolage approximatif.

Laissons faire. Après tout, ce ne sont que des projets sans importance ...

D'autant que ces concaténations en cascade sont complètement inutiles.
Il suffirait de faire des print au fur et à mesure.

Bonsoir hbachetti

MDR
Comme d'habitude, tout dans l'élégance!

Bonne soirée
Cordialement
jpbbricole

Je pense que vous n’avez pas saisi le sens de la remarque : un bioreacteur n’est justement pas un petit projet jetable qui tourne 5 minutes sur un coin de table… donc quite à récrire un bout du code autant se débarrasser des Strings.

effectivement le projet va tourner sans arrêt minimum 48 à72h voir plus !

Bonsoir J-M-L

Oui, j'ai parfaitement saisi le sens du message de @hbachetti....
Mais, je me pose la question, ces gens qui on écrit et publié ce programme ne l'ont donc jamais essayé sur une longue durée, ce serait étonnant!

Cordialement
jpbbricole

Si à un instant donné le constructeur de String() renvoie une String incomplète par manque de mémoire, il n'y aura aucun moyen de le savoir. S'il trouve un bloc mémoire de 15 octets alors que la demande est de 20 octets, String() renverra une chaîne de 15 octets, si c'est le plus gros bloc à disposition.

L'allocation dynamique, soit on comprend, soit on ne comprend pas.

Rien qu'à lire le code d'origine, on voit bien qu'il a été écrit par un noob.

Leur papier montre un log sur 20h, ils ne parlent pas d’analyse de robustesse.

Le challenge de l’allocation dynamique c’est que ça fonctionne jusqu’à ce que ca ne fonctionne plus. Je ne dis pas que leur code plantera, je dis que ce n’est pas simple à évaluer car ils utilisent justement abondamment l’opérateur + ou l’instanciation par String() qui sont les plus gros facteurs de morcellement (et un mega dispose de plus de ram qu’un UNO)

Cela dit la durée de vie est celle d’une fonction de mise à jour de l’écran ponctuelle et il semble qu’elle est peu imbriquée avec d’autres parties dynamiques,

Comme vous faites le boulot de réécriture, c’est celui qui fait qui a raison :wink:

mais le conseil qui est partagé serait de se passer de toutes ces strings et de faire juste des lcd.print() par petits bouts pour construire l’affichage surtout qu’il n’a pas l’air super compliqué

(Et idem pour les logs SD ou série)

Je ne pense pas avoir de leçons à recevoir de toi.

Dans ce post, tu n'a pas compris que la première remarque t'était adressée.

J'ai la détestable habitude de conserver certains posts croustillants, une sorte de bêtisier, avec les noms des auteurs.

On peut citer :
https://forum.arduino.cc/t/conseil-comment-faier-communiquer-12-arduinos-et-un-rasperry/555577/20
Où tu te permets de contester les explications de deux éminents membres de ce forum, et de qualifier leur propos de "lithanie technique".

Celui-ci est pas mal non plus :
https://forum.arduino.cc/t/sequence-sur-le-portd/557356/79

Il faut te rendre à l'évidence. Tu as des efforts à faire pour considérer que certains ici ont des connaissances énormes par rapport à d'autres.

Bonjour hbachetti

Alors, pourquoi tu l'écris et tu ne le pratiques pas.

Excellent et j'assume et le fait que tu gardes ça va tout à fait dans le sens de ton comportement dans ce forum. Tu rentres tout à fait dans cette catégorie de gens cités ici:

Tu vois, je garde aussi certains trucs.

Dan ton énumération tu as oublié la modestie.

Donc je persiste, @hbachetti Comme d'habitude, tout dans l'élégance!

Sur ce, je vais arrêter de perdre mon temps avec toi et surtout cesser de polluer cette discussion

Cordialement
jpbricole

Bien, après ce déversement de fiel, nous allons donc pouvoir revenir au sujet tranquillement.

Le problème serait simple s'il n'y avait pas ceci :

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

LCD_string est copié dans serial_string puis dans web_string pour envoi HTTP.
Donc, il faut bien fabriquer une string (mais une C string), avec les moyens habituels (sprintf, strcat, etc.).

Comme cherif27 est débutant, cela risque de ne pas être de la tarte.

Le début pourrait ressembler à ceci :

void refreshDisplay()  //update LCD display
{
  char LCD_string[100];    // cette taille est à ajuster en fonction du total (à estimer)

  int length = sprintf(LCD_string, "%d.%02d.%02d %02d:%02d:%02d\n", year(t), month(t), day(t), hour(t), minute(t), second(t));
  length += sprintf(LCD_string + length, "S%d", stage);
  switch (stage) {
    case 0:
      strcat(LCD_string, ":Ready  ");
      break;
    case 1:
      strcat(LCD_string, ":Growing");
      break;
    case 2:
      strcat(LCD_string, ":IPTG ON");
      break;
    case 3:
      strcat(LCD_string, ":Harvest");
      break;
    case 4:
      strcat(LCD_string, ":Done ! ");
      break;
  }
  length += 8;
  // etc, etc, etc.
}

Vu le nombre de String dans ce programme je me demande comment il a pu tourner plus de qq minutes.

Comme dit plus haut, tout dépend de l’imbrication des allocations et libérations. Si elles sont FILO alors on ne morcelle pas le tas, de même si globalement toutes les allocations sont effectuées dans une fonction à courte durée de vie sur des variables locales sans appel à d’autres fonctions imbriquées qui utilisent le tas de manière plus longue (ce qui semble le cas ici).

Sur un UNO ce serait peut être aussi plus challenging mais la MEGA a plus de SRAM

Le challenge bien sûr c’est qu’on ne sait pas si on est juste à la limite ou s’il y a une grosse marge.