Loss of internet access but not Ethernet Arduino mega with Ethernet shield

Hello, I made a project with a mega Arduino and an Ethernet shield.
This monitors a hive and sends alert emails and updates ThingSpeak and a personal server.
It also has an incorporated web page that allows you to download or delete files contained in the SD card of the Ethernet Shield.
My prototype works well for several days (from 15 to 30 days) then it loses the functions of updating Thingspeak, my server (on raspberry pi4) and sending email ,!
But from my network, I can access my web page via the Arduino's IP address, download the files from the SD card, on the spot the program continues to run normally (except internet access)!
How is it possible?
My box can see the Arduino well, I can access the Arduino's web server from a browser on my PC but when the problem occurs, the Arduino no longer accesses the internet ....
Does anyone have any idea where the bug might come from?
Thank you

Hello @mgnthe54, your topic has been moved to a more suitable location on the forum.

I would like to advise you to post your code using code tags; just in case you don't know about code tags, you can read How to get the best out of this forum.

The first thing I look for in a program that works for a while and then fails is dynamic memory allocation, including (especially) String objects. Are you using any?

Yes I use some String objects. I can put my code if you want.

You should indeed post your code.

the file fonction.ino

#include<Arduino.h>
#include "RTClib.h"
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>        		// utile uniquement si écran sans interface I2C
#include "SD.h"
#include <Ethernet.h>      			// bibliotheque Ethernet ou ethernet2 uniquement W5500...


extern boolean AlertePbCapteurs;    		// dans parametres_Fonctions_internet


String Mois;                                  	// création d'une chaine de caractère Mois   
String Moisavant;                             	// création d'une chaine de caractère Mois d'avant
float units;                                    // variable de stockage du poids
boolean EtatBouton = 1;                         // état dans lequel se trouve le bouton reset
boolean OKSD = 0;                             	// si on a déjà envoyé un email de pb SD on attendra le lendemain pour en renvoyer un autre
boolean OKTempHygro = 0;                      	// si on a déjà envoyé un email de pb capteur de température et hygrométrie on attendra le lendemain pour en renvoyer un autre
boolean OKHorloge = 0;                        	// si on a déjà envoyé un email de pb d'horloge on attendra le lendemain pour en renvoyer un autre
boolean OKTempInt = 0;                          // si on a déjà envoyé un email de température intérieure on attendra le lendemain pour en renvoyer un autre

File file;                                     // Création de l'objet fichier nommé file

const char* const PROGMEM Janv ="JANV";
const char* const PROGMEM Fev ="FEVR";
const char* const PROGMEM Mars ="MARS";
const char* const PROGMEM Avril ="AVR";
const char* const PROGMEM Mai ="MAI";
const char* const PROGMEM Juin ="JUIN";
const char* const PROGMEM Juil ="JUIL";
const char* const PROGMEM Aout ="AOUT";
const char* const PROGMEM Sept ="SEPT";
const char* const PROGMEM Octo ="OCT";
const char* const PROGMEM Nove ="NOVE";
const char* const PROGMEM Dece ="DEC";

const char* const PROGMEM LesMois[] ={Janv,Fev,Mars,Avril,Mai,Juin,Juil,Aout,Sept,Octo,Nove,Dece};


String selectionmois(DateTime now)
{
     
   switch (now.month()) {                                // fonction pour création d'un fichier d'enregistrement par mois 

  case 1:                                               // teste le mois si 1 c'est qu'on est en janvier
    Mois=LesMois[0];                                    // ici on affecte JANV à la chaine Mois
    break;                                              // sort du test si on était rentré dans le cas 1
  case 2:
    Mois=LesMois[1]; 
    break;
  case 3:
    Mois=LesMois[2]; 
    break;
  case 4:
    Mois=LesMois[3]; 
    break;
  case 5:
    Mois=LesMois[4]; 
    break;
  case 6:
    Mois=LesMois[5]; 
    break;
  case 7:
    Mois=LesMois[6]; ;
    break;
  case 8:
    Mois=LesMois[7]; 
    break;
  case 9:
    Mois=LesMois[8]; 
    break;
  case 10:
    Mois=LesMois[9]; 
    break;
  case 11:
    Mois=LesMois[10]; 
    break;
  default:                                            // si aucun des autres cas du coup on est à 12 donc en décembre...
    Mois=LesMois[11];                                 // on affecte DEC à la chaine mois 
   
   
  }
  return Mois;
}                                                      // fin de fonction sélection mois


String selectionmoisavant(DateTime now)
{
     
   switch (now.month()) {                               // fonction pour création d'un fichier d'enregistrement par mois
  case 1:                                               // teste le mois si 1 c'est qu'on est en janvier
    Moisavant=LesMois[11];                              // ici on affecte DEC à la chaine Moisavant
    break;                                              // sort du test si on était rentré dans le cas 1
  case 2:
    Moisavant=LesMois[0]; 
    break;
  case 3:
    Moisavant=LesMois[1]; 
    break;
  case 4:
    Moisavant=LesMois[2]; 
    break;
  case 5:
    Moisavant=LesMois[3]; 
    break;
  case 6:
    Moisavant=LesMois[4]; 
    break;
  case 7:
    Moisavant=LesMois[5]; ;
    break;
  case 8:
    Moisavant=LesMois[6]; 
    break;
  case 9:
    Moisavant=LesMois[7]; 
    break;
  case 10:
    Moisavant=LesMois[8]; 
    break;
  case 11:
    Moisavant=LesMois[9]; 
    break;
  default:                                            // si aucun des autres cas du coup on est à 12 donc en décembre...
    Moisavant=LesMois[10];                           // on affecte NOVE à la chaine Moisavant 
   
   
  }
  return Mois;
}                                                   // fin de fonction selection mois d'avant



void affichageserie(DateTime now, float t, float h, float units, int lum, float TempInt,boolean eau)
{
 
  Serial.print(now.day(), DEC);           // affiche sur le moniteur série le jour
  Serial.print('/');                      // affiche sur le moniteur série /
  Serial.print(now.month(), DEC);         // affiche sur le moniteur série le mois
  Serial.print('/');                      // affiche sur le moniteur série /
  Serial.print(now.year(), DEC);          // affiche sur le moniteur série l'année en 4 chiffres
  Serial.print(' ');                      // affiche sur le moniteur série un espace


  Serial.print(now.hour(), DEC);          // affiche sur le moniteur série l'heure
  Serial.print(':');                      // affiche sur le moniteur série :
  Serial.print(now.minute(), DEC);        // affiche sur le moniteur série les minutes
  Serial.print(':');                      // affiche sur le moniteur série :
  Serial.print(now.second(), DEC);        // affiche sur le moniteur série les secondes
  Serial.print(' ');                      // affiche sur le moniteur série un espace
  
  Serial.print(F("Humidité: "));           // Affiche sur le moniteur série Humidité :
  Serial.print(h,1);                      // affiche la valeur d'humidité avec un seul caractère derriere la virgule
  Serial.print(F(" %\t"));                // affiche le symbole % suivi d'une tabulation
  
  Serial.print(F("Temp Ext: "));          // affiche sur le moniteur série Température:
  Serial.print(t,1);                      // affiche la valeur de la température avec un seul chiffre derrière la virgule
  Serial.print(F(" °C\t"));               // affiche le texte °C suivi d'une tabulation
  
  Serial.print(F("Temp Int: "));         // affiche sur le moniteur série Température:
  Serial.print(TempInt,1);               // affiche la valeur de la température avec un seul chiffre derrière la virgule
  Serial.print(F(" °C\t"));              // affiche le texte °C suivi d'une tabulation
  
  Serial.print(F("Lum ext "));
  Serial.print(lum);                    // affiche la valeur de luminosité en %
  Serial.print(F(" %\t"));              // affiche le symbole % suivi d'une tabulation
  
  Serial.print(units,1);              	// affiche sur le moniteur série la valeur du poids sur la balance
  Serial.print(F(" Kg "));            	// affiche sur le moniteur série Kg
 
 /* EST-CE QU'IL PLEUT? */   
  if(eau == LOW)  Serial.println(F("Pluie"));   // affiche sur le moniteur série Pluie
  else Serial.println(F("Pas de Pluie"));       // affiche sur le moniteur série Pas de Pluie
  
}                                       // fin de fonction affichage moniteur série



void affichageecran(DateTime now, float t,float h,float units, int lum, float TempInt, boolean eau)
{

  monEcran.clear();                   				// on efface l'écran LCD
  if (Typeecran == 1 )monEcran.backlight();   // allumage le retro éclairage
  
  monEcran.print(now.day(), DEC);     				//affiche sur l'écran le jour en cours
  monEcran.print('/');                				//affiche sur l'écran le symbole /
  monEcran.print(now.month(), DEC);   				//affiche sur l'écran le mois en cours
  monEcran.print('/');                				//affiche sur l'écran le symbole / 
  monEcran.print(now.year()%100, DEC); 				//affiche sur l'écran l'année en cours sur 2 chiffres uniquement
  monEcran.print(F(" "));                			//affiche sur l'écran un espace

  monEcran.print(units,1);          				  // affiche sur l'écran le poids
  monEcran.print(F("Kg"));          				  //affiche sur l'écran Kg

  monEcran.setCursor(0,1);          				  // positionne le curseur de l'écran sur le début de la 2eme ligne

  monEcran.print(F("Ext: "));        				  // on affiche Ext:
  monEcran.print(t,1);               				  // on affiche la valeur de température avec un seul chiffre derriere la virgule sur l'écran
  monEcran.print((char)223);         				  // affiche sur l'écran le symbole °
  monEcran.print(F("C"));            				  // affiche sur l'écran la lettre C
  monEcran.print(F(" "));            				  // affiche sur l'écran un espace
  monEcran.print(h,0);               				  // on affiche la valeur d'hygromètrie sans chiffre derrière la virgule sur l'écran
  monEcran.print(F("%"));            				  //affiche sur l'écran le caractère %
  
  delay(6000);                       //ATTENTION SI VOUS MODIFIEZ CETTE VALEUR VOUS CHANGEZ AUSSI LE TEMPS D'EXECUTION DE LA BOUCLE DONC LE TEMPS DES MISES A JOUR
                                     // DE THINGSPEAK ET LE DELAI DE MISE A JOUR DU STOCKAGE DU POIDS DE LA RUCHE...POUR L'ESSAIMAGE
                                     // on maintient affiché 7,5 secondes ce premier écran MIEUX FAUT LAISSER LA SOMME DES 2 DELAY 15 SECONDES...
  
  if (PresenceCapteurPluie == 1  || PresenceCapteurLuminosite == 1  || PresenceCapteurTemperatureInterieure == 1)   // si aucun capteur optionnel on n'affiche pas de 2eme page
    {
     monEcran.clear();                   			// on efface l'écran LCD on passe au deuxième écran
     monEcran.print(F("Int."));          			// affiche sur l'écran Temp.Int.  
     monEcran.print(TempInt,1);          			// affiche sur l'écran la température Intérieure avec un seul chiffre derrière la virgule
     monEcran.print((char)223);          			// affiche sur l'écran le symbole °
     monEcran.print(F("C"));             			// affiche sur l'écran la lettre C
     monEcran.print(" ");                			// affiche sur l'écran un espace
     monEcran.print(units,0);            			// affiche sur l'écran le poids en kilo complet sans chiffre derrière la virgule
     monEcran.print(F("Kg"));            			// affiche sur l'écran Kg
 
     monEcran.setCursor(0,1);            			// positionne le curseur de l'écran sur le début de la 2eme ligne
  
     if (PresenceCapteurLuminosite == 1){
                                         monEcran.print(F("Lum."));		    // on affiche Lum.
                                         monEcran.print(lum);             // on affiche la valeur de luminosité
                                         monEcran.print(F("% "));         // affiche sur l'écran le caractère %
                                         }   					                    // fin de la présence ou pas du capteur de luminosité
                                      
     if (PresenceCapteurPluie == 1 && eau == 0) monEcran.print(F("Pluie"));          // Signale de la pluie! bon vous êtes sur place donc... mais cela permet de contrôler l'état du capteur de pluie
     if (PresenceCapteurPluie == 1 && eau == 1) monEcran.print(F("No Pluie"));       // no et pas "pas" car pas assez de place sur l'écran... désolé!
                                              // fin de la présence ou pas du capteur de pluie*/
    }                                         //fin du 3 if

if(PresenceCapteurPluie == 0 && PresenceCapteurLuminosite == 0){
                                                                 monEcran.clear();                   // on efface l'écran LCD on passe au deuxième écran
                                                                 monEcran.print(F("Poids : "));      // affiche sur l'écran la lettre C
                                                                 monEcran.print(units,2);            // affiche sur l'écran le poids en kilo 
                                                                 monEcran.print(F("Kg"));            // affiche sur l'écran Kg
 
                                                                 monEcran.setCursor(0,1);            // positionne le curseur de l'écran sur le début de la 2eme ligne
                                                                 monEcran.print(F("Temp. Int."));    // affiche sur l'écran Temp.Int.  
                                                                 monEcran.print(TempInt,1);          // affiche sur l'écran la température Intérieure avec un seul chiffre derrière la virgule
                                                                 monEcran.print((char)223);          // affiche sur l'écran le symbole °
                                                                 monEcran.print(F("C"));             // affiche sur l'écran la lettre C 
                                                                }// fin du if à 2 conditions et
    
  delay(6000);                        //ATTENTION SI VOUS MODIFIEZ CETTE VALEUR VOUS CHANGEZ AUSSI LE TEMPS D'EXECUTION DE LA BOUCLE DONC LE TEMPS DES MISES A JOUR
                                     // DE THINGSPEAK ET LE DELAI DE MISE A JOUR DU STOCKAGE DU POIDS DE LA RUCHE...POUR L'ESSAIMAGE
                                      //délai d'attente avant de refaire la boucle 6000 =6 secondes auquel s'ajoute les 6 secondes du 1er écran! 
                                      //MIEUX FAUT LAISSER LA SOMME DES 2 DELAY 15 SECONDES...
                                       
                                     

}                                             // fin de fonction affichage LCD




void err(int code, int x){
  
monEcran.clear();                      				// on efface l'écran LCD
if (Typeecran==1)monEcran.backlight();        // allumage retro éclairage si écran I2C
 

switch (code) {           					          // quel code erreur doit-on afficher?
    
  case 1 :{    
          Serial.println(F("Paramétrage avec une IP fixe..."));  // si une erreur a eu lieu cela signifie que l'attribution DHCP ne fonctionne pas. On initialise donc en forçant une IP
          if (Typeecran==1)monEcran.backlight();              	// allumage rétro éclairage si écran I2C
	  monEcran.print(Ethernet.localIP());                         // affiche l'adresse Ip sur le moniteur
          monEcran.setCursor(0,1);				                      // positionne le curseur en début de 2 eme ligne
          monEcran.print (F("IP Fixe..."));			                // affiche le texte IP Fixe...
          Serial.println(Ethernet.localIP());                 	// affiche sur le moniteur série l'adresse IP, pas d'envoi d'email puisque IP fixe donc surement pas d'internet
          break;						                                    // sortie du switch
          }                                                     //fin du code erreur 1


  case 2 :{
         Serial.println(F("Problème SD"));      							// affichage du texte Problème SD... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
         if (Typeecran==1)monEcran.backlight();           		// allumage rétro éclairage si écran I2C  
	 monEcran.print(F("Probleme SD"));     								      // affiche sur l'écran le message Problème SD
         if (AlertePbCapteurs == 1 && OKSD == 0) envoimail(6,MonAdresseIP2,nbessai);			// envoi l'email journalier sur le dysfonctionnement
         if (retourmail == 0) OKSD=1;									        // si l'envoie d'email c'est bien passé on positionne pour n'envoyer qu'une fois par jour
         break;                                								// on sort!
          }                                                   // fin du code erreur 2

   case 3 : {
              Serial.println(F("Impossible de lire le capteur de Température et Hygrométrie!"));   	// signale sur le moniteur série que l'on arrive pas à lire le capteur de T° & Hygro
	      if (Typeecran==1)monEcran.backlight();           						// allumage rétro éclairage si écran I2C
              monEcran.print(F("Probleme capteur"));            		// affiche sur l'écran le message
              monEcran.setCursor(0,1);									            // positionne le curseur en début de 2 eme ligne
              monEcran.print (F("Temp + Hygro"));							       // affiche sur l'écran Temp + Hygro
              if (AlertePbCapteurs==1 && OKTempHygro==0) envoimail(7,MonAdresseIP2,nbessai);		// envoi l'email journalier sur le dysfonctionnement
              if (retourmail==0)OKTempHygro=1;								      // si l'envoie d'email c'est bien passé on positionne pour n'envoyer qu'une fois par jour
              break;											                          // on sort!
            }                                                       // fin du code erreur 3

    case 4 : {
              Serial.println(F("Ecriture SD désactivée"));   				// signale sur le moniteur série que l'on arrive pas à lire le capteur de T° & Hygro
	      if (Typeecran==1)monEcran.backlight();           						// allumage rétro éclairage si écran I2C              
	      monEcran.print(F("Ecriture SD"));               						// affiche sur l'écran le message
              monEcran.setCursor(0,1);                        			// passe à la ligne suivante
              monEcran.print (F("DESACTIVEE"));               			// affiche sur l'écran le message DESACTIVEE, pas d'envoi d'email c'est un choix utilisateur
              break;					      					                    	// on sort!
            }                                                       // fin du code erreur 4

   
    case 5 :{
            Serial.println(F("L'horloge ne fonctionne PAS!"));     			// affichage du texte l'horloge ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						// allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme Horloge"));           						// affiche sur l'écran le message
            if (AlertePbCapteurs==1 && OKHorloge==0) envoimail(8,MonAdresseIP2,nbessai);		// envoi l'email journalier sur le dysfonctionnement
            if (retourmail==0)OKHorloge=1;								              // si l'envoie d'email c'est bien passé on positionne pour n'envoyer qu'une fois par jour
            break;                                               				// on sort!
            }                                                          // fin du code erreur 5  

     case 6 :{
            Serial.print(F("Probléme de mise à jour de la chaine. code erreur HTTP "));			// on affiche sur le moniteur série Probléme de mise à jour de la chaine. code erreur HTTP
            Serial.println(String(x));									// suivi du code d'erreur de Thingpeak
            if (Typeecran == 1)monEcran.backlight();           					// allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme mise a"));           						// affiche sur l'écran le message
            monEcran.setCursor(0,1);                        						// passe à la ligne suivante
            monEcran.print (F("jour ThingSpeak"));          						// affiche sur l'écran le message
            break;                                               				// on sort!
            }                                                           // fin du code erreur 6   

      case 7 :{
            Serial.println(F("Le capteur de température intérieur a un problème"));     		// affichage du texte Le capteur ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						// allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme capteur"));          						// affiche sur l'écran le message
            monEcran.setCursor(0,1);                        						// passe à la ligne suivante
            monEcran.print (F("Temperature Int."));          						// affiche sur l'écran le message Temp....
            if (AlertePbCapteurs==1 && OKTempInt==0) envoimail(9,MonAdresseIP2,nbessai);		// envoi l'email journalier sur le dysfonctionnement
            if (retourmail==0)OKTempInt=1;								              // si l'envoie d'email c'est bien passé on positionne pour n'envoyer qu'une fois par jour
            break;                                               				// on sort!
            }                                                           // fin du code erreur 7  

       case 8 :{
            Serial.println(F("Problème d'envoi d'email d'adresse IP!")); // affichage du texte Problème ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						// allumage retro éclairage si écran I2C
            monEcran.print(F("Probleme envoi"));           						  // affiche sur l'écran le message Probleme envoi
            monEcran.setCursor(0,1);                        						// passe à la ligne suivante
            monEcran.print (F("mail adresse IP"));          						// affiche sur l'écran le message mail adresse IP
            break;                                               				// on sort!
            }                                                           // fin du code erreur 8  
            
        case 9 :{
            Serial.println(F("Problème d'envoi d'email Mensuel"));    	// affichage du texte Problème ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						// allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme envoi"));           					  	// affiche sur l'écran le message Probleme envoi
            monEcran.setCursor(0,1);                        						// passe à la ligne suivante
            monEcran.print (F("mail Mensuel"));          						    // affiche sur l'écran le message mail Mensuel
            break;                                               				// on sort!
            }                                                           // fin du code erreur 9   
       case 10 :{
            Serial.println(F("Problème d'envoi d'email de vol de ruche"));     // affichage du texte problème ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						      // allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme envoi"));           						        //affiche sur l'écran le message Probleme envoi
            monEcran.setCursor(0,1);                        						      // passe à la ligne suivante
            monEcran.print (F("mail de vol"));          						          // affiche sur l'écran le message mail de vol
            break;                                               					    // on sort!
            }                                                                 // fin du code erreur 10
            
      case 11 :{
            Serial.println(F("Problème d'envoi d'email d'essaimage"));     	// affichage du texte Probleme ... sur le moniteur série, PEUT-ETRE MASQUé UNE FOIS LA MISE AU POINT FINIE
            if (Typeecran==1)monEcran.backlight();           						    // allumage rétro éclairage si écran I2C
            monEcran.print(F("Probleme envoi"));           						      // affiche sur l'écran le message Probleme envoi
            monEcran.setCursor(0,1);                        						    // passe à la ligne suivante
            monEcran.print (F("mail essaimage"));          						      //affiche sur l'écran le message mail essaimage
            break;                                               					 // on sort!
            }                                                               // fin du code erreur 11 

      case 12 :{
            Serial.println(F("Tarage de la balance"));     						//Affiche sur le moniteur série comme quoi on tare la balance!
            if (Typeecran==1)monEcran.backlight();           					// allumage rétro éclairage si écran I2C
            monEcran.print(F("Tarage"));          							      // affiche sur l'écran le message Tarage
            monEcran.setCursor(0,1);                        					// passe à la ligne suivante
            monEcran.print (F("balance a vide"));          						//affiche sur l'écran le message balance a vide
            break;                                               			// on sort!
                 }                                                    // fin du code erreur 12    

      case 13 :{
            Serial.println(F("Inhibition afficheur LCD"));     				// Affiche sur le moniteur série comme quoi on a inhibé l'afficheur (accès en cours à distance au serveur Arduino=
            if (Typeecran==1)monEcran.backlight();           				  // allumage retro éclairage si écran I2C
            monEcran.print(F("Acces a distance"));           					// affiche sur l'écran le message Acces a distance
            monEcran.setCursor(0,1);                        					// passe à la ligne suivante
            monEcran.print (F("en cours"));          							    //affiche sur l'écran le message en cours
            return;                                               		// on sort sans la tempo  et le clear (le message reste affiché sur l'écran
                 }                                                    // fin du code erreur 13
                }                                                    // fin du switch

      delay(10000);                          					             	 // on affiche les messages durant 10 secondes
      monEcran.clear();                      						            // on efface l'écran LCD
      
                  }                                                 //fin de la fonction d'affichage des erreurs






void ecritureSD(DateTime now, float t,float h,String Mois, int lum, float TempInt, boolean eau)
{

    if (SD.begin(pinCS_SD)) {    					                  // si la Sd démarre bien 
    String Typefichier=".csv";                         			// type de fichier que l'on souhaite sur SD
    String NomFichier=Mois+now.year()+Typefichier;    			// création du nom du fichier mois+ année +type
 
    
    file = SD.open( NomFichier, FILE_WRITE);         			// ouverture ou création si non existant du fichier
    if (file) {                                     			// si le fichier s'ouvre bien on fait ce qui suit

      file.print(now.day(), DEC);                   			// stocke le jour dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(now.month(), DEC);                 			// stocke le mois dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(now.year(), DEC);                  			// stocke l'année dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(now.hour(), DEC);                  			// stocke l'heure dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(now.minute(), DEC);                			// stocke les minutes dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(now.second(), DEC);                			// stocke les secondes dans le fichier
      file.print(',');                              			// stocke une virgule de séparation
      file.print(h,1);                              			// stocke la valeur d'hygrométrie avec 1 chiffre derriere la virgule
      file.print(",");                              			// stocke une virgule de séparation
      file.print(t,1);                              			// stocke la température avec 1 chiffre derriere la virgule
      file.print(",");                              			// stocke une virgule de séparation
      file.print(units);                            			// stocke le poids avec 1 chiffre derriere la virgule
      file.print(",");                              			// stocke une virgule de séparation
      file.print(lum);                              			// stocke la luminosité
      file.print(",");                              			// stocke une virgule de séparation
      file.print(eau);                              			// stocke la valeur de pluie 0 si pluie 1 sinon
      file.print(",");                              			// stocke une virgule de séparation
      file.print(TempInt);                          			// stocke la Température intérieure de la ruche
      file.println();                               			// enregistrement à la ligne suivante
      file.close();                                 			// ferme le fichier 
              } 							                            //fin du if file
    else err(2,0);							                          // si problème on envoie à la fonction de gestion d'erreur
  			}   						                                  //fin du SD begin
  else err(2,0);                                   			  // si problème on envoie à la fonction de gestion d'erreur

}                                                         		 // fin de fonction écriture sur SD




void GestionBouton() {    						    // fonction de reset de la tare

EtatBouton = digitalRead(bouton);					//EtatBouton est égale à la lecture du bouton

if (EtatBouton == 0) {        						// c'est que l'on vient d'appuyer sur le bouton (inversé avec la résistance de PULLUP)
err(12,0);								                // affichage du message sur écran LCD comme quoi on tare la balance à zéro
balance.tare();              						  // Partant du principe que la balance est vide lors de l'appui sur ce bouton, remise à zéro de la balance
remise_zero = 1 ;           						  // remise en place de la tare sur coupure............car sinon la balance indiquera moins de la valeur de correction...
delay(50);           } 						    	  // fin de l'appui sur bouton reset poids

                             
                        }     						// fin gestion appui bouton reset tare
                        

The file of internet function.ino

#include<Arduino.h>
#include <Ethernet.h>      				 // bibliotheque Ethernet ou ethernet2 uniquement W5500...
#include "SD.h"
#include <EMailSender.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>        			 // utile uniquement si écran sans interface I2C
#include "RTClib.h"
#include "ThingSpeak.h"
#include"parametres_Fonctions_internet.h"
#include <EasyNTPClient.h>
#include <Timezone.h>

extern void err(int code, int x);                        // fonction située dans l'onglet Fonctions.cpp                 
extern float h;						 // fonction située dans l'onglet principal
extern float t;						 // fonction située dans l'onglet principal
extern float units;					 // fonction située dans l'onglet principal
extern float TempInt;					 // fonction située dans l'onglet principal
extern boolean eau;						 // fonction située dans l'onglet principal
extern int lum;						 // fonction située dans l'onglet principal
extern String rafraichir;     				 // chaine contenant la ligne de commande de rafraichissement de la page web


EthernetServer server(80);                      	// attribution du port 80 au serveur web arduino
File root;                                     	   	// création de l'objet fichier nommé root (racine)
IPAddress MonAdresseIP;                                	// l'adresse IP en format chaine de caractère
float resultat;                                    	// stocke le resultat du calcul de perte de poids sur essaimage
String AdresseIP(); 					// convertion adresse IP en chaine de caractères
int retourmail = 0;                           		// contient l'information de retour d'envoi du mail
boolean desarmement = 0;                        	// désarmement de l'envoi du mail d'essaimage une fois réussi
String readString;                              	// chaine de lecture des données de la page web
int Choix1 = 0;                               		// sélection lecture ou effacement du fichier sur la SD
boolean drapeau = 0;                          		// indicateur de si on a déjà fait son choix de télechargement ou suppression


/* Pour mise à jour horloge*/

EthernetUDP Udp_G; 					// Objet UDP permettant d'envoyer et recevoir des trames ethernet selon le protocole UDP
EasyNTPClient ClientNtp_G(Udp_G, "pool.ntp.org"); 	// Objet NTP synchronisé avec le site "pool.ntp.org"

/*Abbréviation: Chaîne de maximum 5 caractères permettant d’identifier de façon unique cette règle. Dans ce programme, vous choisirez les majuscules des noms de structures.
Semaine: Semaine du mois pour laquelle la règle s’applique. Pour les 2 changements d’heure en Europe, il s’agit des 2 dernières semaines du mois. Vous utiliserez donc le mot clef “Last“.
Jour de la semaine:  Jour de la semaine pour lequel la règle s’applique. Pour les 2 changements d’heure en Europe, il s’agit du dimanche. Vous utiliserez donc le mot clef “Sun“.
Heure: Heure de déclenchement de la règle. Le passage à l’heure d’été se déclenche à 2 heures et le passage à l’heure d’hiver à 3 heures.
Décalage: Décalage du fuseau horaire en nombre de minute par rapport à l’heure UTC (Temps universel coordonné). Ce décalage est de 2 heures l’été et 1 heure l’hiver.
*/

TimeChangeRule RegleHeureEteFrance_G = {"RHEE", Last, Sun, Mar, 2, 120}; 		// Règle de passage à l'heure d'été pour la France
TimeChangeRule RegleHeureHiverFrance_G = {"RHHE", Last, Sun, Oct, 3, 60}; 		// Règle de passage à l'heure d'hiver la France
Timezone ConvertirHeureFrance_G(RegleHeureEteFrance_G, RegleHeureHiverFrance_G); 	// Objet de conversion d'heure avec les caractéristique de la métropole française

char Buffer_L[30]; 

/* variables servant au serveur personnel*/
char tX[5];         					 // tableau de caractère pour conversion de la température extérieure
char unitsX[5];     					 // tableau de caractère pour conversion du poids
char lumX[5] ;      					 // tableau de caractère pour conversion de la luminosité
char tinX[5];       					 // tableau de caractère pour conversion de la température intérieure
char hygX[5];       					 // tableau de caractère pour conversion de l'hygrométrie

String tX1;         					// chaine de caractère pour la conversion de la température extérieure
String unitsX1;     					// chaine de caractère pour la conversion du poids
String lumX1;       					// chaine de caractère pour la conversion de la tluminosité
String eauX1;       					// chaine de caractère pour la conversion de la pluie
String tinX1;       					// chaine de caractère pour la conversion de la température intérieure
String hygX1;       					// chaine de caractère pou la conversion de l'hygrométrie

String tX2;         					// chaine de caractère pour la conversion de la température extérieure sans caractère vide
String unitsX2;     					// chaine de caractère pour la conversion du poids sans caractère vide
String lumX2;       					// chaine de caractère pour la conversion de la luminosité sans caractère vide
String tinX2;      					// chaine de caractère pour la conversion de la température intérieure sans caractère vide
String hygX2;       					// chaine de caractère pour la conversion de l'hygrométrie sans caractère vide

/* Données pour mon serveur perso!*/
int    HTTP_PORT   = 80;				// port 80 pour le serveur personnel
String HTTP_METHOD = "GET";

char   HOST_NAME[] = "77.777.777";			// adresse IP du serveur
String PATH_NAME   = "/ruche/import.php";		// chemin d'accès du serveur
String queryString;					// chaine texte contenant les informations à envoyer au serveur personnel

//String PATH_NAME   = "/ruches/import"+numRuche+".php";





 /* on paramètre la messagerie de l'envoyeur et le serveur !*/
EMailSender emailSend(email_login,email_password,email_from,name_from,smtp_server,SMTP_PORT);  // application des infos contenues dans Parametres_internet.h
 


/* fonction de convertion de l'adresse IP en chaine de caractères */

String AdresseIP(){       				
  char MonAdresseIP2[26] ;
  MonAdresseIP = Ethernet.localIP();
  sprintf(MonAdresseIP2, "%d.%d.%d.%d", MonAdresseIP[0], MonAdresseIP[1], MonAdresseIP[2], MonAdresseIP[3]);
  return (MonAdresseIP2);
}     							//  fin de la conversion adresse IP en chaine de caractères




/* Fonction de listing des fichiers sur la page web */

void ListFiles(EthernetClient client, uint8_t flags, File dir) {    
  client.println("<ul>");
  while (true) {
    File entry = dir.openNextFile();
   
    /* voici ce que l'on fait s'il n'y a plus d'entrée dans le répertoire*/
     if (! entry) {
       // plus de fichiers
       break;     //on sort du while
                   }
 
    // imprimer tous les espaces de retrait

    client.print("<li><a href=\"");
    client.print(entry.name());
    if (entry.isDirectory()) {
       client.println("/");
                             }
    client.print("\">");
    
    // imprimer le nom du fichier avec un remplissage des vides possible
    client.print(entry.name());
    if (entry.isDirectory()) {
       client.println("/");
                             }
        
    client.print("</a>");
 
    client.println("</li>");
    entry.close();
  }   // fin du while
  client.println("</ul>");
                                                          }   // fin de la fonction liste les fichiers
 



/* Fonction de création du listing des fichiers présent sur la SD */

void printDirectory(File dir, int numTabs) {      
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       // Plus de fichier
       break;
     }
     for (uint8_t i = 0; i < numTabs; i++) {
       Serial.print('\t');
     }
     Serial.print(entry.name());
     if (entry.isDirectory()) {
       Serial.println(F("/"));
       printDirectory(entry, numTabs+1);
     } else {
       // les fichiers ont des tailles, les répertoires n'en n'ont pas
       Serial.print(F("\t\t"));
       Serial.println(entry.size(), DEC);
     }
     entry.close();
   }
}         						 	//fin de printDirectory




/* Fonction accès à distance fichiers SD sur Arduino (serveur web) */

void GestionFichier(int Choix){          			// affichage page web

switch (Choix){
  case 1 :   Choix1 = 1;      					// on veut supprimer un fichier
            break;
            
  default : Choix1 = 2;       					// on veut télécharger un fichier   
}
  
char clientline[BUFSIZ];
char name[17];
int index = 0;

  
  EthernetClient client = server.available();
  

  if (client) {
  //  Serial.println(F("il y a un client"));
   

    root = SD.open("/");
    printDirectory(root, 0);      				// va à la fonction de création du listing des fichiers présent sur la SD
    
    // initialisation avec une extrémité de la requête HTTP avec une ligne vide
    boolean current_line_is_blank = true;
    
    // réinitialisation du tampon d'entrée
    index = 0;
    
    while (client.connected()) {
   //   Serial.println(F("il y a un client connecté"));
      if (client.available()) {
        

        char c = client.read();
        
        // S'il ne s'agit pas d'une nouvelle ligne, ajoutez le caractère au tampon
        if (c != '\n' && c != '\r') {                     	// obtenir un \ n ou \ r nouvelle ligne, signifie que la chaîne est terminée
          clientline[index] = c;
          index++;
          // Sommes-nous trop gros pour le tampon? Si c'est la cas on commence à jeter des données
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // on continue à lire plus de données!
          continue;
        }
        
        // obtenir un \ n ou \ r nouvelle ligne, signifie que la chaîne est terminée
        clientline[index] = 0;
 
        
        // Affichage pour le debugging
        Serial.println(clientline);
        
        // Recherchez une sous-chaîne telle qu'une demande pour obtenir le fichier
        if (strstr(clientline, "GET /") != 0) 
        {
          // cette fois pas d'espace après le /, donc un sous-fichier!
          char *filename;
          
          filename = clientline + 5; 				// un petit truc,regardez après le "GET /" (5 caractères) ******* 
          							// recherchez la chaîne "HTTP / 1.1" et transformez le premier caractère de la sous-chaîne en un 0 pour l'effacer.
          (strstr(clientline, " HTTP"))[0] = 0;
 
          if(filename[strlen(filename)-1] == '/') {  		// Découper le nom de fichier de répertoire
            filename[strlen(filename)-1] = 0;        		// car l'ouverture génère une erreur si on laisse à la fin le caractère /
                                                  } 		// fin du if filename
          
          Serial.print(F("Requête Web pour : ")); 
          Serial.println(filename);  				// Affichage du nom du fichier demandé
          

 
   
         File file = SD.open(filename, O_READ);
         
          if ( file == 0 ) {  					// L'ouverture du fichier avec le code retour de 0 est une erreur dans SDFile.open
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>Fichier non trouv&#233;!</h2>");
            client.println("<br><h3>Impossible d'ouvrir le fichier!</h3>");
            drapeau = 0;    					// réinitialise la variable pour la prochaine fois
            compteursauvegarde = 0;				// réinitialise la variable pour la prochaine fois
            affichage = 0;    					// on réactive l'écran LCD et les tempos..
            break; 
                           }  					// fin de l'erreur d'ouverture fichier
          
          Serial.println(F("Fichier ouvert!"));
                    
          client.println("HTTP/1.1 200 OK");
          if (file.isDirectory()) {
            Serial.println(F("C'est un répertoire"));		// peut-être masqué
        
            client.println("Content-Type: text/html");
            client.println();
            client.print("<h2>Fichiers contenu dans la SD: /");
            client.print(filename); 
            client.println(":</h2>");
            ListFiles(client,LS_SIZE,file);  
            file.close();                   
                                 } 				// fin du if repertoire
                                 
          else {                    				// Tout non-répertoire cliqué, le serveur enverra le fichier au client pour téléchargement

            /*fonction d'effacement de fichier*/

              if (Choix1==1){SD.remove(filename);  
                            Serial.print(F("on va effacer "));  // peut-être masqué
                            Serial.println(filename); 		// peut-être masqué
                            drapeau = 0; 				// réinitialise la variable pour la prochaine fois
                            affichage = 0;			// réinitialise la variable pour la prochaine fois
                            compteursauvegarde = 0;		// réinitialise la variable pour la prochaine fois
                            break;}  				// fin de la fonction d'effacement de fichier
              
              /*fonction de téléchargement du fichier : */

            client.println("Content-Type: application/octet-stream");       //Si changement texte entre guillemets on affiche dans le navigateur les données sans les sauvegarder dans un fichier...
            client.println();
          
            char file_buffer[16];
            int avail;
            while (avail = file.available()) {
              int to_read = min(avail, 16);
              if (to_read != file.read(file_buffer, to_read)) {  break; }
              
              
              client.write(file_buffer, to_read);
                                           }  				// fin du while
            file.close();						// ferme le fichier
              drapeau = 0;    						// réinitialise la variable pour la prochaine fois
              compteursauvegarde = 0;					// réinitialise la variable pour la prochaine fois
              affichage = 0;    					// on réactive l'écran LCD et les tempos..
         
               
               }    							//fin du else envoie fichier au client pour téléchargement

        } 								// fin du if fin de chaine strstr

        else {
          // N'importe quelle erreur est une erreur 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>Fichier non trouv&#233;!</h2>");
          
          drapeau = 0;    						// réinitialise la variable pour la prochaine fois
          compteursauvegarde = 0;					// réinitialise la variable pour la prochaine fois
          affichage = 0;    						// on réactive l'écran LCD et les tempos..
            }   // fin du else = erreur
        break;
                                 } 					// fin du if client disponible
                                 } 					// fin du if client connecté
    
    delay(200);								// donne au navigateur Web le temps de recevoir les données 
    client.stop();							// arrête le client
   }                        						// fin du if client


}                                					// fin de la fonction du serveur web Arduino





void pagewebselection(){
  
Choix1=0;   								//réinitialise la variable pour permettre un nouveau choix...

 EthernetClient client = server.available();				// test si il y a un clent Ethernet en attente
  if (client) {								// si il y a quelqu'un au bour de la ligne...
       


    while (client.connected()) { 					// tant que le client est là
  
      if (client.available()) {						// si le client est disponible
        char c = client.read();						// on lit la demande du client


     
        //lit le caractére de la requette HTTP
        if (readString.length() < 100) {
          //Stocke les caractères de la chaine
          readString += c;
          Serial.print(c);
                                       }
                                       


         if (c == '\n') {          					//si la requête HTTP est terminée
     
           client.println("HTTP/1.1 200 OK"); 				//envoie une nouvelle page
           client.println("Content-Type: text/html");
           client.println();     
           client.println("<HTML>");
           client.println("<HEAD>");
           client.println("<meta name='apple-mobile-web-app-capable' content='yes' />");
           client.println("<meta name='apple-mobile-web-app-status-bar-style' content='black-translucent' />");
           client.println("<link rel='stylesheet' type='text/css 'href='https://randomnerdtutorials.com/ethernetcss.css' />");
           client.println(F("<TITLE>Page de sélection Suppression ou Téléchargement fichiers depuis la SD!</TITLE>"));
           client.println("</HEAD>");
           client.println("<BODY>");
           client.println(F("<H1>Page de s&#233;lection de Suppression ou de T&#233;l&#233;chargement des fichiers depuis la SD!</H1>"));
           client.println("<hr />"); 
           client.println(F("<H2>Attention &#224; votre choix, vous n'avez pas le droit &#224; l'erreur en cas du choix Suppression</H2>"));
           client.println("<br />");  
           client.println("<a href=\"/?button1Suppr\"\">Acc&#233;s &#224; la page de Suppression</a>");
           client.println("<a href=\"/?button1Tele\"\">Acc&#233;s &#224; la page de T&#233;l&#233;chargement</a><br />");   
           client.println("<br />");     
           client.print(F("<p>Vous allez acc&#233;der aux fichiers de la ruche n&#186;  ")); 
           client.print("<font size =+6>");       			//agrandit la taille du texte suivant
           client.print("<font color=#FF0000>");    			// change la couleur en rouge
           client.print("<b>");                     			// passe en gras
           client.println(numRuche);      				// affiche le numéro de la ruche
           client.println("<br />");  					// saut de ligne
           client.println("<br />"); 
           client.println("</font>");
           client.println("</b>");
           client.println("</font color>");
           client.println(F("<p>Une fois votre choix fait, patientez quelques secondes avant l'affichage de la liste des fichiers  "));
           client.print("<font color=#FF0000>");    			// change la couleur en rouge
           client.print("<font size =+6>");       			//agrandit la taille du texte suivant
           client.println("<br />");                			// saut de ligne
           client.println (F("<p>Si vous cliquez sur un des 2 boutons, il faudra aller jusqu'au bout car l'affichage LCD sera d&#233;sactiv&#233; et les tempos aussi. Ne quittez pas la page suivante sauvagement! Si vous devez changer d'avis c'est maintenant!"));
           client.println("</font color>");
           client.println(" </p>");
           client.println("<br />");  					// saut de ligne
           client.println("<br />"); 
           client.println("</BODY>");
           client.println("</HTML>");
     
           delay(2);
           
          
           
           if (readString.indexOf("?button1Suppr") >0){			//contrôle si vous avez pressé un des boutons
               Serial.println(F("on vient d'appuyer sur Suppression"));
           
               client.println("Content-Type: text/html");      		//permet la mise à jour de la page web
               client.println();     
               client.println("<HTML>");
               client.println("<HEAD>");
               client.println(rafraichir);                 	      // pour passer à la suivante!(bidouille moi...)
               client.println("</HEAD>");
               
               affichage = 1;
               drapeau = 1;
               Choix1 = 1;                            } 	     // fin d'action sur bouton suppression

                                                       
           if (readString.indexOf("?button1Tele") >0){
               Serial.println(F("on vient d'appuyer sur téléchargement"));

               client.println("Content-Type: text/html");   	    //permet la mise à jour de la page web
               client.println();     
               client.println("<HTML>");
               client.println("<HEAD>");
               client.println(rafraichir);                  	   // pour passer à la suivante!
               client.println("</HEAD>");
               
               affichage =1 ;
               drapeau = 1;
               Choix1 = 2;
                                                      } 	  // fin d'action sur bouton téléchargement
                
           client.stop();      				 	 // arrêt du client                                 

            
           readString="";  					 // effacement de la chaine pour une prochaine lecture

   
    
    delay(200);							// donne au navigateur Web le temps de recevoir les données

           
                           }  					// fin du if la requête est terminée

                           

              
                                }    				// fin du if client disponible
       }   							// fin du temps que le client est connecté
     }      							// fin du if client

                        }   					// fin de la fonction pagewebselection






/* Fonction d'envoi des emails */
 
int envoimail(int lequel,String NomFichierMensuel,int nbessai){   //nomfichier mensuel contient soit l'adresse IP soit le nom du fichier mensuel à envoyer selon les besoins...
 

boolean piecejointe;						// variable pour savoir si on a une pièce jointe à envoyer avec le mail
String msgsujet;						// chaine de caractère contenant les textes des emails selon les cas
String msgsujet1;						// chaine de caractère contenant les textes des emails selon les cas
String msgsujet2;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps1;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps2;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps3;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps4;						// chaine de caractère contenant les textes des emails selon les cas
String msgcorps5;						// chaine de caractère contenant les textes des emails selon les cas

  switch (lequel){										// test pour savoir quel email envoyer

  case 1 :{    											// envoi mail vol
    piecejointe = 0;
    msgsujet = F("ATTENTION VOL d'une ruche en cours!");
    msgcorps1 = F("Le capteur de poids vient de tomber à moins de 5kg, <br>soit la balance a un problème, soit quelqu'un embarque votre ruche n° ");
    msgcorps2 = F("<br>Soit vous faites une visite approfondie et vous testez la fonction...<br>Pour mémoire mon adresse IP est :");
    msgcorps3 = F("<br>Le poids mesuré est de :");
    msgcorps4 = F("Kg"); 
    msgcorps= msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel + msgcorps3  + units + msgcorps4;
 
    break;}											// on sort du test


  case 2 :{    											// envoi mail mensuel
    piecejointe = 1;
    msgsujet = F("Pas de panique c'est le relevé mensuel!");
    msgcorps1 = F("Veuillez trouver ci-joint le relevé du mois de ");
    msgcorps2 = F(" de la ruche n° ");
    msgcorps = msgcorps1 + NomFichierMensuel + msgcorps2 + numRuche;
    break;}											// on sort du test


  case 3 :{   											// envoi mail essaimage primaire...
    piecejointe = 0;
    msgsujet = F("ATTENTION essaimage possible...");
    msgcorps1 = F("Ici la Ruche N° ");
    msgcorps2 = F(" <br> Détection d'un possible essaimage, <br> soit vous intervenez sur la ruche soit il y a essaimage, soit à voir... <br> La perte de poids est de : ");
    msgcorps3 = F("Kg <br>Voici les valeurs mémorisées : <br>");
    msgcorps4 = F("Kg<br>");
    msgcorps5 = F("<br>Pour mémoire mon adresse IP est :");
    msgcorps = msgcorps1 + numRuche + msgcorps2 + resultat + msgcorps3 + tableaumemoirepoids[0]+ msgcorps4 +tableaumemoirepoids[1]+ msgcorps4 + tableaumemoirepoids[2]+ msgcorps4 +tableaumemoirepoids[3]+ msgcorps4 +tableaumemoirepoids[4]+ msgcorps4 + msgcorps5 + NomFichierMensuel;
    break;}											// on sort du test

    
  case 4 :{   											// envoi adresse IP...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(" pour te donner mon adresse IP.");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Je viens de démarrer ou de rebooter mon adresse IP est :");
    msgcorps = msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel;  				// ici je me sers de nomfichiermensuel pour transmettre l'adresse IP
    break;}											// on sort du test

    
  case 5 :{   											// envoi mise à jour de l'heure...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(".Je viens d'ajuster l'heure de l'horloge");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Je viens de mettre à jour l'heure avec ces valeurs : ");
    msgcorps =  msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel;  				// ici je me sers de nomfichiermensuel pour transmettre l'heure
    break;}											// on sort du test

  case 6 :{   											// envoi message erreur carte SD...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(".Il y a un problème de carte SD");
    msgsujet =  msgsujet1+ numRuche +msgsujet2 ;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Mon adresse IP est : ");
    msgcorps3 = F(".<br>Il faudrait vérifier car il y a un problème de carte SD!");
    msgcorps = msgcorps1 + numRuche +msgcorps2 + NomFichierMensuel+ msgcorps3;  		// ici je me sers de nomfichiermensuel pour transmettre l'adresse IP
    break;}											// on sort du test
    
    case 7 :{   										// envoi message pb capteur température et hygrométrie extérieur...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(".Il y a un problème de capteur Température et hygrométrie");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Mon adresse IP est : ");
    msgcorps3 = F(".<br>Il faudrait vérifier mais j'ai un problème de capteur Température et hygrométrie!");
    msgcorps =  msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel+ msgcorps3;  		// ici je me sers de nomfichiermensuel pour transmettre l'adresse IP
    break;}											// on sort du test

    case 8 :{   										// envoi message pb d'horloge...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(". Il y a un problème d'horloge");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Mon adresse IP est : ");
    msgcorps3 = F(".<br>Un problème d'horloge signifie de nombreuses fonctions Hors Service!<br> Tu risques d'avoir des soucis sur beaucoup d'autres fonctions, une visite urgente s'impose!");
    msgcorps = msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel+ msgcorps3;  		// ici je me sers de nomfichiermensuel pour transmettre l'adresse IP
    break;}											// on sort du test
    
    case 9 :{   										// envoi message pb de capteur de température intérieure...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(".Il y a un problème de capteur de température intérieure");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Mon adresse IP est : ");
    msgcorps3 = F(".<br>Il y a un problème de capteur de température intérieure, il géle dans la ruche!<br> Le capteur indique une température < à 2°c.");
    msgcorps = msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel + msgcorps3;  		// ici je me sers de nomfichiermensuel pour transmettre l'adresse IP
    break;}											// on sort du test
    
    case 10 :{   										// envoi message pb de capteur de température intérieure...
    piecejointe = 0;
    msgsujet1 = F("Salut, c'est la Ruche N° ");
    msgsujet2 = F(".Il y a peut-être un essaimage secondaire");
    msgsujet = msgsujet1 + numRuche + msgsujet2;
    msgcorps1 = F("Bonjour ici la Ruche N° ");
    msgcorps2 = F(".<br>Mon adresse IP est : ");
    msgcorps3 = F(".<br>La perte de poids est de : ");
    msgcorps4 = F("Kg <br> "); 
    msgcorps5 = F("Kg <br>Voici les valeurs mémorisées :  <br>");
    msgcorps = msgcorps1 + numRuche + msgcorps2 + NomFichierMensuel + msgcorps3 + resultat + msgcorps5 + tableaumemoirepoids[0]+ msgcorps4 
    + tableaumemoirepoids[1] + msgcorps4 + tableaumemoirepoids[2]+ msgcorps4 + tableaumemoirepoids[3]+ msgcorps4 + tableaumemoirepoids[4] + msgcorps4;
    
    break;}											// on sort du test
              }     										// fin du switch


/* début de l'envoi du mail */

    EMailSender::EMailMessage message;    // structure défine dans EmailSender.h

    message.subject = msgsujet;
    message.message = msgcorps;

   Serial.println(message.subject);      // pour infos peut-être masqué
   Serial.println(message.message);      // pour infos peut-être masqué


 
  
   if (piecejointe!=0){ EMailSender::FileDescriptior fileDescriptor[piecejointe];   // début envoi mail avec piece jointe structure défine dans EmailSender.h

   
    fileDescriptor[0].filename = NomFichierMensuel;
    fileDescriptor[0].url = NomFichierMensuel;
    fileDescriptor[0].mime = "text/csv";
    fileDescriptor[0].encode64 = false;
    fileDescriptor[0].storageType = EMailSender::EMAIL_STORAGE_TYPE_SD;

    EMailSender::Attachments attachs = {piecejointe, fileDescriptor};   	 // structure défine dans EmailSender.h
                        
    EMailSender::Response resp = emailSend.send(arrayOfEmail, nb_Destinaire_Mail, message, attachs);	// on envoi l'email

    Serial.println(F("Statut de l'envoi avec piece jointe : "));		// pour infos peut-être masqué

    Serial.println(resp.status);						// pour infos peut-être masqué
    Serial.print(F("resp Code: "));    						// pour infos peut-être masqué
    Serial.println(resp.code);							// pour infos peut-être masqué
    Serial.print(F("Description : "));						// pour infos peut-être masqué
    Serial.println(resp.desc);							// pour infos peut-être masqué

    	if (resp.code == "0")   {						// l'envoi du mail c'est bien passé
		//	Serial.println(F("resp.code fichier mensuel = 0!")); 	// pour infos peut-être masqué
			retourmail = 0;						// positionne retourmail à 0
			return (retourmail); 					// retourne la valeur de retourmail 0
		       		}                        			// fin de c'est bon le mail avec pièce jointe

    	else {									// on a un problème d'envoi d'email
		//	Serial.println(F("resp.code envoi de fichier différent de 0"));		// pour infos peut-être masqué
			retourmail = 1; 					// positionne retourmail à 1
			return (retourmail);  					// retourne la valeur de retourmail 1
	     }      								// fin du else (fin du on a un problème d'envoi mail)

                           }       						// fin du if il y a une piece jointe 
                           
                           
   else {     									// début envoi mail sans pièce jointe!

   EMailSender::Response resp = emailSend.send(arrayOfEmail, nb_Destinaire_Mail, message);	// on envoi l'email

   nbessai=nbessai-1;                      					// on vient de faire un esai de plus on décrémente

   	if (resp.code == "0")   {						// on vient de faire un essai d'envoi de mail
			  desarmement = 1;					// positionne desarmement à 1
			  retourmail = 0; 					// positionne retourmail à 0
			  return (retourmail); 					// retourne la valeur de retourmail 0
				} 						// c'est bon on sort en retournat la valeur 0
   


  while(nbessai > 0){ 								// tant qu'on a choisit de faire un essai on recommence


  	if (resp.code	!= "0"){                                               //début d'un autre essai d'envoi des mails

    				delay (30000);        			       // on attends 30 secondes avant de reessayer pendant ce temps on ne fait rien....

				Serial.println(F("On vient d'attendre 30 secondes")); // peut-être masqué
 
			        EMailSender::Response resp = emailSend.send(arrayOfEmail, nb_Destinaire_Mail, message);	// on envoi l'email
 
    			        nbessai=nbessai-1;    					// on vient de faire un essai de plus...
    

    
   		if (resp.code == "0") { 						// on a a réussit à envoyer l'email
					desarmement = 1; 				// positionne desarmement à 1
					retourmail = 0;					// positionne retourmail à 0
					return (retourmail); 				// retourne la valeur de retourmail 0
					}       					// fin du if on a bien envoyé le mail
                 
   		if (nbessai <= 0){							// si on a atteint le nombre d'essai maxi on arrête
				  retourmail = 1; 					// positionne retourmail à 1
				  return (retourmail);  				// retourne la valeur de retourmail 1, c'est un echec
				 }                  					// fin du nombre d'essai d'envoi d'email sans succés...
                          
                 }  									// fin d'un autre essai
                }									// fin du while     
          }        									// fin de l'envoi de mail sans pièce jointe
    
                                      			} 				//fin fonction envoi mail

/* Fonction de gestion de ThingSpeak */
                                      
void thingspeak(float t, float h, float units, int lum, int TempInt){
  
   /* fixe les champs de Thingspeak avec les données */
  ThingSpeak.setField(1, t);								// assigne la valeur du champ 1 à la température exterieure
  ThingSpeak.setField(2, h);								// assigne la valeur du champ 2 à l'hygrométrie exterieure
  ThingSpeak.setField(3, units);							// assigne la valeur du champ 3 au poids
  ThingSpeak.setField(4, lum);								// assigne la valeur du champ 4 à la luminosité exterieure
  ThingSpeak.setField(5,TempInt);							// assigne la valeur du champ 5 à la température intérieure
  ThingSpeak.setStatus("Mise à jour réussie : ");					// assigne le champ Status avec le message Mise à jour réussie : 

 
    /* écriture des champs dans ThingSpeak */
  int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);			// on écrit réellement les champs préremplie ci-dessus
 
 
  if(x == 200){										// si cela c'est bien passé
    Serial.println(F("Mise à jours de ThingSpeak réussi."));				// peut-être masqué
    
              } 									// fin du pas d'erreur de mise à jour ThingSpeak
  else err(6,x);									// si erreur on va sur la fonction de gestion des erreurs
 
                                                   		   }        		// fin de la fonction thingspeak



/* fonction de conversion en texte des données à envoyer au seveur personnel */

 void conversion(float h,float t,float units,int lum,int TempInt,boolean eau){     		// Fonction de conversion en chaine de caractère des chiffres de données
  
 dtostrf(h,2,0,hygX);                                 					// conversion float en char de l'hygrométrie 2 chiffres sans virgule
 hygX1=hygX;                                          				        // conversion char en string
 hygX2="";                                             				        // réinitialisation de la chaine pour la prochaine utilisation
   
 for(int i=0; i < hygX1.length(); i++) {                			        // suppression des éventuels espaces si pas assez de chiffres

 if(hygX1[i] !=' ') hygX2 += hygX1[i];                  			        // recherche espace et suppression dans la nouvelle chaine. a+=y signifie a=a+y
                                        } 

                                        
 dtostrf(t,3,1,tX);                                     				// conversion float en char de la température exterieure 3 chiffres dont 1 derrière la virgule
 tX1=tX;                                               					// conversion char en string
 tX2="";                                              					// réinitialisation de la chaine pour la prochaine utilisation

 for(int i=0; i < tX1.length(); i++) {                					//  suppression des éventuels espaces si pas assez de chiffres

 if(tX1[i] !=' ') tX2 += tX1[i];                    					// recherche espace et suppression dans la nouvelle chaine. tX2+=tX1[i] signifie tX2 =tX2 + tX1[i]
                                      }

                                        
 dtostrf(units,4,1,unitsX);                           					// conversion float en char du poids 4 chiffres dont 1 derrière la virgule
 unitsX1=unitsX;                                     					// conversion char en string
 unitsX2="";                                         					// réinitialisation de la chaine pour la prochaine utilisation
 
 for(int i=0; i < unitsX1.length(); i++) {           					// suppression des eventuels espaces si pas assez de chiffres

 if(unitsX1[i] !=' ') unitsX2 += unitsX1[i];        					// recherche espace et suppression dans la nouvelle chaine. unitsX2 +=unitsX1[i] signifie unitsX2= unitsX2+ unitsX1[i]
   
                                        }

                                         
 dtostrf(lum,2,0,lumX);                              					// conversion int en char de la luminosité 2 caractères sans virgule
 lumX1=lumX;                                         					// conversion char en string
 lumX2="";                                           					// réinitialisation de la chaine pour la prochaine utilisation

for(int i=0; i < lumX1.length(); i++) {              					// suppression des éventuels espaces si pas assez de chiffres

if(lumX1[i] !=' ') lumX2 += lumX1[i];                					// recherche espace et suppression dans la nouvelle chaine. Lumx2= LumX2+LumX[i]
                                      }


 dtostrf(TempInt,3,1,tinX);                          					// conversion int en char de la température intérieure 3 chiffres dont 1 derière la virgule
 tinX1=tinX;                                         					// conversion char en string
 tinX2="";                                           					// réinitialisation de la chaine pour la prochaine utilisation
 
 for(int i=0; i < tinX1.length(); i++) {             					// suppression des eventuels espaces si pas assez de chiffres
  
 if(tinX1[i] !=' ') tinX2 += tinX1[i];                					// recherche espace et suppression dans la nouvelle chaine. tinX2= tinX2+ tinX1[i]
                                        }


if (eau == 0) eauX1 = "0";								// on positionnne la chaine eauX1 à 0
if (eau == 1) eauX1 = "1";								// on positionnne la chaine eauX1 à 1

 queryString = "?hyg=" + hygX2 + "&tex=" + tX2 + "&tin="+ tinX2 + "&mas=" + unitsX2 + "&lum=" + lumX2 + "&eau=" + eauX1;           // création de la chaine à envoyer au serveur personnel           
                                                                                                                        
}        										 // fin de la fonction de conversion


/* fonction pour envoie sur le serveur personnel */

void envoiserveur(){        
   if(client.connect(HOST_NAME, HTTP_PORT)) {

    /* si connecté: */
                            Serial.println(F("Connecté au serveur"));        // peut-être masqué

    // Fait une requête HTTP :

    // Envoie une entête HTTP

    client.println(HTTP_METHOD + " " + PATH_NAME + queryString + " HTTP/1.1");
    client.println("Host: " + String(HOST_NAME));
    client.println("Connection: close");
    client.println();                                     		   // fin de l'entête HTTP 

    while(client.connected()) {						   // tant que le client est connecté 
     	 if(client.available())  {					   // si le client est disponible

        // lit un byte entrant depuis le serveur et l'imprime sur le moniteur série:
        			char c = client.read();
        			Serial.print(c);
      				 }					  // fin du if client dispo
    			      }     					  // fin du while client connecté

    /* Si le serveur est déconnecté, stop le client: */

    client.stop();					// arrêt client
    Serial.println();        				// saute une ligne sur le moniteur série
    Serial.println(F("déconnecté"));			// inscrit déconnecté sur le moniteur série
  						}      // fin du client connecté


  else {            					//  Si pas connecté:
    	Serial.println(F("Echec connexion"));        	// sur le moniteur série
       }    						// fin du pas de client connecté

       			}          					// fin de la fonction envoie sur serveur personnel



 void MiseAjourHeure(DateTime now){   			//fonction de mise à jour de l'horloge

  /* Gestion mise à l'heure au boot */

  if(BootMiseAJourHeure == 1) {				// on a besoin de faire la mise à jour de l'heure

       			    time_t HeureLocale_L;   	// crée l'objet temps HeureLocale_L

        		    HeureLocale_L = ConvertirHeureFrance_G.toLocal(ClientNtp_G.getUnixTime());	// fait appel à la fonction pour obtenir l'heure locale et la positionner dans l'objet time
        
    
  		           char AbreviationMois_L[12][4]={"Jan","Fev","Mar","Avr","Mai","Jun","Jul","Aou","Sep","Oct","Nov","Dec"};
  			   char AbreviationJour_L[7][4]={"Dim","Lun","Mar","Mer","Jeu","Ven","Sam"};
    
    	/* met dans la chaine Buffer_L les date et heures */
    			  sprintf(Buffer_L, " %.2d:%.2d:%.2d %s %.2d %s %d", hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L), AbreviationJour_L[weekday(HeureLocale_L)-1], day(HeureLocale_L), AbreviationMois_L[month(HeureLocale_L)-1], year(HeureLocale_L)); 


 if ( Ethernet. hardwareStatus ( ) == EthernetW5100 ) {					// si on a un shield Ethernet W5100 on fait ce qui suit :
  if (year(HeureLocale_L)== now.year()) {						// si l'année communiquée par l'HeureLocale correspond à l'année actuelle cela doit être bon pour la liaison Ethernet
                                                Serial.println(F("on a un Shield W5100 mais il semble connecté à internet donc OK"));
                                                rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L)));	// on régle l'heure
                                                envoimail(5,Buffer_L,nbessai);		// appel à la fonction d'envoi mail
                                                BootMiseAJourHeure =0;			// on a bien fait la mise à jour 
                                                }  					// fin du if now.year
   else Serial.println(F("Shield W5100 mais pas de connexion internet!"));                                                
                                                     }					// fin du W5100

    // comme on ne peut pas tester la connexion avec un W5100 on espère que la connexion internet est bonne....
    
  else {    										// du coup c'est mieux c'est un W5200 ou W5500

  if(Ethernet. linkStatus ( ) == LinkON) {   						// uniquement valable avec shield W5200 et W5500...
  rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L))); 		// on régle l'heure
  envoimail(5,Buffer_L,nbessai);							// appel à la fonction d'envoi mail
  BootMiseAJourHeure =0;								// position la variable à 0 puisqu'on a fait la mise à jour de l'heure au boot
  Serial.println(F("on vient de réajuster l'horloge!"));
                                        }   						// fin du if linkstatus OK
                                        
  else Serial.println(F("Pb Internet! L'heure n'a pas été changée")); 
      }     										// fin du c'est un W5200 ou W5500
     
                            } 								// fin de la fonction boot mise à jour


                            
/* Gestion heure d'été */

   if (ChangementHeure == 0 && now.month() == 3 && now.dayOfTheWeek() == 0 && now.day() > 24 && now.hour() == 2 && now.minute() >= 1){    
    //mois = 3(mars) & jour = 0(dimanche) & on est après le 24 du mois & heure = 2 (heures du matin pour passage été) & il laisse une minute pour si jamais l'horloge interne a un peu dérivé que le serveur soit à jour
      
      

        time_t HeureLocale_L;   							// crée l'objet temps HeureLocale_L

        HeureLocale_L = ConvertirHeureFrance_G.toLocal(ClientNtp_G.getUnixTime());	// fait appel à la fonction pour obtenir l'heure locale et la positionner dans l'objet time
        
    
  	char AbreviationMois_L[12][4]={"Jan","Fev","Mar","Avr","Mai","Jun","Jul","Aou","Sep","Oct","Nov","Dec"};
    	char AbreviationJour_L[7][4]={"Dim","Lun","Mar","Mer","Jeu","Ven","Sam"};
    
    /* met dans la chaine Buffer_L les date et heures */
    	sprintf(Buffer_L, "    %.2d:%.2d:%.2d %s %.2d %s %d", hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L), AbreviationJour_L[weekday(HeureLocale_L)-1], day(HeureLocale_L), AbreviationMois_L[month(HeureLocale_L)-1], year(HeureLocale_L)); 

 if ( Ethernet. hardwareStatus ( ) == EthernetW5100 ) {					// si on a un shield Ethernet W5100 on fait ce qui suit :
  if (year(HeureLocale_L)== now.year()) {
                                         Serial.println(F("on a un Shield W5100 mais il semble connecté à internet donc OK"));
                                         rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L)));		// ajuste l'heure
                                         ChangementHeure = 1;     			 //on indique que l'on a déjà fait la mise à l'heure! Du coup on ne le fera qu'une seule fois tous les 6 mois...
                                         envoimail(5,Buffer_L,nbessai);			// appel à la fonction d'envoi mail
                                                }  					// fin du if now.year

   else Serial.println(F("Shield W5100 mais pas de connexion internet!"));             	// peut-être masqué                                   
                                                     }					// fin du W5100

    // comme on peut pas tester la connexion avec un W5100 on espère que la connexion internet est bonne....
    
  else {    										// du coup c'est bon c'est un W5200 ou W5500

  if(Ethernet. linkStatus ( ) == LinkON) {   						// uniquement valable avec shield W5200 et W5500...
  rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L))); 	// ajuste l'heure
  ChangementHeure=1;      								// on indique que l'on a déjà fait la mise à l'heure! Du coup on ne le fera qu'une seule fois tous les 6 mois...
  envoimail(5,Buffer_L,nbessai);							// appel à la fonction d'envoi mail
  Serial.println(F("on vient de réajuster l'horloge!"));				// peut-être masqué
                                        }   						// fin du if linkstatus
                                        
  else Serial.println(F("Pb Internet! L'heure n'a pas été changée")); 			// peut-être masqué

    }     										// fin du else c'est un W5200 ou W 5500 en été
                  														}   	//fin du if changement été
                  

/*Gestion heure d'hiver */
if (ChangementHeure == 0 && now.month() == 10 && now.dayOfTheWeek() == 0 && now.day() > 24 && now.hour() == 3 && now.minute() >= 1){    
    //mois = 10(octobre) & jour = 0(dimanche) & on est après le 24 du mois & heure = 3 (heures du matin pour passage hiver) & il laisse une minute pour si jamais l'horloge interne a un peu dérivé que le serveur soit à jour
      
      ChangementHeure=1;      								//on indique que l'on a déjà fait la mise à l'heure! Du coup on ne le fera qu'une seule fois tous les 6 mois...

        time_t HeureLocale_L;   							// crée l'objet temps HeureLocale_L

        HeureLocale_L = ConvertirHeureFrance_G.toLocal(ClientNtp_G.getUnixTime());	// fait appel à la fonction pour obtenir l'heure locale et la positionner dans l'objet time
        
    char AbreviationMois_L[12][4]={"Jan","Fev","Mar","Avr","Mai","Jun","Jul","Aou","Sep","Oct","Nov","Dec"};
    char AbreviationJour_L[7][4]={"Dim","Lun","Mar","Mer","Jeu","Ven","Sam"};
    
    
    sprintf(Buffer_L, "    %.2d:%.2d:%.2d %s %.2d %s %d", hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L), AbreviationJour_L[weekday(HeureLocale_L)-1], day(HeureLocale_L), AbreviationMois_L[month(HeureLocale_L)-1], year(HeureLocale_L)); 


 if ( Ethernet. hardwareStatus ( ) == EthernetW5100 ) {					// si on a un shield Ethernet W5100 on fait ce qui suit :
  if (year(HeureLocale_L)== now.year()) {
                                                Serial.println(F("on a un Shield W5100 mais il semble connecté à internet donc OK "));
                                                rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L)));
                                                ChangementHeure=1;      		//on indique que l'on a déjà fait la mise à l'heure! Du coup on ne le fera qu'une seule fois tous les 6 mois...
                                                envoimail(5,Buffer_L,nbessai); 		// appel à la fonction d'envoi mail
                                                }  					// fin du if now.year
   else Serial.println(F("Shield W5100 mais pas de connexion internet!"));              // peut-être masqué    
                             
                                                     }					// fin du W5100
    // comme on ne peut pas tester la connexion avec un W5100 on espère que la connexion internet est bonne....
    
  else {    										// du coup c'est bon c'est un W5200 ou W5500

  if(Ethernet. linkStatus ( ) == LinkON) {   						// uniquement valable avec shield W5200 et W5500...
  rtc.adjust(DateTime(year(HeureLocale_L), month(HeureLocale_L), day(HeureLocale_L), hour(HeureLocale_L), minute(HeureLocale_L), second(HeureLocale_L))); 	// ajuste l'heure
  ChangementHeure=1;      								// on indique que l'on a déjà fait la mise à l'heure! Du coup on ne le fera qu'une seule fois tous les 6 mois...
  Serial.println(F("on vient de réajuster l'horloge!"));				// peut-être masqué
  envoimail(5,Buffer_L,nbessai);							// appel à la fonction d'envoi mail
                                        }   						// fin du if linkstatus
                                        
  else Serial.println(F("Pb Internet! L'heure n'a pas été changée")); 			// peut-être masqué  
     	}     										// fin du else c'est un shield W5200 ou w5500 dans l'heure d'hiver 
                  															}   	//fin du if changement hiver

          
                      } // fin de la fonction mise à jour de l'heure

the file of internet-parametre.h


  /*REGLAGES PARAMETRES DE TOUT CE QUI TOUCHE à INTERNET, THINGPEAK, IP, MAILS,SERVEUR...*/ 
    
  
   /*CODE SECRET DE VOTRE CLE API ET NUMERO DE CANAL THINGSPEAK (voir lien pour infos sur serveur Thingspeak)
   * https://www.wiki.lesfabriquesduponant.net/images/8/81/Tuto7-Tutoriel-Thingspeak.pdf
   */

int things = 1;        				 // Si vous avez un compte ThingSpeak (et que vous voulez le mettre à jour) positionnez à 1 sinon 0 = pas de mise à jour ThingSpeak...


unsigned long myChannelNumber= 1234567;     	 // remplacez 1234567 avec votre numéro de Channel ID
const char * myWriteAPIKey= "3AA1I9SXZL69NFNL";  // remplacez 3AA1I9SXZL69NFNL avec votre clé API WRITE

int MaJ = 13;      				//si MAJ = 0 mise à jour toutes les 15 secondes environ (maxi avec version gratuite) mais impossible si plusieurs Arduino sur le même channel
                				// si MAJ = 5 => 1 mn, si 9 = 2 mn, si 13 = 3 mn entre mise à jour du site ThingSpeak


/* NUMERO DE VOTRE RUCHE  */
                
String numRuche = "1";        			// pensez aussi à changer votre adresse MAC ci-dessous si vous avez plusieurs ruches sur le même réseau! 

  

  /* INFORMATIONS INTERNET : ADRESSE IP FIXE SI BESOIN, ADRESSE MAC (PARAMETRES A CHANGER A CHAQUE ARDUINO
     Entrez une adresse MAC addresse pour votre carte Ethernet.
     LES NOUVEAUX SHIELD ETHERNET ONT UNE ADRESSE MAC INSCRITE SUR EUX.
     ATTENTION SI VOUS AVEZ PLUSIEURS SHIELD ETHERNET IL FAUT CHANGER L'ADRESSE MAC SINON VOTRE BOX NE VA PAS AIMER....
    NE CHANGER PAS LES 3 PREMIERES SéRIES DE  CHIFFRES : 0x90, 0xA2,0xDA ne changent jamais!
    EXEMPLES : #define SECRET_MAC {0x90, 0xA2, 0xDA, 0x10, 0x40, 0x4F}
               #define SECRET_MAC {0x90, 0xA2, 0xDA, 0x0E, 0xA5, 0x7E}
               #define SECRET_MAC {0x90, 0xA2, 0xDA, 0x0D, 0x81, 0xB2}
  */

#define SECRET_MAC {0x90,0xA2,0xDA,0x10,0x40,0x4F}    			 // ici votre adresse MAC ...

byte mac[]=SECRET_MAC;							// variable pour votre adresse AMC

IPAddress ip(192,168,1,143);  						// L'adresse IP par défaut que prendra le shield si la box n'attribue pas une IP




#define DEFAULT_EMAIL_NETWORK_TYPE_ARDUINO   NETWORK_W5100		// pour la fonction d'envoi d'eamil fonctionne comme cela avec W5100, W5200, W5500



String expediteur = "Ruche "+ numRuche;					// création de la chaine de caractère expéditeur




// émission moi depuis la poste  attention selon votre messagerie cela fonctionne plus ou moins bien par exemple depuis orange cela ne marche pas...

uint16_t SMTP_PORT = 587;
const char* smtp_server = "smtp.laposte.net";				// votre serveur smtp d'adresse mail
const char* email_login = "votreadresse@laposte.net";			// votre adresse email expéditeur
const char* email_from = "votreadresse@laposte.net";			// votre adresse email
const char* name_from =expediteur.c_str();   			        //transformation chaine string en const char*
const char* email_password = "Password";				// votre mot de passe messagerie






/* Definition des destinataires des mails*/
const char* arrayOfEmail[] = {"premier destinataire@gmail.com", "deuxiemedestinataire@laposte.net","troisiemedestinataire@wanadoo.fr","quatriemedestinataire@orange.fr" };  // adresse mail de vos destinataires
byte nb_Destinaire_Mail=4;       															     // nombre de destinataire des mails (compter les adresses mails de la ligne ci-dessus!    
                                 
  
/* definition du nombre de tentative d'envoi email et de quels mails on envoie ou pas */
int nbessai=2;                    					// nombre d'essai d'envoi des emails en cas d'échec...
boolean EnvoiMailMensuel = 1;     					// si à 1 on envoie les mails de relevés mensuels
boolean EnvoiMailVol = 1;         					// si à 1 on envoie le mail de vol de la ruche (si cela se produit en espérant que non...) idem si vous enlevez la ruche de la balance (visite de printemps ou autre)
boolean EnvoiMailIP = 1;            					// si à 1 on envoie l'email de l'Ip de la ruche au démarrage de l'Arduino
boolean EnvoiMailEssaimage = 1;   					// si à 1 on envoie le mail de détection d'essaimage 
                                  					// de plus lors de visite pour récolte ou autres le fait d'enlever au moins pertepoids kg
                                  					// déclenchera l'envoi de mail mais bon cela fera une vérification du bon fonctionnement...
                                  
/* VARIABLE ESSAIMAGE : QUEL PERTE DE POIDS LORS DE L'ESSAIMAGE ET EN COMBIEN DE TEMPS...*/

int pertepoids = 2;               // valeur en Kg que l'on a choisi pour estimer l'essaimage de la ruche.... ici 2 kg  (0.1 gr par abeille * 20 000 abeilles = 2 kg...) essaimage primaire constaté en réel 2,4 kg

int TempsPertePoids = 7;          // valeur de temps que vous estimez pour que la moitié de la colonie sorte ...
                                  // si TempsPertePoids=0 on stocke le poids dans le tableau toutes les 15 secondes(c'est plutôt rapide non?) du coup on regarde la perte de poids sur 15 * 5 = 1 mn 15s
                                  // si TempsPertePoids=1 on stocke le poids dans le tableau toutes les 30 secondes(c'est plutôt rapide non?) du coup on regarde la perte de poids sur 30 * 5 = 2mn 30s
                                  // si TempsPertePoids=2 on stocke le poids dans le tableau toutes les 45 secondes du coup on regarde la perte de poids sur 45 * 5 = 3 mn 45s
                                  // si TempsPertePoids=3 on stocke le poids dans le tableau toutes les minutes du coup on regarde la perte de poids sur 1 mn * 5 = 5 mn 
                                  // si TempsPertePoids=4 on stocke le poids dans le tableau toutes les 1 minutes 15 secondes du coup on regarde la perte de poids sur 75s * 5 = 6 mn 15s
                                  // si TempsPertePoids=5 on stocke le poids dans le tableau toutes les 1 minutes et 30 secondes du coup on regarde la perte de poids sur 90s * 5 = 7 mn 25s
                                  // si TempsPertePoids=6 on stocke le poids dans le tableau toutes les 1 minutes et 45 secondes  du coup on regarde la perte de poids sur 105s * 5 = 8 mn 45s  mo essaimage a été vu avec cette constante!
                                  // si TempsPertePoids=7 on stocke le poids dans le tableau toutes les 2 minutes du coup on regarde la perte de poids sur 120s * 5 = 10 mn
                                  //..
                                  // si TempsPertePoids = 11 on stocke le poids dans le tableau toutes les 3 minutes du coup on regarde la perte de poids sur 3 mn * 5 = 15 mn 

int ControlePrimaire = 2;		 // Choix entre 1 et 5 permet la sélection du nombre de points avec la perte de poids ( 1 =  un seul point (de perte de poids) et on envoie l'email, 3 il faut 3 points (de perte de poids) de suite avant l'envoi de l'email)	



/* tentative de gestion des essaimages secondaires... petit poids de l'ordre du kilo alors que la sortie des abeilles pour butiner le matin est d'environ 0.8kg...
du coup cette fonction pourrait vous envoyer pas mal de mail faux... d'où son bornage avec les mois, l'heure et la température exterieure....
valeurs issus de retour d'expérience... on prendra la même mémorisation (TempsPertePoids) que pour l'essaimage principal 
IL faut donc avoir activé l'alerte essaimage normal pour pouvoir faire fonctionner l'essaimage secondaire...
*/

boolean EssaimageSecondaire = 1;   		// si à 1 on envoi les mails si 0 pas d'action!
int DebutMois = 5;     				//mettre ici le numéro de mois de début de cette surveillance (j'ai mis le mois de mai car si avant cela sera surement un gros essaimage donc pris en compte par la 1er fonction de surveillance de l'essaimage
int FinMois =  8;      				// mettre ici le numéro du dernier mois sur lequel activé cette surveillance accrue... donc avec 5 et 8 on surveillera du 1er mai au 31 aout
int DebutHeure = 10;    			// heure de début de la surveillance ne pas mettre trop tôt sinon on détectera le départ vers la récolte...
int Finheure = 18;   				// heure de fin de la surveillance sachant que personnellement mes essaimages ont eu lieu entre 14 et 16h...
int TemperatureMiniEssaimage = 16;  		// oui j'ai eu un essaimage secondaire avec seulement 17°c et avec 5°c cette nuit là: un 7/5/21... elles ont eu froid cette nuit là avant que je vienne les reprendre
int PertePoidsSecondaire = 1 ;  			// comme indiqué ci-dessus lors de mon essaimage secondaire il n'y avait qu'un kilo d'abeilles
int ControleSecondaire = 3;			     // Choix entre 1 et 5 permet la sélection du nombre de points avec la perte de poids ( 3 points de suite (de perte de poids)  et on envoie l'email, 4 il faut 4 points (de perte de poids) de suite avant l'envoi de l'email)	


// permet le passage automatique à l'heure d'hiver et d'été
//mais permet aussi de synchroniser l'horloge de l'Arduino si dérive..

boolean BootMiseAJourHeure = 1;   		// si on veut à chaque reboot mettre à jour l'horloge on met 1 sinon pas de mise à jour = 0
boolean EteHiver = 1;      			// si on veut gérer le passage à l'heure d'été et d'hiver on met un sinon pas de passage heure d'été on met 0!
boolean ChangementHeure = 0;    		//indicateur pour savoir si on a déjà fait le changement d'heure



/* Gestion alerte par mail dysfonctionnement certains capteurs*/

boolean AlertePbCapteurs = 1;     		// si à 1 on envoi un email pour signaler le problème du capteur en question (horloge, Temp interieure, température extérieuret hygro, carte SD)



// DEFINITION DE SI ON VEUT OU PAS METTRE UN ACCES SERVEUR POUR RECUPERER ou EFFACER MANUELLEMENT DES FICHIERS STOCKES SUR LA SD
// ATTENTION C'EST LONG MAIS CELA FONCTIONNE, SI ON PATIENTE LE FICHIER FINI PAR VENIR (IL FAUT PARFOIS S'Y REPRENDRE A PLUSIEURS FOIS POUR ACCEDER à LA PAGE DE SELECTION DEMANDé)
// car si on est dans la tempo de'affichage de 7.5 secondes entre 2 écrans l'Arduino attend et ne peut donc pas traiter la demande de suite 
//et parfois la demande s'égard il faut alors rafraichir la page...
//Une fois activé la page de selection il faut aller ABSOLUMENT jusqu'au bout de la démarche sinon on perd l'affichage LCD et on perd le cadençage du sketch

// Il Y A SOUVENT DES PAGES WEB INDIQUANT UN DéLAI D'AFFICHAGE TROP LONG C'EST NORMAL L'ARDUINO EST OCCUPé à AUTRE 
// CHOSE MAIS TRAITERA LA DEMANDE Dès QUE POSSDIBLE ET LA PAGE SE METTRA à JOUR!
// PATIENCE EST LE METTRE MOT DE CETTE FONCTION!


boolean serveur=1;            // si à 0 on n'installe pas la possibilité d'accéder à distance via l'adresse IP de l'arduino au contenu de la SD


// si vous avez aussi votre serveur personnel comme moi sur Raspberry (si envie demandez moi je vous donnerai ce qu'un ami a développé pour moi....) c'est encore en cours de développement au niveau du serveur même si un prototype fonctionne déjà

int MaJserveur_Perso = 12;         //  nombre de fois 15 secondes que l'on veut aller mettre à jour son serveur personnel
boolean serveurPerso = 0;         // si on a un serveur personnel à mettre à jour in met 1 sinon 0...

The file internet parametre.h



/* CHOIX DU TYPE D'ECRAN LCD I2C OU FILAIRE
  se fait ICI en commentant ou decommentant l'interface voulue (I2c ou filaire)
  ET EN METTANT Typeecran = 1 POUR ECRAN LCD I2C OU = 0 POUR ECRAN LDC FILAIRE*/

//LiquidCrystal monEcran(9,8,7,6,5,4);    		// création de l'objet monEcran en mode filaire
LiquidCrystal_I2C monEcran(0x27, 16, 2);  		// création de l'objet monEcran en cablage I2C

boolean Typeecran = 1;          			//1 on vient de choisir l'écran I2C NE PAS OUBLIER DE COMMENTER LA LIGNE //LiquidCrystal monEcran(9,8,7,6,5,4);



/*SELECTION PRéSENCE OU PAS DE CERTAINS CAPTEURS POUR CEUX DONT SONT LAISSé LE CHOIX!
   CAPTEURS OBLIGATOIRES : POIDS, TEMPéRATURE EXTERIEURE AVEC HYGROMéTRIE...
   CAPTEURS OPTIONNELS : LUMINOSITé, PLUIE, TEMPéRATURE INTéRIEURE
   IL S'AGIT Là D'UN CHOIX PERSONNEL SI VOUS VOULEZ EN CHANGER PLUS, LE CODE EST A VOUS!!!!!
*/

boolean PresenceCapteurPluie = 1;                                   // =1 si présent, =0 si absent
boolean PresenceCapteurLuminosite = 1;                              // =1 si présent, =0 si absent



// SI VOUS UTILSEZ UNE SONDE DE TEMPERATURE DS 1820/18B20 n'oubliez pas de mettre un résistance de tirage de 4,7 K Ohms entre le + 3.3v  et le data (en général fil rouge et jaune)

boolean PresenceCapteurTemperatureInterieure = 1;                  // =1 si présent, =0 si absent



 /*  ET POUR LA VERSION D'HORLOGE, IL FAUT COMMENTER CELLE QUI NE VOUS SERT PAS SELON VOTRE MATERIEL */

 // RTC_DS1307 rtc;                               		   // création de l'objet horloge nommé rtc (bouclier horloge avec SD v1.0)
   RTC_DS3231 rtc;                                 		   // selon votre modèle d'horloge... 



/* ATTRIBUTION DES PINS UTILISES POUR LA BALANCE ET LES CAPTEURS */


#define SCK  2                                 // on affecte le pin 2 -> HX711 DOUT
#define DT  3                                  // on affecte le pin 3-> HX711 CLK
#define pinCS_SD 4                             // on reserve le Pin 4 pour le lecteur de SD
#define ONE_WIRE_BUS 5                         // affecte le pin 5 au wire bus
#define bouton 6                               // déclaration du pin 6 pour le bouton poussoir de réinitialisation forcée de la tare et remise à zéro
#define pinCS_ETH  10				// on affecte le pin 10

#define DHTPIN A0                              // on affecte le pin A0 pour le DHT
#define capteurpluie A1                        // on affecte le pin A1 pour le le détecteur de pluie
#define photores A2                            // on affecte le pin A2 pour le capteur de luminosité
  


/* PARAMETRE DU CHOIX DE TARAGE DE LA BALANCE SI COUPURE DE COURANT AVEC RUCHE DESSUS */
boolean remise_zero = 0;                        /*mettre 1 si on veut reintialiser la balance sur coupure de courant (donc si ruche dessus on tare avec la ruche à o kG)
                                                 sinon mettre 0 en cas de coupure, on ne re tare pas mais mesure corrigée par une légére approximation...*/


float calibration_factor = -20520;              // remplacez cette valeur par votre propre facteur de calibration issue du programme de calibration
float correction = 4.39;                        // correction si redémarrage sans tarage (poids à vide du support de balance vu par la capteur) en kg
						// valeur obtenue aussi avec le programme de calibration en regardant le delta indiqué par la balance
						// lorsque vous la remettez sous tension avec un poids de valeur connue et vous obtenez la différence


/*
CHOIX D'AFFICHAGE OU NON DES PARAMETRES NORMAUX SUR LE MONITEUR SERIE
 IL SUFFIT DE PASSER MONITEUR A 0 POUR NE PLUS AFFICHER SUR LE MONITEUR SERIE
*/

boolean moniteur = 1;


/*OPTION D'UTILISATION OU PAS DE LA CARTE SD
   SI SDOK =1 on utilise la carte SD si SDOK = 0 pas d'écriture sur la SD!
   Attention la mirco SD ne doit pas dépasser 32 GB formatée en FAT */

boolean SDOK = 1;
int MaJ1 =4;            	// permet d'économiser de la taille fichier sur SD si MaJ1 = 0  on consomme environ 6,5 Mo pour un enregistrement d'un mois toutes les 15 secondes
                        	// si Maj1 = 4 on enregistre les données que  toutes les minutes et le fichier est 4 fois moins gros...
                               // surtout si on envoie ce fichier par email les messageries sont souvent limitées en taille de pièce jointe!

And the file.ino (setup and loop)


// on importe les bibliothèques

#include "HX711.h"192.168.1.28
#include "SD.h"
#include "SPI.h"
#include "RTClib.h"
#include <LiquidCrystal.h>        			//utile uniquement si écran sans interface I2C 
#include "DHT.h"
#include"Adafruit_Sensor.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Ethernet.h>     			 	    // bibliotheque Ethernet ou ethernet2 uniquement W5500...
#include "parametres.h"       				// contient vos réglages
#include <avr/pgmspace.h>   				  // pour gagner un peu de mémoire avec les variables PRGMEM...
#include "ThingSpeak.h"
#include <DallasTemperature.h>
#include <OneWire.h>
#include <EMailSender.h>

EthernetClient client;


extern void afficheserveur();                     								                      // fonction située dans l'onglet Fonctions_internet.cpp
extern String AdresseIP();                        							                      	// fonction située dans l'onglet Fonctions_internet.cpp
extern int envoimail(int lequel,String NomFichierMensuel,int nbessai);    					    // fonction située dans l'onglet Fonctions_internet.cpp
extern void thingspeak(float t, float h, float units, int lum, int TempInt);    				// fonction située dans l'onglet Fonctions_internet.cpp
extern String selectionmois(DateTime now);              						                   	// fonction située dans l'onglet Fonctions.cpp
extern String selectionmoisavant(DateTime now);        								                  // fonction située dans l'onglet Fonctions.cpp
extern void affichageecran(DateTime now, float t,float h,float units, int lum, float TempInt, boolean eau);   	// fonction située dans l'onglet Fonctions.cpp
extern void affichageserie(DateTime now, float t, float h, float units, int lum, float TempInt,boolean eau);   	// fonction située dans l'onglet Fonctions.cpp
extern void err(int code, int x);                       						                                          	// fonction située dans l'onglet Fonctions.cpp
extern void ecritureSD(DateTime now, float t,float h,String Mois, int lum, float TempInt, boolean eau); 		    // fonction située dans l'onglet Fonctions.cpp

extern RTC_DS3231 rtc;					                      // à modifier aussi dans onglet parametres_Fonctions_internet
//extern RTC_DS1307 rtc;				                      // à modifier aussi dans onglet parametres_Fonctions_internet

extern float resultat;                     		        // chaine de caractère située dans l'onglet Fonctions_internet.cpp
extern boolean desarmement;                           //variable située dans l'onglet Fonctions_internet.cpp
extern int nbessai;                                 	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EnvoiMailIP;                         	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EnvoiMailVol;                        	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EnvoiMailEssaimage;                  	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EnvoiMailMensuel;                    	//variable située dans l'onglet Fonctions_internet.cpp
extern int TempsPertePoids;                         	//variable située dans l'onglet Fonctions_internet.cpp
extern int pertepoids;                              	//variable située dans l'onglet Fonctions_internet.cpp
extern int PertePoidsSecondaire;                     	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EssaimageSecondaire;                 	//variable située dans l'onglet Fonctions_internet.cpp
extern int DebutMois;                               	//variable située dans l'onglet Fonctions_internet.cpp
extern int FinMois;                                 	//variable située dans l'onglet Fonctions_internet.cpp
extern int DebutHeure;                              	//variable située dans l'onglet Fonctions_internet.cpp
extern int Finheure;                                  //variable située dans l'onglet Fonctions_internet.cpp
extern int TemperatureMiniEssaimage;                  //variable située dans l'onglet Fonctions_internet.cpp
extern int ControlePrimaire;			     	              //variable située dans l'onglet Fonctions_internet.cpp
extern int ControleSecondaire;			     	            //variable située dans l'onglet Fonctions_internet.cpp

extern boolean serveur;                             	//variable située dans l'onglet Fonctions_internet.cpp
extern unsigned long myChannelNumber;               	//variable située dans l'onglet Fonctions_internet.cpp
extern const char * myWriteAPIKey;                  	//variable située dans l'onglet Fonctions_internet.cpp
extern int MaJ;                                     	//variable située dans l'onglet Fonctions_internet.cpp
extern int things;                                  	//variable située dans l'onglet Fonctions_internet.cpp
extern byte mac[];                                  	//variable située dans l'onglet Fonctions_internet.cpp
extern IPAddress ip;                                	//variable située dans l'onglet Fonctions_internet.cpp
extern int Choix1;                                   	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean drapeau;                              	//variable située dans l'onglet Fonctions_internet.cpp
extern int retourmail;                               	// contient l'information de retour d'envoi du mail 
extern boolean serveurPerso;                         	//variable située dans l'onglet Fonctions_internet.cpp
extern int MaJserveur_Perso;                         	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean EteHiver;                             	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean ChangementHeure;                       //variable située dans l'onglet Fonctions_internet.cpp 
extern boolean BootMiseAJourHeure;                    //variable située dans l'onglet Fonctions_internet.cpp
extern boolean OKSD;                                	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean OKTempHygro;                         	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean OKHorloge;                           	//variable située dans l'onglet Fonctions_internet.cpp
extern boolean OKTempInt;                           	//variable située dans l'onglet Fonctions_internet.cpp

extern String Mois;                                  	//variable située dans l'onglet Fonctions.cpp  
extern String Moisavant;                             	//variable située dans l'onglet Fonctions.cpp 
extern boolean PresenceCapteurPluie;                  //variable située dans l'onglet Fonctions.cpp 
extern boolean PresenceCapteurLuminosite;             //variable située dans l'onglet Fonctions.cpp 
extern boolean PresenceCapteurTemperatureInterieure;  //variable située dans l'onglet Fonctions.cpp 

extern boolean Typeecran;				                      // variable contenu dans Parametres.h
extern float calibration_factor;			                // variable contenu dans Parametres.h
extern boolean remise_zero;				                    // variable contenu dans Parametres.h
extern boolean moniteur;				                      // variable contenu dans Parametres.h
extern boolean SDOK;					                        // variable contenu dans Parametres.h
extern float units;                    		          	// variable de stockage du poids situé dans onglet fonctions.cpp



boolean armement = 0;                           	  // armemement pour envoi mail de vol (poids de départ suffisant)
boolean affichage = 0;					                    // variable pour geler l'affiche suer LCD lors de l'accès serveur Arduino
boolean eau;						                            // variable pour savoir si il pleut ou pas
int code = 0;                                   	  // variable pour selection de quelle erreur
boolean okmail=0;					                          // variable pour l'envoi d'un seul mail
int compteur = 0;                              		  // compteur pour aller mettre à jour ThingSpeak
int compteur1 = 0;                              	  // compteur pour aller mettre à jour l'historique du poids
int compteur4 = 0;                                	// compteur du nombre de passage dans le stockage memoriel de poids si on a déjà envoyé avec succés un mail d'essaimage
int compteur2 = 0;                                	// compteur du nombre de passage dans la boucle pour l'écriture SD
int compteur3 = 0;                                	// compteur du nombre de passage dans la boucle pour l'envoi au serveur personnel
int compteursauvegarde = 0;                       	// compteur de retour à la normal si on a quitté en sauvage la page web...
int MemJour;                                    	  // memoire du jour que l'on est pour la gestion des mails d'alerte de perte de capteurs
int MemControlePrimaire;				                    // mémoire du nombre de point pas envoi email essaimage primaire
int MemControleSecondaire;				                  // mémoire du nombre de point pas envoi email essaimage secondaire
int CompteurRearmPrimaire = 0;                      // compteur pour réarmer le compteur si on a eu juste un point sans envoi d'email au bout d'une heure on remet à 0
boolean Drapeau1 = 0;                                   // indicateur de passage dans la boucle de mémorisation

#define DHTTYPE DHT22                          		  // définition du type de capteur
OneWire monWire(ONE_WIRE_BUS);				              // pour capteur température interne
DallasTemperature senseur(&monWire);			          // pour capteur température interne
DHT dht(DHTPIN, DHTTYPE);                       	  // création de l'objet capteur T° et hygro    

            


String Typefichier=".csv";                   		    // type de fichier que l'on souhaite sur SD attention si modification il faut modifier le fileDescriptor[0].mime = "text/csv"; dans la fonction envoimail!
String NomFichierMensuel;                     		  // stockage nom du fichier mois précédent


float tableaumemoirepoids[5];                   	  // tableau de 5 valeurs de poids (fonction mémoire)
int i = 0;                                        	// pour incrémenter le stockage des valeurs de poids dans le tableau
int I2;                                           	// variable pour connaitre i-5 enregistrements
int nbessaimemoire;
String rafraichir;					                        // définition chaine pour rafraichissement page web de l'Arduino

String MonAdresseIP2; //= AdresseIP();     		      // envoi l'adresse IP à la fonction de convertion en chaine de caractère //pour affichage mail



HX711 balance;                                  	// Création de l'objet mesure de poids nommé balance
Sd2Card card;

/*ON COMMENCE LE SETUP */

void setup(){ 

MemControlePrimaire = 0; 				                  // on initialise le compteur de relevé consécutif de perte de poids pour envoie email essaimage primaire
MemControleSecondaire = 0;				                // on initialise le compteur de relevé consécutif de perte de poids pour envoie email essaimage secondaire
nbessaimemoire = nbessai;                 		    // on memorise le nombre d'essai demandé pour pouvoir le réintialiser!
  
Serial.begin(9600);                           		// on initialise le moniteur série a 9600 bauds
           
pinMode(bouton, INPUT_PULLUP);                		// déclaration du bouton reset en entrée avec la résistance pullup
    
if (Typeecran == 1) monEcran.init();            	// initialisation ecran LC I2C
else                monEcran.begin(16,2);       	//on initialise la communication avec 16 colonnes et deux lignes
                    
 
pinMode(53, OUTPUT);              			          //carte mega pour pouvoir accéder en même temps à la SD et à Ethernet doit être activé
 

  /*Test pour voir si la carte SD fonctionne */

if (!card.init(SPI_HALF_SPEED, pinCS_SD))err(2,0);              // si on n'arrive pas à démarrer la carte SD on envoie à la fonction de gestion des erreurs                                   
else Serial.println(F("Montage correct et carte présente"));		// sinon on affiche sur le moniteur série que tout va bien
         
senseur.begin();         				                  // on démarre le capteur de température interne     
    
   /* ON ATTAQUE LA MISE EN SERVICE DE L'ETHERNET */ 
Ethernet.init(pinCS_ETH);                   		  // démarre la carte ethernet sur le pin 10

                      
char erreur = 0;					                        // initialise la variable d'erreur de démarrage du Shield Ethernet

erreur = Ethernet.begin(mac);              		    // On tente de démarrer le shield Ethernet SANS adresse IP (donc donnée via DHCP de la box)
  
if (erreur == 0) {
      Ethernet.begin(mac, ip);                   // forcage de l'adresse ip si pas acquise par box
      err(1,0);
                 }   					                  // fin du if erreur d'obtention IP dynamique!
                  
 
delay(1000);                                    // Donne une seconde au shield pour s'initialiser
   
MonAdresseIP2 = AdresseIP();     			          // envoi l'adresse IP à la fonction de convertion en chaine de caractère pour affichage mail

rafraichir = "<meta http-equiv=refresh content=8;URL='//" + MonAdresseIP2 +"/'>";   // création de la chaine de rafraichissement de la page web toutes les 8 secondes...   



  /* envoi un email contenant l'adresse IP de la ruche*/

if(EnvoiMailIP == 1) {
                    envoimail(4,MonAdresseIP2,nbessai);	 // envoi vers la fonction d'envoi d'email

                    if (retourmail == 1) err(8,0);  	   // si on a pas réussi à envoyer le mail on envoi l'info à la fonction de gestion d'erreur

                    nbessai = nbessaimemoire; 		      // on reinitialise le nombre d'essai pour les mails suivants
 
                    }                                   // fin de l'envoi mail IP  
  

balance.begin(DT, SCK);                       		// on intialise la balance, avec les pins réservés

balance.set_scale(calibration_factor);        		// on applique le coefficient de calibrage determiné lors de l'utilisation du programme de calibration

if (remise_zero == 1)                           	// on vient ici si l'on souhaite refaire la tare à chaque coupure de courant
    {
    balance.tare();                            		//Partant du principe que la balance est vide lors de l'allumage, remise à zéro 
                                                	//attention si coupure de courant sur place cela remet à zéro la balance avec la ruche dessus...
                                                	//mais si pas de tarage à la remise sous tension, du coup décalage leger du zéro d'environ 4.35 kg
							                                    //(varie un peu suivant le capteur : à modifier à chaque balance comme le coefficient de calibration) corrigé plus loin...
    }

 
pinMode(capteurpluie,INPUT);    			            // Initialise le capteur de pluie
pinMode(pinCS_SD, OUTPUT);                        // Initisalisation le lecteur de carte SD en écriture
  
dht.begin();                  				            // démarrage du capteur dht de T° & hygro



  /* ON ATTAQUE LA MISE EN SERVICE DE L'HORLOGE  */
 
rtc.begin();                  				            // démarrage de l'horloge rtc
    
//  rtc.adjust(DateTime(__DATE__, __TIME__));    	//A DECOMMENTER POUR LA MISE à JOUR INITIALE D'UNE HORLOGE NEUVE (puis à recommenter)
  
// if (! rtc.isrunning()) {                        // Si l'horloge 1307 (ou bouclier horloge v 1.0 avec SD) decommentez cette ligne à la place de la ligne ci dessous  (commentez celle du dessous)
                                                       
if (! rtc.begin()) {                             	// cette ligne active si horloge 3231 à la place 1307
    err(5,0);						                          // envoi à la fonction de gestion des erreurs
    rtc.adjust(DateTime(__DATE__, __TIME__));     // Cette ligne ajuste le RTC à la date et time du moment de compilation si l'horloge n'est pas opérationnelle
                    }       				              // fin de problème de démarrage de l'horloge  
                     
    /* QUELLE HEURE EST-IL? */               
DateTime now = rtc.now();                             	//lit l'heure au boot                        
if(BootMiseAJourHeure == 1) MiseAjourHeure(rtc.now());  // au boot on régle l'heure si option activée
 
senseur.begin();                                 	// on démarre le capteur de température intérieure
ThingSpeak.begin(client);                       	// on initialise ThingSpeak

    
              }                                    // fin du setup



/* 	DEBUT DE LA BOUCLE  */

void loop()
{

 CompteurRearmPrimaire = CompteurRearmPrimaire+ 1; //compteur de passage dans la boucle
 /* Si on a passé 1 heure (240*15=3600 secondes on reinitialise les compterus d'envoimail essaimage */
 if(CompteurRearmPrimaire == 240){CompteurRearmPrimaire=0; MemControleSecondaire = 0; MemControlePrimaire = 0;}

 
/* ON S'OCCUPE DE LA BALANCE */
     
units = balance.get_units(20),10;               	//get_units(n)lecture et moyennage de n valeurs « brutes », moins la valeur de tare, puis mise à l’échelle
 
if (remise_zero == 0)                         		// si on souhaite pas remettre à zéro la balance on corrige le léger écart pour essayer de garder le vrai poids suite à coupure alimentation
  {
    units = units-correction;                    //correction du à l'absence de tarage en cas de coupure de courant avec la ruche sur la balance
 
  }
  
/*    if (units < 0 && remise_zero == 1)         // sert à empêcher l'affichage des valeurs négatives
    {                                         	//mais peut-être utile pour signaler une remise à zéro
      units = 0;                           		  //de la tare si cette option est en service et coupure
    }    */                                     // alimentation arduino...
    
if (units <= 0 & remise_zero == 0) {units = balance.get_units(),10;       // si il y a une aberration dans la lecture on refait une lecture
                                    units=units-correction;            	 //correction du à l'absence de tarage en cas de coupure de courant avec la ruche sur la balance
 
                                   }

if (units <= 0) units = balance.get_units(),10;    // ici pour effacer les aberrations avec remise_zero =1 et en même temps deuxieme chance (sans correction) si 2 lectures aberrantes de suite..

      
                           
GestionBouton();                             		  // permet de forcer la tare de la balance si on appui sur le bouton reset!



/* ON LIT LES AUTRES CAPTEURS */
float TempInt;                                  	//reserve une variable pour la température intérieure ruche (capteur DS18B20)
float h = dht.readHumidity();                   	// lit le taux d'humidité
float t = dht.readTemperature();                	// lit la temperature en Celsius (par défaut)
int lum = analogRead(photores);                 	//lit le capteur analogique de luminosité
lum = map(lum, 0, 1023, 0, 100);                	//convesrion valeur analogique en numérique
boolean eau = digitalRead(capteurpluie);          // prend une valeur de 0 si il pleut ou 1 si pas de pluie (mesure de conductivité)
if (PresenceCapteurPluie == 0) eau = -1;        	// si pas de capteur de pluie on stocke -1 sur le fichier csv
int a = 0;                                        // variable de stockage de i
int lequel;                                     	// quel type de mail envoyer 
int nbessai=nbessaimemoire;                     	// nombre d'essai d'envoi de mail sur echec
 


senseur.requestTemperatures();                  	// requete de lecture du capteur sur bus wire

if (PresenceCapteurTemperatureInterieure == 1) TempInt = (senseur.getTempCByIndex(0));               // lit la température du capteur intérieur de la ruche
else TempInt = -1;                                	// si pas de capteur de température intérieure on stocke à -1 
  

	/* VERIFICATION ETAT CAPTEUR TEMPERATURE ET HYGROMETRIE */
if (isnan(h) || isnan(t)) err(3,0);                   	// Vérifie si la lecture T° et hygro sont correctes sinon envoie le message d'erreur numéro 3.
if (TempInt<-2) err(7,0);                             	// Vérifie si la lecture de la température intérieure est correcte sinon envoie le message d'erreur numéro 7.
               
          
	/* EST-CE QU'IL PLEUT? */   
//if(eau == LOW) Serial.println(F("Pluie "));		        // affichage sur moniteur série quand il pleut peut-être masqué


        /* QUELLE HEURE EST-IL? */               
DateTime now = rtc.now();                              //lit le temps à chaque boucle



   /* Gestion mémoire d'envoi que d'un seul mail par jour sur problème capteurs*/

if (OKSD == 1 ||OKTempHygro == 1 ||OKHorloge == 1 || OKTempInt == 1) MemJour == now.day();     	              // on mémorise le date du jour pour pouvoir réintisaliser l'envoi d'un autre mail demain...
if (now.day() > MemJour || now.day() == 1) {OKSD = 0; OKTempHygro = 0; OKHorloge = 0; OKTempInt = 0;}	        // si on est demain on réinitialise la possibilité d'envoi un email d'information de problème capteur
  

  /* comptage du passage dans la boucle*/

if(things == 1) compteur = compteur+1;                  	 // on incrémente le nombre de passage dans la boucle pour la mise à jour de ThingSpeak

if(EnvoiMailEssaimage == 1)compteur1 = compteur1+1;     	// on incrémente le nombre de passage dans la boucle pour la mémorisation du poids dans le temps

if (SDOK == 1) compteur2=compteur2+1;                   	// on incrémente le nombre de passage dans la boucle pour l'écriture sur la SD
  

selectionmois(rtc.now());                            	    //   appel à la fonction choix du mois actuel

selectionmoisavant(rtc.now());                       	    //   appel à la fonction choix du mois précédent
   


//ATTENTION au changement d'année...
if(now.month() == 1)NomFichierMensuel = Moisavant + (now.year()-1)+ Typefichier; 		// création du nom du fichier mois de décembre + année précédente + type
else NomFichierMensuel = Moisavant + now.year()+ Typefichier;       				        // création du nom du fichier mois d'avant + année en cours + type



   /* appels aux différentes fonctions selon besoin */
       
if (SDOK == 1 && compteur2 == MaJ1) {ecritureSD(rtc.now(),t,h,Mois,lum,TempInt,eau);           	// appel à la fonction écriture fichier SD
                                      compteur2=0;						                                  // réinitialise le compteur2
                                 }
if(SDOK == 0) err(4,0);                                                              		        // informe que la SD est désactivéze uniquement sur l'écran local
   


if (moniteur == 1) affichageserie(rtc.now(),t,h,units,lum,TempInt,eau);      			              // appel à la fonction  affichage sur moniteur série PEUT ETRE COMMENTE POUR GAGNER DE LA PLACE MEMOIRE AVANT MISE EN PLACE REEL! (-38 octets)
    
 

if(affichage == 0)  affichageecran(rtc.now(),t,h,units,lum,TempInt,eau);                    	  // appel à la fonction qui inscrit sur l'écran LCD
else err(13,0);											// si problème envoie à la fonction de gestion des erreurs
   


if( things == 1 && compteur == MaJ) {thingspeak(t,h,units,lum,TempInt);                         // permet de moins mettre à jour ThingSpeak pour profiter de plusieurs Arduino sur un seul champ ici environ toutes les 2 mn
                                           
                        	  compteur = 0;                                                      // réinitialise compteur       
                      		}          					 // fin de la mise à jour de ThingSpeak


                      
   /* ici on gére la mise à jour de son serveur personnel! */

if (serveurPerso == 1) compteur3 = compteur3+1;         									    // si on a un serveur personnel on incremente le passage dans la boucle de ce compteur

if (serveurPerso == 1 & compteur3 == MaJserveur_Perso){      									// si on a un serveur personnel et une mise à jour toutes les x mn...
   						   conversion( h, t, units, lum, TempInt, eau);					        // envoi à la fonction de conversion en string
   						   envoiserveur();    								                          // envoi au serveur personnel
   						   compteur3 = 0;                                 				      //réinitialise compteur3
						  }										                                            //  fin de fonction serveur personnel


                       
	/* mémorisation poids de la ruche en vue de la surveillance de l'essaimage...*/

if (EnvoiMailEssaimage == 1 && compteur1 == TempsPertePoids){        					// si on a décidé d'envoyer le mail sur essaimage et qu'on a fait x passages dans la boucle 
  
 		if (desarmement == 1) compteur4 = compteur4 + 1;                    		// si on a déjà envoyé avec succés un mail d'essaimage on incremente
 		if (compteur4 == 5){ compteur4 = 0; desarmement = 0; MemControleSecondaire = 0; MemControlePrimaire = 0;}       // lorsque l'on a stocké 5 poids dans la mémoire on réarme la possibilité d'envoyer un mail d'essaimage


 	/* on fait cela à chaque fois que le nombre de passage dans la boucle = tempsPertePoids*/

 		tableaumemoirepoids[i]=units;                                             // on stocke le poids dans le tableau

  
  		switch (i){                                                             // sélection du point de comparaison
  		case 0 : I2 = 1; break;
  		case 1 : I2 = 2; break;
  		case 2 : I2 = 3; break;
  		case 3 : I2 = 4; break;
  		default : I2 = 0; }  

   
      a = i;								                                          // on mémorise i dans a

   		resultat= tableaumemoirepoids[I2]-tableaumemoirepoids[a];    	    // stocke la perte de poids 
      Serial.print(F("resultat : "));
      Serial.println(resultat);
      Serial.print(F("Voci les valeurs de poids mémorisées"));
      Serial.println(F("tableau mémoire de 0 à 4 :"));
      Serial.println (tableaumemoirepoids[0]);
      Serial.println (tableaumemoirepoids[1]);
      Serial.println (tableaumemoirepoids[2]);
      Serial.println (tableaumemoirepoids[3]);
      Serial.println (tableaumemoirepoids[4]);

  
   		i = i+1;                                                          // incrémente i pour stocker le poids dans la cellule suivante du tableau
   
   		if (i == 5) i = 0;                                                // réintialise i à zéro=> on recommence à remplir le tableau depuis le début

   		compteur1 = 0;                                                    // réinitialise compteur1
      Drapeau1 = 1;                                                     // indicateur du passage dans la boucle de mémorisation
   		                                                }                // fin du if mémorisation pour stockage poids dans tableau, on réinitialise compteur1




  /* envoi mail sur essaimage de la ruche...*/
 
if (EnvoiMailEssaimage == 1 && resultat >= pertepoids && Drapeau1 == 1){							      //  si la perte de poids de la ruche est supérieur ou égal à PertePoids 


                MemControlePrimaire = MemControlePrimaire + 1;            // on incrémente le compteur
                Drapeau1 = 0;                                             // on réinitialise l'indicateur de passage dans la boucle de mémorisation

                Serial.print(F("On vient d'avoir les conditions d'envoi d'eamil essaimage primaire x fois : "));
                Serial.println(MemControlePrimaire);

                

  						   if(desarmement == 0 && MemControlePrimaire == ControlePrimaire){ envoimail(3,MonAdresseIP2,nbessai); 		// on essaye d'envoyer un email d'essaimage mais si on en a déja envoyé un avec succés... non
  								   						                                                 
  								   						                                                  MemControlePrimaire = 0;				         // on réinitialise le compteur
                                                                              
								  						                                                  }							                            // fin du if pour envoi mail

						   if (retourmail == 1) err(11,0);			                      // si problème on va à la fonction gestion d'erreur

                                                   }                    //fin de la gestion mail essaimage


                                                                                                                            
  /* envoi email essaimage secondaire....*/                                                                                         

if (EssaimageSecondaire == 1 && resultat<pertepoids && resultat >= PertePoidsSecondaire && Drapeau1 == 1 && t >= TemperatureMiniEssaimage && now.month() >= DebutMois && now.month() <= FinMois && now.hour() >= DebutHeure && now.hour() < Finheure){			//  si la perte de poids de la ruche est supérieur ou égal à PertePoids

  						   MemControleSecondaire = MemControleSecondaire + 1;		      // on incrémente le compteur
                 Drapeau1 = 0;                                             // on réinitialise l'indicateur de passage dans la boucle de mémorisation
  						   
  						   if(desarmement == 0 && MemControleSecondaire == ControleSecondaire) {envoimail(10,MonAdresseIP2,nbessai); 	  // on essaye d'envoyer un email d'essaimage mais si on en a déja envoyé un avec succés... non
  								       							                                                MemControleSecondaire = 0;		          // on réinitialise le compteur
								      							                                                  }					                              // fin du if pour envoi mail
                 if (retourmail == 1) err(11,0);							            // si problème on va à la fonction gestion d'erreur
  
                                                                      }  // fin de la fonction d'envoi mail essaimage secondaire




	/* envoi mail sur vol de la ruche...*/

if (remise_zero == 0 && units> 10) armement = 1;      		            // évite l'envoi du mail à la mise en place de la ruche sur la balance 
if (remise_zero == 1 && units>pertepoids+1) armement = 1;     	      //si on a choisi de remettre à zero la tare sur coupure de courant on attend que la ruche prenne au moins pertepoids + 1 kg avant 
                                                       		            // d'activer l'option de vol de la ruche....
  
                                            
if (armement == 1 && units <= 7 && EnvoiMailVol == 1) {
  
          					   envoimail(1,MonAdresseIP2,nbessai);  	      // gros problème de poids on enlève la ruche de la balance! on fera nbessai essais
          					   if (retourmail==1) err(10,0); 		            // si problème on va à la fonction gestion d'erreur
                                        
          					   armement =0;                                //on n'envoi qu'un seul mail 

                              			     }                          // fin de l'envoi du mail sur vol

                              

	/* envoi mail mensuel des données de la ruche...*/
if (EnvoiMailMensuel == 1 && okmail==0 && now.day() == 1 && now.hour() == 1){
 									envoimail(2,NomFichierMensuel,nbessai);         //on est le 1 du mois à une heure du matin on envoi le mail avec fichier
 									if (retourmail==1) err(9,0); 		                // si problème on va à la fonction gestion d'erreur
 									else okmail=1;    			                        // si on a bien envoyé l'email on ne le fait qu'une seule fois 
  
									if (okmail==1 && now.day()==5)okmail=0;         // le 5 du mois on réintialise l'envoi de mail de relevé mensuel pour le mois suivant
  

        
                                                                           }     // fin du test d'envoi mensuel du mail

                                                                              
	/* fonction serveur Arduino pour accès manuel à distance */

if (serveur ==1 && drapeau == 0) pagewebselection();           		//  on va sur la fonction serveur qui permet de choisir si on veut supprimer ou télécharger un fichier depuis la SD


if (serveur ==1 && drapeau == 1){compteursauvegarde = compteursauvegarde+1; GestionFichier(Choix1);}	// on a fait notre choix entre suppression et téléchargement on envoie à la fonction GestionFichier

if (compteursauvegarde == 35) {affichage = 0; compteursauvegarde = 0; drapeau = 0;}			// fonction de sauvegarde en cas de problème avec le serveur Arduino on redémarre les services




	/* Fonction mise à jour horloge depuis réseau*/

if (EteHiver == 1) MiseAjourHeure(rtc.now());   							// si on souhaite faire le changement d'heure d'été et d'hiver ou tout simplement réajuster l'horloge 
                      
if( ChangementHeure == 1 && now.month() == 4 || now.month() == 11) ChangementHeure = 0;    		// en avril et novembre on réintialise la possiblité de changer d'heure pour la saison suivante                 

}                                                              // fin de la boucle principale












 

Get yourself a copy of one of the freemem functions and log what is reports over time. It will let you see if you have a leak.

Hello, if I possibly have a memory space problem (due to a crazy String, or other) the Arduino (here a mega Arduino with only 54% of memory used during compilation) shouldn't it crash completely?

There it continues to run almost normally:
sensor acquisitions,
display,
storage on the micro SD,
it responds to the embedded web server,
transfer files from the local network, ...

It is "just" a loss of the functions of sending emails, and updates of the 2 servers (ThingSpeak and my personal server)

Can memory full do this? a partial malfunction ...

It's possible if the library code was written carefully. If something needs to allocate memory and actually checks for malloc failure, it could fail gracefully rather than crashing. Oftentimes, that isn't the case of course.

ok, i will test int Memoryfree = (freeMemory (), DEC); and store it on the micro SD in a debugging log!
Thank you

I only checked for 3 hours (I haven't had enough time to let the program run yet) but the memory remained to the nearest byte at its boot value: freememory = 2206 ...

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.