RESOLU <> Besoin d'aide pour debugger un sketch

Bonjour,

Je travaille sur un programme devant gérer "automatiquement" une serre.

Je découvre Arduino et me suis dit que ce serait un bon exercice (un peu ambitieux je l'admets).

Je me base sur un DHT11 pour l’hygrométrie et la température.

En fonction des valeurs lues et de consignes pré définies, Arduino actionne une platine 4 relais qui commandent :

  • la ventilation.
  • l'arrosage.
  • l'éclairage.
  • l’hygrométrie.

Tout ce petit monde est "piloté/suivi" via un interface Android réalisé sous MIT APP Inventor2.

Voila pour le projet.

Le matériel :

  • Arduino uno
  • Bread board 4 relais modules
  • DTH11 (bread bord capteur température/hygrométrie)
  • module bluetooth HC06.
  • module RTC tiny rtc.

Au démarrage tout se passe bien, lecture et affichage température/hygrométrie mais à la deuxième boucle, une erreur de lecture du capteur apparaît et cette erreur persiste.

J'ai beau relire le programme, je ne trouves pas.

Merci de ne se concentrer (pour le moment) que sur cette erreur.
(sauf si bien sur elle est la conséquence d'autres boulettes que j'aurais fait dans le code)

Une fois ce problème réglé, je me pencherai très volontiers sur l'art de la programmation et l'optimisation du code.

Le code est surement pourri et Il peu très certainement être optimisé.
(je ne suis pas programmeur de formation mais je ne demande qu'a apprendre).

D'avance merci.

PS:J'ai cherché sur la toile un debugger (avance pas à pas type VBA) pour analyser le déroulement du code mais sans succès.

si quelqu'un connait, je suis prenneur.

hello

:slight_smile: il ne manque rien ?

si le code arrive mais :

1 il est trop gros (13000 carracteres) donc je le splitte en 2 messages
2 j'ai le droit a un message toutes les 5 minutes ???

Donc j'attends un peu.

Dites donc vous etes des rapides ici :slight_smile: :slight_smile:

Le programme (partie 1/2) :

 * Connect to Android via serial Bluetooth and demonstrare the use of interrupt

 Un module bluetooth serie est utilisé pour créer une connexion a un périphérique Android via une application MIT AppInentor2.
 Arduino attend des commandes pour allumer des relais et donner leurs status. De plus, une interruption lui fait verifier la temperature et l'humidité d'un DHT11.
 Si la temperature et/ou l'humidité sont plus haute que leur consigne, Arduino ouvre les relais concernés.
 Toutes les n secondes parametrable via l'application(via la commande MSG) un rapport est envoyé a l'application.
 Une simple structure de commande permet a l'application d'envoyer des parametres et valeurs a l'arduino et inversement.

 Le circuit:
 * Yellow led on Pin 9 via relais with 220 Ohm resistor in series
 * Green led  on Pin 10 via relais with 220 Ohm resistor in series
 * Red led  on Pin 11 via relais with 220 Ohm resistor in series
 * Bleue led  on Pin 12 via relais with 220 Ohm resistor in series
 * the built-in led on Pin 13 is also used

 * DHT11 connected to 5V, Pin digital 8 and Gnd
 * HC-06 Bluetooth Wireless Serial Port Module (slave) connected as follows:
 VCC <--> 5V
 GND <--> GND
 TXD <--> Pin 0 (Rx)
 RXD <--> Pin 1 (Tx)
 The Bluetooth module may interfere with PC to Arduino communication: disconnect VCC when programming the board

 Adapté par mo@ sur la
 base d'une création de 2014 par
 Paolo Mosconi

 This example code is in the public domain.

 // Serial Parameters: COM11 9600 8 N 1
// \r or \n to end command line
// Bluetooth is on Pin 8 & 1 @ 9600 speed

// Command structure
// CMD VENTIL|HYGRO|POMPE|LUM=ON|OFF
// CMD TMAX|HMIN|GROW=value
// CMD MSG=value
// CMD STATUS
*/

#include <FlexiTimer2.h>
#include "DHT.h"
#define DHTPIN 8
#define DHTTYPE DHT11
#include <Wire.h>
#include "RTClib.h"
RTC_DS1307 RTC;
DHT dht(DHTPIN, DHTTYPE);
float maxTemp = 25.0; // Temperature maximum
float minTemp = 20.0; // Temperature maximum
float maxHumid = 80.0; // Humiditée minimum
float minHumid = 32.0; // Humiditée minimum
float temperature = 0.0; // lecture temperature DTH11
float humidite = 0.0; // Lecture humiditée DHT11
float maxTempSensor = (float) (maxTemp); // à definir
float minTempSensor = (float) (minTemp); // à definir
float minHumidSensor = (float) (minHumid); // a definir
float maxHumidSensor = (float) (maxHumid); // a definir
volatile float tempVal;
volatile float humidVal;
volatile int TMar=0;
volatile int CMar=0;
volatile int Grow=0;
volatile int Msg=15;
volatile boolean flagcompteur=false;
volatile boolean tempHigh = false;
volatile boolean humidLow = false;
volatile boolean statusReportT = false;
volatile boolean statusReportH = false;
String inputString = "";
String command = "";
String value = "";
boolean stringComplete = false;
char inChar;
const int led1Pin = 9; //  Yellow + Relais 1    => LUMIERE
const int led2Pin = 10; // Green + Relais 2     => POMPE BAIN
const int led3Pin = 11; // Red + Relais 3       => VENTILATION
const int led4Pin = 12; // Bleue + Relais 4     => HYGRO
void setup(){
  Serial.begin(9600);
  dht.begin();
  Wire.begin();
  RTC.begin();
  
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(led3Pin, OUTPUT);
  pinMode(led4Pin, OUTPUT);
  
  digitalWrite(led1Pin, HIGH);
  digitalWrite(led2Pin, HIGH);
  digitalWrite(led3Pin, HIGH);
  digitalWrite(led4Pin, HIGH);
  
  inputString.reserve(50);
  command.reserve(50);
  value.reserve(50);
  // following line sets the RTC to the date & time this sketch was compiled
  RTC.adjust(DateTime(__DATE__, __TIME__));
  
  action(); //initialise lecture DHT11 et initialise les variables TempVal et HumidVal et verifie que les valeures lues sont exploitables
  report();//affiche les valeurs TempVal et HumidVal et les compares a leurs consignes.
  
  FlexiTimer2::set(Msg*1000, action); //recupere les valeurs du DHT toutes les 5 secondes et ouvre ou ferme le relais concerné si necessaire.
  FlexiTimer2::start();
  }
void loop(){     // enchainement des differentes fonctions conctituant le programme
  serialEvent();
// "ecoute" le port serie (entrée de données venant du peripherique Android)
    //Serial.println("VERIF ENTREES SERIAL");
    //delay(1000);
  //CheckVal();
    //Serial.println("VERIF ENTREES VALIDES");
    //delay(1000);    // verifie que les valeurs lues sur le DHT sont exploitables.
  Check();      // traite les données recues de la console Android et mets a jour les variables du programme.
      //Serial.println("VERIF BLUETOOTH");
    //delay(1000);
}
void action(){
    Serial.println("RELEVE DHT11 ACTION");
    ReadDht11();
    delay(1000);
  if (tempVal > maxTemp) {
    digitalWrite(led3Pin, LOW);
    tempHigh = true;
    statusReportT =true;
  }
  else {
    digitalWrite(led3Pin, HIGH);
    tempHigh = false;
    statusReportT =false;
  }
  if (humidVal < minHumid) {
    digitalWrite(led4Pin, LOW);
    humidLow = true;
    statusReportH =true;
      }
  else {
    digitalWrite(led4Pin, HIGH);
    humidLow = false;
    statusReportH =false;
  }
}
void report(){
    Serial.print("INTERVAL MESSAGE = ");
    //delay(1000);
    Serial.println (Msg);
    statusReportT =true;
    statusReportH =true;
    Report_DHT ();
}
/*
  SerialEvent occurs whenever a new data comes in the
 hardware serial RX.  This routine is run between each
 time loop() runs, so using delay inside loop can delay
 response.  Multiple bytes of data may be available.
 */
void serialEvent() {

  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    // if the incoming character is a newline or a carriage return, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n' || inChar == '\r') {
       stringComplete = true;
     }
   }
  }
void CheckVal(){ // verifie que les valeurs lues sur le DHT sont exploitables.
  if (isnan(humidVal) || isnan(tempVal))
        Serial.println( "Lecture du capteur impossible !");
}

Le programme (partie 2/2) :

 void Check(){
  float intValue = 0;
  if (stringComplete) {
    Serial.print(inputString);
    boolean stringOK = false;
        if (inputString.startsWith("CMD ")) {
            inputString = inputString.substring(4);
            int pos = inputString.indexOf('=');
                if (pos > -1) {
                command = inputString.substring(0, pos);
                value = inputString.substring(pos+1, inputString.length()-1);  // extract command up to \n exluded
                //Serial.println(command);
                //Serial.println(value);
                    if (command.equals("VENTIL")) { // RED=ON|OFF
                        value.equals("ON") ? digitalWrite(led3Pin, LOW) : digitalWrite(led3Pin, HIGH);
                        stringOK = true;
                    }
                    else if (command.equals("HYGRO")) { // BLEUE=ON|OFF
                        value.equals("ON") ? digitalWrite(led4Pin, LOW) : digitalWrite(led4Pin, HIGH);
                        stringOK = true;
                    }
                    else if (command.equals("POMPE")) { // GREEN=ON|OFF
                        value.equals("ON") ? digitalWrite(led2Pin, LOW) : digitalWrite(led2Pin, HIGH);
                        stringOK = true;
                    }
                    else if (command.equals("LUM")) { // YELLOW=ON|OFF
                        value.equals("ON") ? digitalWrite(led1Pin, LOW) : digitalWrite(led1Pin, HIGH);
                        stringOK = true;
                    }
                    else if (command.equals("TMAX")) { // Consigne de Temperature MAX
                        intValue = value.toInt();
                            if (intValue > 0) {
                            maxTemp = (float) intValue;
                            stringOK = true;
                            }
                    }
                    else if (command.equals("HMIN")) { // Consigne d'humidité min
                        intValue = value.toInt();
                            if (intValue > 0) {
                            minHumid = (float) intValue;
                            stringOK = true;
                            }
                    }
                    else if (command.equals("GROW")) { // Grow=value (Cycle de lumiere 8 ou 16 par 24H)
                        intValue = value.toInt();
                            if (intValue > 0) {
                            Grow = intValue;
                            stringOK = true;
                            }
                    }
                    else if (command.equals("MSG")) { // MSG=temps entre chque messages
                        flagcompteur=true;
                        intValue = value.toInt();
                            if (intValue > 0) {
                            Msg = intValue;
                            stringOK = true;
                                if (flagcompteur){
                                FlexiTimer2::set((Msg*1000), report); // lance un rapport du systeme toutes maxSeconds
                                FlexiTimer2::start();
                                flagcompteur=false;
                                }
                            }
                    }
                    else if (command.equals("CclMar")) {
                        intValue = value.toInt();
                            if (intValue > 0) {
                                CMar = intValue;
                                stringOK = true;
                            }
                        }
                    else if (command.equals("TmpMar")) {
                        intValue = value.toInt();
                        if (intValue > 0) {
                            TMar = intValue;
                            stringOK = true;
                        }
                    }
                } // END pos > -1
        } // END inputString.startsWith("CMD ")
    else if (inputString.startsWith("STATUS")) {
      Serial.print("CONSIGNE Temperature: ");
      Serial.print(maxTemp);
      Serial.print(" CAPTEUR TEMPERATURE = ");
      Serial.println(tempVal);
      Serial.print("CONSIGNE Humidite : ");
      Serial.print(minHumid);
      Serial.print(" CAPTEUR HUMIDITE = ");
      Serial.println(humidVal);
      Serial.print("STATUS VENTIL  = ");
      digitalRead(led3Pin)== 1 ? Serial.print("OFF") : Serial.print("ON");
      Serial.print("__STATUS POMPE = ");
      digitalRead(led2Pin)== 1 ? Serial.println("OFF") : Serial.println("ON");
      Serial.print("STATUS LUMIERE = ");
      digitalRead(led1Pin)== 1 ? Serial.print("OFF") : Serial.print("ON");
      Serial.print("__STATUS HYGRO = ");
      digitalRead(led4Pin)== 1 ? Serial.println("OFF") : Serial.println("ON");
      stringOK = true;
    }// END inputString.startsWith("STATUS")
    stringOK ? Serial.println("Command Executed") : Serial.println("Invalid Command");
    // clear the string for next iteration
    inputString = "";
    stringComplete = false;
  } // END stringComplete

}// END CHECK
void Time (){
  DateTime now = RTC.now();
  Serial.print(now.day(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.year(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.println(now.second(), DEC);
}
void ReadDht11(){
  tempVal = (float)dht.readTemperature();
  humidVal = (float)dht.readHumidity();
  CheckVal();
}
void MsgTempHaute(){
  Serial.println("ALERTE TEMPERATURE HAUTE");
}
void MsgTempOk(){
  Serial.println("TEMPERATURE OK");
}
void MsgHumBasse(){
  Serial.println("ALERTE HUMIDITE BASSE");
}
void MsgHumOk(){
  Serial.println("HUMIDITE OK");
}
void Report_DHT(){
if (statusReportT) {
    Time();
        if (tempHigh) {
            MsgTempHaute();
            Serial.print("TEMPERATURE ");
            Serial.print(tempVal);
            Serial.print (" > ");
            Serial.print("CONSIGNE ");
            Serial.println(maxTemp);
            tempHigh=false;
            statusReportT=false;
        }
        else{
            MsgTempOk();
            Serial.print("TEMPERATURE ");
            Serial.print(tempVal);
            Serial.print (" < ");
            Serial.print("CONSIGNE ");
            Serial.println(maxTemp);
            tempHigh=false;
            statusReportT = false;
            Serial.println(" ");
        }
        tempVal=0;
    }//END statusReport T
    if (statusReportH) {
        Time();
        if  (humidLow){
            MsgHumBasse();
            Serial.print("HUMIDITE ");
            Serial.print(humidVal);
            Serial.print (" < ");
            Serial.print ("CONSIGNE ");
            Serial.println(minHumid);
            statusReportH=false;
            humidLow = false;
    }
        else{
            MsgHumOk();
            Serial.print("HUMIDITE ");
            Serial.print(humidVal);
            Serial.print (" > ");
            Serial.print ("CONSIGNE ");
            Serial.println(minHumid);
            statusReportH=false;
            humidLow = false;
        }
            humidVal=0;
  }//END Statutsreport H
  }

il semble que ton sketch vienne du net.
en principe un gars qui poste un sketch est sensé mettre un sketch qui tourne bien.

cependant, dans ta loop, tu appelles serialEvent() .
cela ne se fait pas, il faut voir cette fonction comme une isr.
s'il y a une communication qui arrive sur le serial, cette fonction est exécutée, elle lit le tampon et mets la variable stringCpmplete= true.

et toi dans ta loop tu devrais uniquement tester si la variable est "true".
c'est d'ailleurs ce que tu fais dans la fonction check que tu appelles dans ta loop.
en résumé, ta loop devrait simplement appeler la fonction check.

je n'ai pas regardé le reste du code.
es tu sur de ton application sur l'android ?

as tu testé le capteur et ses branchement avec un sketch de la librairie ?

Voici comment j'ai procédé afin de suivre (essayer) tes recommandations:

Pour la partie LOOP:

Je n'appelle que la fonction Check.

void loop(){     // enchainement des differentes fonctions conctituantes du programme
  //serialEvent();
// "ecoute" le port serie (entrée de données venant du peripherique Android)
    //Serial.println("VERIF ENTREES SERIAL");
    //delay(1000);
  //CheckVal();
    //Serial.println("VERIF ENTREES VALIDES");
    //delay(1000);    // verifie que les valeurs lues sur le DHT sont exploitables.
  Check();      // traite les données recues de la console Android et mets a jour les variables du programme.
      //Serial.println("VERIF BLUETOOTH");
    //delay(1000);
}

Et dans la partie Check j'integre ce qu'il y avait dans le serialEvent :

void Check(){
  float intValue = 0;
  while (Serial.available()) {
    char inChar = (char)Serial.read();
    inputString += inChar;
    // if the incoming character is a newline or a carriage return, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n' || inChar == '\r') {
       stringComplete = true;
     }
   }
.....

Cela ne règle malheureusement pas mon problème.

Première boucle OK mais la suivante (15 secondes après... ==> KO)

en pièce jointe capture du moniteur série.

EDIT :

Ci-dessous un lien vers les tutos Arduino relatif a l'utilisation du SerialEvent.

A priori il est utilisé dans la Loop (l'appel de la foncion) tout comme je l'ai fait dans mon code initial :confused:

SerialEvent

Capture.JPG

zefram-28:
Voici comment j'ai procédé afin de suivre (essayer) tes recommandations:

Bonsoir
si ton code ne tient pas en PJ sur le forum
passe par un tiers hébergeur (ça ne garantit pas la pérennité du lien )
mais ça evite des "contorsions" pour remettre en forme/verifier, ce qui est une manip souvent rédhibitoire pour qq'un qui "passe en coup de vent"

perso j'utilise depuis longtemps ça

pour du petit fichier ce n'est pas trop lourd, ni trop invasif et rapide
mais il y en a d'autres

bien vu et merci pour le conseil :sunglasses:

Ci-après le lien vers le fichier (complet) suite aux indications de dfgh.

CODE PROJET

Et dans la partie Check j'integre ce qu'il y avait dans le serialEvent :

Je pense que tu as une mauvaise compréhension de l'utilisation de serialEvent().
On ne fait pas d'appel à serialEvent(), cette fonction est appelée automatiquement à chaque itération de loop et assure la gestion du lien série en tâche de fond. Et ce n'est pas à strictement parler une ISR puisqu'elle est appelée explicitement et non en réponse à une interruption.

Edit: à la réflexion, comme c'est une fonction "normale" rien n'empêche de faire des appels directs à serialEvent(). Mais cela à peu de sens car on risque de l'appeler alors qu'il n'y a pas de données à traiter.

fdufnews:
Je pense que tu as une mauvaise compréhension de l'utilisation de serialEvent().
...

Edit: à la réflexion, comme c'est une fonction "normale" rien n'empêche de faire des appels directs à serialEvent(). Mais cela à peu de sens car on risque de l'appeler alors qu'il n'y a pas de données à traiter.

Donc a priori mon problème ne viens pas de là.

mais alors pourquoi ce fonctionnement erratique???

as tu testé le fonctionnement ( la lecture du capteur avec un sketch d'exemple de la librairie?

le debugage commence par là

Il y a un truc qui me trouble dans ton code.
La fonction action() est appelée par FlexiTimer2. J'en déduit, peut-être à tort, que cela est fait sous interruption. Or dans action() il y a un appel à delay() et delay() ne fonctionne pas sous interruption.

Dans tous les cas, l'usage de delay() lorsque cela est possible, et c'est souvent le cas, doit être proscrit. Il est préférable d'utiliser millis() en association avec une petite machine à état pour gérer les actions liées au temps.

Si action() est bien appelée sous interruption, je te conseille de ne pas utiliser de Serial.print() car cela peut aussi planter Serial qui fonctionne aussi sous IT. De plus, comme le timer peut tomber n'importe quand il peut y avoir des effets de bords indésirables. Dans les fonctions appelées sous interruption, au lieu de faire des Serial.print(), il vaut mieux remonter un octet d'état qui sera exploité dans la boucle principal qui se chargera d'émettre le Serial.print().

dfgh:
as tu testé le fonctionnement ( la lecture du capteur avec un sketch d'exemple de la librairie?

le debugage commence par là

Bonjour,

Oui, chaque éléments à été testé avec la librairie d'exemple et fonctionne parfaitement.

Zefram

fdufnews:
Il y a un truc qui me trouble dans ton code.
La fonction action() est appelée par FlexiTimer2. J'en déduit, peut-être à tort, que cela est fait sous interruption. Or dans action() il y a un appel à delay() et delay() ne fonctionne pas sous interruption.

Je vais virer ce delay qui n’était là que pour laisser du temps a la lecture du capteur (mais pas indispensable).

fdufnews:
Si action() est bien appelée sous interruption, je te conseille de ne pas utiliser de Serial.print() car cela peut aussi planter Serial qui fonctionne aussi sous IT. De plus, comme le timer peut tomber n'importe quand il peut y avoir des effets de bords indésirables. Dans les fonctions appelées sous interruption, au lieu de faire des Serial.print(), il vaut mieux remonter un octet d'état qui sera exploité dans la boucle principal qui se chargera d'émettre le Serial.print().

Action est bien appellé par Flexitimer2 : l'interuption les Xsecondes (parametrable).
Action ne fait que lire le capteur.
==> pas d'appel SerialEvent en cours d'interupt.

le serialEvent (exterieur au loop) est appelé par la fonction "Check" qui est dans le loop.

J’espère avoir été clair.

A priori je ne rentre pas dans les cas de figure "a éviter" que tu me décris.

Zefram.

zefram-28:
A priori je ne rentre pas dans les cas de figure "a éviter" que tu me décris.

Je parle du Serial.print()

Oups,

Distrait dans ma lecture.

Désolé.

peux tu tester en mettant des // devant les 4 lignes qui initialisent flexi2

FlexiTimer2::set(Msg * 1000, action); //recupere les valeurs du DHT toutes les 15 secondes et ouvre ou ferme le relais concerné si necessaire.
FlexiTimer2::start();

et

FlexiTimer2::set((Msg * 1000), report); // lance un rapport du systeme toutes maxSeconds
FlexiTimer2::start();

OK,

En invalidant les lignes FlexiTime2 je n'ai plus de plantage mais je n'ai plus les retours me disant ou en sont les valeurs réelles par rapport aux valeurs cibles.

Je n'ai donc plus le retour Arduino vers Android de ce qui se passe dans la serre.

Sais tu comment récupérer cette information?

Voir le lien suivant : MsTimer2 and FlexiTimer2 Arduino Libraries, Run a Function At Regular Intervals
il y a des remarques sur l'utilisation de la librairie du au fait que la fonction est appelée sous interruption.