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é!</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é!</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élection de Suppression ou de Téléchargement des fichiers depuis la SD!</H1>"));
client.println("<hr />");
client.println(F("<H2>Attention à votre choix, vous n'avez pas le droit à l'erreur en cas du choix Suppression</H2>"));
client.println("<br />");
client.println("<a href=\"/?button1Suppr\"\">Accés à la page de Suppression</a>");
client.println("<a href=\"/?button1Tele\"\">Accés à la page de Téléchargement</a><br />");
client.println("<br />");
client.print(F("<p>Vous allez accéder aux fichiers de la ruche nº "));
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ésactivé 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.