Automatisation de mon Aquarium

Bonjour à tous,

Nouveau dans le monde Arduino, j'aimerai automatiser la gestion de mon aquarium

  • Contrôle de la température

  • Contrôle du pH

  • Contrôle de la conductivité de l eau

  • Contrôle de la hauteur d'eau

Je n'ai pas encore reçu l'ensemble des capteurs et éléments constituants le circuit mais je me suis mis au code.
Il se compile sans erreur mais je ne peux pas encore le tester.

Pouvez vous m'aider en m'indiquant les possibles erreurs de conception ainsi que les optimisations possibles si vous en voyez.

Merci à vous tous pour votre aide

#include "OneWire.h"
#include "DallasTemperature.h"
#include "Ultrasonic.h"         
#include "LiquidCrystal_I2C.h"
#include "Wire.h"
#include "DS3231.h"

// ************************************************
//
//          DECLARATION DES PINS A UTLISER
//
// ************************************************
//
// 0 -> sonde pH
// 2 -> interrupteur Marche Arret
// 3 -> PWM pour Lumiere (relais 100% / MOSFET PWM pour v2) - 12V
// 4 -> Relais chauffage - 220V
// 5 -> Relais Ventilateur - 12V
// 6 -> Relais pompe remplissage - 12V voir 6V après test
// 8 -> ECHO capteur Ultrason
// 9 -> TRIG capteur Ultrason
// A0 -> Sonde temperature DS18B20
// A1 -> Sonde TDS
// A4 et A5 -> SDA et SCL pour Communication I2C pour ecran LCD et DS3231 
// vcc -> Alimentation 12V du circuit (Arduino et relais / mosfet)
// GND
// 5V -> Alimentation des capteurs et I2C
// 

//  Definition donnees de temps
RTClib monTemps;

// définition du type d'ecran lcd 20 x 4
LiquidCrystal_I2C LCD(0x27,20,4); 

// Definition capteur ultrason
Ultrasonic ultrasonic(9, 8); // Trig et Echo
int dist = 0;

// Definition sonde DS18B20
OneWire oneWire(A0);
DallasTemperature ds(&oneWire);

// Definition bouton Marche/Arret
#define BoutonMarche 2
boolean Marche = 0;

// Definition sonde TDS
#define TdsSensorPin A1
#define VREF 5.0              // Voltage de reference de ADC
#define SCOUNT  20            // Nombre d'echantillons
int analogBuffer[SCOUNT];     // Tableau pour les donnees analogique de ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0, copyIndex = 0, temperature = 25;
float averageVoltage = 0, tdsValue = 0;

//Definition sonde PH
#define PHSensorPin 0           //pH meter Analog output to Arduino Analog Input 0
unsigned long int avgValue;     //Store the average value of the sensor feedback
float b, pH;
int buf[SCOUNT], temp;

// Definition relais
#define relaisLumiere 3
#define relaisChauffage 4
#define relaisVentilateur 5
#define relaisPompe 6

void setup()
{
  Serial.begin(115200);
  Wire.begin();                     // initialisation i2c
  pinMode(TdsSensorPin, INPUT);     // sonde TDS sur pin TdsSensorPin - input
  pinMode(PHSensorPin, INPUT);      // sonde PH sur pin PHSensorPin
  ds.begin();                       // sonde temperature activée
  LCD.init();                       // initialisation de l'afficheur
  LCD.backlight();
  pinMode(BoutonMarche, INPUT);     // initialisation du bouton Marche/Arret
  attachInterrupt(digitalPinToInterrupt(BoutonMarche), changementEtat, CHANGE);
  pinMode(relaisLumiere, OUTPUT);
  pinMode(relaisChauffage, OUTPUT);
  pinMode(relaisVentilateur, OUTPUT);
  pinMode(relaisPompe, OUTPUT);     // definition des pins des relais
}

void loop()
{
//      Acquisition des donnees : temperature, TDS, PH, hauteur d'eau, temps
    temperature = getTemper();
    tdsValue = getTDS(temperature);
    pH = getPH();
    dist = ultrasonic.read();
    DateTime now = monTemps.now();

//      Affichage des donnees : temperature, TDS, PH, Niveau d'eau, focntionnement
    affichage(temperature, tdsValue, pH, dist, now, Marche);

//      Traitement de la lumiere
// Version 1 : si l'heure est comprise entre 10 et 20h -> activation du relais 1 pour la lumiere
// Version 2 : activation de la lumiere via PWM et MOSFET pour lever et coucher de soleil

// Version 1 : Allumage à 10h extinction à 20h
  if ((now.hour()>=10) && (now.hour()<20))
    digitalWrite (relaisLumiere, HIGH);
  else
    digitalWrite(relaisLumiere, LOW);    

// Version 2 : A ecrire : Allumage 20% a 10h, +20% toutes les 10mn , -20% toutes les 10mn a partir de 19h jusqu'a extinction à 20h


//      Traitement Chauffage / Refroiddissement
// si temperature < 25°C -> activation relai 2 pour le chauffage
// si temperature > 28°C -> activation relai 3 pour la ventilation
  if (temperature <25)
    digitalWrite(relaisChauffage, HIGH);
  else
    digitalWrite(relaisChauffage, LOW);

   if (temperature >=28)
    digitalWrite(relaisVentilateur, HIGH);
  else
    digitalWrite(relaisVentilateur, LOW);

//      Traitement niveau d'eau
// si niveau d'eau > 3cm ET systeme en marche -> activation relais 4 pour la pompe de remplissage
  if ((dist > 3) && Marche) 
  {
    digitalWrite(relaisPompe, HIGH);      // Activation de la pompe
    while (dist >= 3)                     // Tant que le niveau d'eau est > 3cm
      {
        dist = ultrasonic.read();         // Mesure du niveau car la pompe tourne
      }
    digitalWrite(relaisPompe, LOW);       // Extinction de la pompe qd le niveau est atteint  
  }
}


// *******************************************
//
//          DECLARATION DES FONCTIONS
//
// *******************************************

//
//          Acquisition de la temperature
//    Parametre : aucun
//    Renvoi : temperature sous forme d'entier
//

int getTemper()
{
  ds.requestTemperatures();
  int t = ds.getTempCByIndex(0);
  return t;
}

//
//          Acquisition TDS
//    Parametre : T° sous forme d'entier
//    Renvoi : valeur TDS en ppm sous forme de float

//
float getTDS(int temp)
{
  static unsigned long analogSampleTimepoint = millis();
  if (millis() - analogSampleTimepoint > 40U)  //every 40 milliseconds,read the analog value from the ADC
  {
    analogSampleTimepoint = millis();
    analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin);    // read the analog value and store into the buffer
    analogBufferIndex++;
    if (analogBufferIndex == SCOUNT)
      analogBufferIndex = 0;
  }
  static unsigned long printTimepoint = millis();
  if (millis() - printTimepoint > 800U)
  {
    printTimepoint = millis();
    for (copyIndex = 0; copyIndex < SCOUNT; copyIndex++)
      analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
    averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
    float compensationCoefficient = 1.0 + 0.02 * (temp - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
    float compensationVolatge = averageVoltage / compensationCoefficient; //temperature compensation
    tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5; //convert voltage value to tds value

    return tdsValue;
  }
}

//
//          Acquisition pH
//    Parametre : aucun
//    Renvoi : valeur du PH sous forme de float
//

float getPH()
{
 for(int i=0; i<SCOUNT; i++)          //for(int i=0;i<10;i++)       //Get SCOUNT sample value from the sensor for smooth the value every 40 ms
 { 
   buf[i]=analogRead(PHSensorPin);
   delay(10);
 }
  avgValue = getMedianNum (buf, SCOUNT);
  float phValue = (float)avgValue*5.0/1024;
  phValue = 3.5*phValue;
  return phValue; 
}

//
//          Renvoie la valeur median d'un tableau d'entier
//    Parametres : Tableau de valeurs sous forme d'entier
//                 Nombre de valeurs dans le tableau sous forme d'entier
//    Renvoi : Valeur median du tableau sous forme d'entier
//

int getMedianNum(int bArray[], int iFilterLen)
{
  int bTab[iFilterLen];
  for (byte i = 0; i < iFilterLen; i++)
    bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++)
  {
    for (i = 0; i < iFilterLen - j - 1; i++)
    {
      if (bTab[i] > bTab[i + 1])
      {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0)
    bTemp = bTab[(iFilterLen - 1) / 2];
  else
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  return bTemp;
}

//
//          Affichage sur ecran LCD
//    Parametres : Valeur de la temperature sous forme d'entier
//                 Valeur TDS sous forme de float
//                 Valeur pH sous forme de float
//                 Valeur de la hauteur d'eau sous forme d'entier
//                 Etat du system (ON/OFF) sous forme de booleen
//    Renvoi : rien - Affichage
//

void affichage(int dataTemp, float dataTDS, float dataPH, int dataHauteurH2O, DateTime temps, boolean fonctionnement)
{
  LCD.clear ();
  LCD.setCursor (0,0);
  LCD.print(temps.hour(), DEC);
  LCD.print(':');
  LCD.print(temps.minute(), DEC);
  LCD.print(':');
  LCD.print(temps.second(), DEC);
  LCD.print(' ');
  LCD.print(temps.day(), DEC);
  LCD.print('/');
  LCD.print(temps.month(), DEC);
  LCD.print('/');
  LCD.print(temps.year(), DEC);

//  LCD.print ("HH:MM;SS JJ/MM/YYYY");         // Affichage DATE et Heure  ligne 1
  LCD.setCursor (0, 1); 
  LCD.print ("Temp : ");
  LCD.print (dataTemp, 1);
  LCD.print ("°");                       // Affichage TEMPERATURE ligne 2
  LCD.setCursor (0, 2);
  LCD.print ("pH    : ");
  LCD.print (dataPH, 1);                   // Affichage PH au debut de la ligne 3
  LCD.setCursor (10, 2);
  LCD.print ("TDS : ");
  LCD.print (dataTDS, 0);
  LCD.print (" ppm");                    // Affichage TDS au milieu de la ligne 3
  LCD.setCursor (0, 3);
  LCD.print ("-");
  LCD.print (dataHauteurH2O);
  LCD.print (" cm");                     // Affichage niveau manque d'eau
  LCD.setCursor (17, 3);
  if (fonctionnement) 
    LCD.print ("ON");
  else
    LCD.print ("OFF");                    // Affichage de l'etat en bas a droite
  LCD.display ();
}

//
//          Change l'etat de la variable Marche selon la position de l'interrupteur Marche/Arret
//    Parametres : aucun
//    Renvoi : rien
//
void changementEtat()
{
  Marche = !Marche;
}

Bonjour à toutes et à tous !

J'ai trouvé des erreurs. J'ai donc modifié le code...
Je suis sur qu'il y a encore des erreurs et des tas d'optimisations possibles...

Merci pour votre aide ..

#include "OneWire.h"
#include "DallasTemperature.h"
#include "Ultrasonic.h"         
#include "LiquidCrystal_I2C.h"
#include "Wire.h"
#include "DS3231.h"

// ************************************************
//
//          DECLARATION DES PINS A UTLISER
//
// ************************************************
//
// 0 -> Sonde pH    // PASSER A A2 - valeur ANALOGIQUE
// 2 -> interrupteur Marche Arret
// 3 -> PWM pour Lumiere (relais 100% / MOSFET PWM pour v2) - 12V
// 4 -> Relais chauffage - 220V
// 5 -> Relais Ventilateur - 12V
// 6 -> Relais pompe remplissage - 12V voir 6V après test
// 8 -> ECHO capteur Ultrason
// 9 -> TRIG capteur Ultrason
// A0 -> Sonde temperature DS18B20
// A1 -> Sonde TDS
// A2 -> Sonde pH
// A4 et A5 -> SDA et SCL pour Communication I2C pour ecran LCD et DS3231 
// vcc -> Alimentation 12V du circuit (Arduino et relais / mosfet)
// GND
// 5V -> Alimentation des capteurs et I2C
// 

//  Definition donnees de temps (Date et heure)
RTClib monTemps;

// définition du type d'ecran lcd 20 x 4 a l'adresse adresse I2c 0x27
LiquidCrystal_I2C LCD(0x27,20,4); 

// Definition capteur ultrason
Ultrasonic ultrasonic(9, 8); // Trig et Echo
int dist = 0;

// Definition sonde DS18B20
#define tempSensorPin A0
OneWire oneWire(tempSensorPin);
DallasTemperature ds(&oneWire);

// Definition bouton Marche/Arret
#define BoutonMarche 2
boolean Marche = 0;

// Definition sonde TDS
#define TdsSensorPin A1
#define VREF 5.0              // Voltage analogique de reference pour ADC
#define SCOUNT  20            // Nombre d'echantillons a relever
int analogBuffer[SCOUNT];     // Tableau de stockage des valeurs analogiiques relevees
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0, copyIndex = 0, temperature = 25;
float averageVoltage = 0, tdsValue = 0;

//Definition sonde PH
#define PHSensorPin A2           //sortie analogique du pHmetre vers l'entree analogique A2
unsigned long int avgValue;     //Valeur moyenne du sensor
float b, pH;
int buf[SCOUNT], temp;

// Definition relais
#define relaisLumiere 3
#define relaisChauffage 4
#define relaisVentilateur 5
#define relaisPompe 6

void setup()
{
  Serial.begin(115200);
  Wire.begin();                     // initialisation i2c
  ds.begin();                       // sonde temperature activée
  LCD.init();                       // initialisation de l'afficheur
  LCD.backlight();

  pinMode(TdsSensorPin, INPUT);     // sonde TDS sur pin TdsSensorPin - input
  pinMode(PHSensorPin, INPUT);      // sonde PH sur pin PHSensorPin - input
  pinMode(BoutonMarche, INPUT);     // initialisation du bouton Marche/Arret
  attachInterrupt(digitalPinToInterrupt(BoutonMarche), changementEtat, CHANGE);
  pinMode(relaisLumiere, OUTPUT);
  pinMode(relaisChauffage, OUTPUT);
  pinMode(relaisVentilateur, OUTPUT);
  pinMode(relaisPompe, OUTPUT);     // definition des pins des relais
}

void loop()
{
//      Acquisition des donnees : temperature, TDS, PH, hauteur d'eau, temps
    temperature = getTemper();
    tdsValue = getTDS(temperature);
    pH = getPH();
    dist = ultrasonic.read();
    DateTime now = monTemps.now();

//      Affichage des donnees : temperature, TDS, PH, Niveau d'eao, focntionnement
    affichage(temperature, tdsValue, pH, dist, now, Marche);

//      Traitement de la lumiere
// Version 1 : si l'heure est comprise entre 10 te 20h -> activation du relai 1 pour la lumiere
// Version 2 : activation de la lumiere via PWM et MOSFET pour lever et coucher de soleil

// Version 1 : Allumage à 10h extinction à 20h
  if ((now.hour()>=10) && (now.hour()<20))
    digitalWrite (relaisLumiere, HIGH);
  else
    digitalWrite(relaisLumiere, LOW);    

// Version 2 : A ecrire : Allumage 20% a 10h, +20% toutes les 10mn , -20% toutes les 10mn a partir de 19h jusqu'a extinction à 20h


//      Traitement Chauffage / Refroiddissement
// si temperature < 25°C -> activation relai 2 pour le chauffage
// si temperature > 28°C -> activation relai 3 pour la ventilation
  if (temperature <25)
    digitalWrite(relaisChauffage, HIGH);
  else
    digitalWrite(relaisChauffage, LOW);

   if (temperature >=28)
    digitalWrite(relaisVentilateur, HIGH);
  else
    digitalWrite(relaisVentilateur, LOW);

//      Traitement niveau d'eau
// si niveau d'eau > 3cm ET systeme en marche -> activation relai 4 pour la pompe de remplissage
  if ((dist > 3) && Marche) 
  {
    digitalWrite(relaisPompe, HIGH);      // Activation de la pompe
    while (dist >= 3)                     // Tant que le niveau d'eau est > 3cm
      {
        dist = ultrasonic.read();         // Mesure du niveau car la pompe tourne
      }
    digitalWrite(relaisPompe, LOW);       // Extinction de la pompe qd le niveau est atteint  
  }
}


// *******************************************
//
//          DECLARATION DES FONCTIONS
//
// *******************************************

//
//          Acquisition de la temperature
//    Parametre : aucun
//    Renvoi : temperature sous forme d'entier
//

int getTemper()
{
  ds.requestTemperatures();
  int t = ds.getTempCByIndex(0);
  return t;
}

//
//          Acquisition TDS
//    Parametre : T° sous forme d'entier
//    Renvoi : valeur TDS en ppm sous forme de float

//
float getTDS(int temp)
{
  static unsigned long analogSampleTimepoint = millis();
  if (millis() - analogSampleTimepoint > 40U)                       //Releve de la valeur analogique toutes les 40ms
  {
    analogSampleTimepoint = millis();
    analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin);    //Lecture de la valeur analogique et stockage dans le buffer
    analogBufferIndex++;
    if (analogBufferIndex == SCOUNT)
      analogBufferIndex = 0;
  }
  static unsigned long printTimepoint = millis();
  if (millis() - printTimepoint > 800U)
  {
    printTimepoint = millis();
    for (copyIndex = 0; copyIndex < SCOUNT; copyIndex++)
      analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
    averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // Retourne la valeur analogique moyenne du tableau de valeur
    float compensationCoefficient = 1.0 + 0.02 * (temp - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
    float compensationVolatge = averageVoltage / compensationCoefficient; //temperature compensation
    tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5; // Convertit la valeur en TDS (en ppm)

    return tdsValue;
  }
}

//
//          Acquisition pH
//    Parametre : aucun
//    Renvoi : valeur du PH sous forme de float
//

float getPH()
{
 for(int i=0; i<SCOUNT; i++)                 // Preleve 1 echantillons toutes les 10 ms
 { 
   buf[i]=analogRead(PHSensorPin);
   delay(10);
 }
  avgValue = getMedianNum (buf, SCOUNT);        // Retourne la valeur mayenne
  float phValue = (float)avgValue*5.0/1024;
  phValue = 3.5*phValue;
  return phValue; 
}

//
//          Renvoie la valeur median d'un tableau d'entier
//    Parametres : Tableau de valeurs sous forme d'entier
//                 Nombre de valeurs dans le tableau sous forme d'entier
//    Renvoi : Valeur median du tableau sous forme d'entier
//

int getMedianNum(int bArray[], int iFilterLen)
{
  int bTab[iFilterLen];
  for (byte i = 0; i < iFilterLen; i++)
    bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++)
  {
    for (i = 0; i < iFilterLen - j - 1; i++)
    {
      if (bTab[i] > bTab[i + 1])
      {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0)
    bTemp = bTab[(iFilterLen - 1) / 2];
  else
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  return bTemp;
}

//
//          Affichage sur ecran LCD
//    Parametres : Valeur de la temperature sous forme d'entier
//                 Valeur TDS sous forme de float
//                 Valeur pH sous forme de float
//                 Valeur de la hauteur d'eau sous forme d'entier
//                 Etat du system (ON/OFF) sous forme de booleen
//    Renvoi : rien - Affichage
//

void affichage(int dataTemp, float dataTDS, float dataPH, int dataHauteurH2O, DateTime temps, boolean fonctionnement)
{
  LCD.clear ();
  LCD.setCursor (0,0);
  LCD.print(temps.hour(), DEC);
  LCD.print(':');
  LCD.print(temps.minute(), DEC);
  LCD.print(':');
  LCD.print(temps.second(), DEC);
  LCD.print(' ');
  LCD.print(temps.day(), DEC);
  LCD.print('/');
  LCD.print(temps.month(), DEC);
  LCD.print('/');
  LCD.print(temps.year(), DEC);

//  LCD.print ("HH:MM;SS JJ/MM/YYYY");         // Affichage DATE et Heure  ligne 1
  LCD.setCursor (0, 1); 
  LCD.print ("Temp : ");
  LCD.print (dataTemp, 1);
  LCD.print ("°");                       // Affichage TEMPERATURE ligne 2
  LCD.setCursor (0, 2);
  LCD.print ("pH    : ");
  LCD.print (dataPH, 1);                   // Affichage PH au debut de la ligne 3
  LCD.setCursor (10, 2);
  LCD.print ("TDS : ");
  LCD.print (dataTDS, 0);
  LCD.print (" ppm");                    // Affichage TDS au milieu de la ligne 3
  LCD.setCursor (0, 3);
  LCD.print ("-");
  LCD.print (dataHauteurH2O);
  LCD.print (" cm");                     // Affichage niveau manque d'eau
  LCD.setCursor (17, 3);
  if (fonctionnement) 
    LCD.print ("ON");
  else
    LCD.print ("OFF");                    // Affichage de l'etat en bas a droite
  LCD.display ();
}

//
//          Change l'etat de la variable Marche selon la position de l'interrupteur Marche/Arret
//    Parametres : aucun
//    Renvoi : rien
//
void changementEtat()
{
  Marche = !Marche;
}

Bonjour,

Bienvenue à toi sur ce forum.
...
Bonne continuation.

OUPS:

Tu viens de modifier tes affectations d'entrées sorties 2mn avant que j'envoies mon message!

bonjour,
le code suivant n'est pas vraiment préconiser

if (temperature <25)
    digitalWrite(relaisChauffage, HIGH);
  else
    digitalWrite(relaisChauffage, LOW);

   if (temperature >=28)
    digitalWrite(relaisVentilateur, HIGH);
  else
    digitalWrite(relaisVentilateur, LOW);

En général on préfère faire une hystérésis, c'est à dire que la valeur d'activation, n'est pas strictement exact à la valeur de désactivation.
ce qui donne quelque chose comme ça:

  if (temperature <25)
    digitalWrite(relaisChauffage, HIGH);
  else if (temperature > 26)
    digitalWrite(relaisChauffage, LOW);

   if (temperature >=28)
    digitalWrite(relaisVentilateur, HIGH);
  else if (temperature < 27)
    digitalWrite(relaisVentilateur, LOW);

Personnellement ce genre de constante, je préfère les mettre dans une variable constante en début de code, même si elles sont utilisées qu'une seul fois.
Car on peut changer la configuration rapidement en voyant tout les paramètres en même temps.

Merci @jef59
Merci @terwal ,

On va dire que pour l'instant, je me suis beaucoup plus attaché aux entrées et à récupérer les bonnes infos, en fractionnant beaucoup le programme, histoire de debugger rapidement si besoin.

Le traitement de ces données et les sorties sont pour le moment très sommaire.

Merci pour l'idée et le code.

ZiEg

Oui après c'est une bonne pratique, il est aussi possible que ton code ne bagote pas trop en fonction de l'inertie du système.

Oui un aquarium est très inerte, mais la répétabilité de la mesure par le DS18B20 peut aussi faire en sorte que lorsque la température est très proche de la consigne, le capteur renvoie une température légèrement supérieure ou inférieure à celle-ci, et que la valeur renvoyée change plusieurs fois par seconde.
Le chauffage (ou le ventilateur) risque d'être activé ou coupé pour rien, et de plus le bruit du relais risque d'être agaçant.
L'hystérésis est indispensable.

On peut également faire la mesure seulement toutes 5 minutes ou plus. La température n'est pas une grandeur qui varie rapidement.

@hbachetti je pense avoir un peu amorti la non répétabilité des sondes pH et TDS (mais pas de température).
Merci pour la piste, je vais aussi lisser la température sur une vingtaine de mesures...
La piste de la vérification toute les 5 mns est aussi très intéressante et je n'y avait clairement pas pensé
ZiEg

5 minutes est peut être un peu trop, tout dépend de l'élévation de température engendrée par le chauffage pendant ces 5 minutes sans mesure. Cela dépend de la taille du bac et de la puissance du chauffage.

A propos : l'AQUABOUNS , l'automate pour aquarium récifal
C'est un gros projet, mais il y a peut être des idées à reprendre.

Pour avoir testé étant jeune ce n'est pas intéressant c'est la seule solution pour un petit aquarium. Sans hystérésis le système entre en oscillation autour de la valeur de consigne. Avec une mesure toute les 5 minutes il reste au moins allumé ou éteint pendant 5 minutes.
J'utilise un hystérésis qui me permet une stabilité nettement supérieure au degré sur mon aquarium en utilisant une troisième variable. Je supprime la virgule pour le calcul d'où le 250 pour 0,25°C

ControlTemp::ControlTemp()
    :a(0),b(0),c(0)
{

}

int ControlTemp:: gestHeater(double tempEau, double tempCons) //hysteresis
{
    if (tempEau < tempCons-250)
    {
        a =1;
        b =0;
        c =0;
    }
    if (tempCons-250<tempEau && tempEau< tempCons+250)
    {
        b =1;
        c =0;
    }
    if (tempEau > (tempCons+250))
    {
        a =0;
        b =0;
        c =1;
    }
    else
    {
       c =0;
    }

    if (a ==1 || (a==1&&b==1))
    {
        //GPIO on
        return 3;
    }

    else
    {
        return 5;
        //GPIO off
    }
}

Merci @le_viking .
Tu pourrais m en dire un peu plus sur tes variables a, b et c. Je suis quasi d avoir compris l utilité de a et c mais b.....
En même temps devant un écran qui ne soit pas le téléphone, ça aiderait peut-être à comprendre.

je dis peut être une grosse connerie, mais dans ta condition b==1 n'est jamais évalué :slight_smile:

@terwal merci
Non seulement tu ne dis pas de connerie mais c'est même pire puisque c ne sert à rien non plus!
Je devais avoir une idée en tête quand je l'ai fait pour avoir créé une classe. C'est fonctionnel mais pas rationnel du tout, j'ai visiblement coupé le cheminement avec une condition et un return car il n'y a aucune raison d'utiliser plusieurs variables. Je vais essayé de retrouver pourquoi sinon je simplifierais ce code

@ziegfried Le principe d'un hystérésis est de faire fonctionner le chauffage dans une plage donnée par ex: pour une consigne à 24°C il va chauffer de 23.75 jusqu'à 24.25 mais ne pas redémarrer tant que la température n'est pas redescendue à 23.75. Ce que évite une zone instable marche /arrêt qui peut être très rapide sur un petit volume d'eau

je dis peut être une grosse connerie, mais dans ta condition b==1 n'est jamais évalué :slight_smile:

OK.... On est sur la même longueur d'onde....

J'ai retrouvé dans mes nombreuses notes. J'avais prévu de faire un avertisseur de tendance . Le but étant surtout d'afficher la tendance à l'augmentation de la température sans chauffage. Je vais surveiller cet été et si j'observe des fortes augmentations je reprendrais la programmation.
Je retiens juste qu'on ne prend jamais assez de notes

Salut @ziegfried
Concernant le ph, tu utilise un module analogique, comme ceux que j'utilisait sur l'aquabouns V1.
Le probleme que j'ai rencontré avec ceux la (j'ai tester 5 models different du premier prix au plus couteux) et j'ai constater toujours le meme probleme : mesure erroné et instable au bout de quelque semaine.
C'est pour cette raison que je suis partie sur des modules Atlas scientifique. certe le prix n'est pas du tout le meme, mais depuis 3 ans, je n'ai aucun probleme (et les autre utilisateur ne m'ont rien remonté non plus)
Si tu arrive a avoir une solution efficace et stable, je suis preneur. de meme pour la conductivité.

@djbouns, c est effectivement le problème que je crains. Cependant étant en eau douce .. je croise les doigts.

Le pH ne devant, théoriquement, que très peu varier, un peu comme la conductivité d'ailleurs, je me demande si un test toutes les 6 voire même 12 heures ne serait pas suffisant.
Tu en penses quoi ?

Le pH varie un peu en suivant le cycle de l’éclairage.
La conductivité ne doit quasiment pas bouger.
Cela est valable en eau douce comme en eau de mer pour ces deux paramètres.
Prends une mesure toutes les minutes où toutes les six heures ne réglera pas le problème des mesures erronées.

Bon j'ai mis des hystéresis pour le chauffage et la descente en température ...

  if (temperature >= (tempCons + tempOffset)) {
    digitalWrite(relaisChauffage, LOW);
  }
  if (temperature <= (tempCons - tempOffset)) {
    digitalWrite(relaisChauffage, HIGH);
  }
  
  if (temperature >=(tempCons+3))
    digitalWrite(relaisVentilateur, HIGH);
  if (temperature <= (tempCons - tempOffset))
    digitalWrite(relaisVentilateur, LOW);

Maintenant, @djbouns , la variabilité des mesures pH et conductivité, es tu sur que ca provienne des cartes électroniques ? Ne serait ce pas du a la qualité des sondes ?

De plus, TDS et pH ne devant, théoriquement, que peu varier, je pense vraiment a ne faire une mesure que toutes les heures

Oui, j'en suis sur car j'ai tester également plusieurs qualité de sonde dont des marques.
D'ailleurs, certaines on tourner avec le module Atlas scientifique sans qu'il n'y est de probleme.