ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

gpio c++

Thu Sep 21, 2023 11:54 am

salut as tous , j'aimerais savoir pourquoi on ajoute une bibliotheque au programme pour la gestion des GPIO on peut pas les gerers simplement ??? merci as vous je suis nouveau sur la framboise

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Thu Sep 21, 2023 1:24 pm

Bonjour, Camarade ludorapsberrypi,
Votre question est une bonne question, ce d'autant plus que je peux investir un peu de temps ce week end pour jouer avec les pattes de mon RapsberryPi tout neuf.
Cependant, j'aimerais savoir quelle bibliotheque vous souhaitez utiliser.http://abyz.me.uk/rpi/pigpio/ me semble très bien (bonne documentation, couvre aussi bien les GPIO en mode "basique" que les fonctions spéciales (un GPIO peut être utilisé comme une portion de bus I2C; un des buts d'une bibliothèque est d'éviter que l'on mélange toutes ces fonctions, ce qui peut être calamiteux) , mais si on parle d'une autre biblothèque, ça peut faire des malentendus.

On ne peut aps gerer simplement des GPIO (risque de semer un beau désordre si deux tâches accèdent simultanément à la même patte avec des fonctions différentes). Même l'arduino ne gère pas simplement (alors que l'arduino est souvent assez limité, réduisant les risques de désordre)

Avant de jouer avec les petites pattes fragiles de mon RPi tout neuf, je relirai avec profit /viewtopic.php?f=91&t=83372

Edité : il ne serait pas idiot non plus de confirmer que vous avez l'intention d'utiliser un RapsberryPi (typiquement sous GNUlinux) et non un picoPi (beaucoup moins cher; peut se programmer comme un Arduino -mais est trois fois moins cher qu'un arduino Uno- ; il arrive que des gens tout à fait estimables les confondent -les cartes bleues ne les confondent pas-).
Edité 2: savoir quelle bibliothèque vous souhaitez utiliser est une bonne question, https://elinux.org/RPi_GPIO_Code_Sample ... 35_library en a recensé 5 ou 6, de biblothèques, plus que je ne le craignais (mais pigpio a l'air d'avoir de beaux exemples...)

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Thu Sep 21, 2023 3:12 pm

merci a toi , je vais lire tous cela , je debute sur le langage arduino (C) donc tirage de cheveux que j'ai deja plus et des nuit blanche bref .
en faite j'ai creer un programme sur arduino mega qui géreras ma clim reversible et le fait que le raspberry a deja le wifi ethernet et plusieur possibilité ; donc je me lance dessu .

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Tue Oct 03, 2023 5:36 pm

Bonjour, désolé de faire mon rabat joie:
si j'ai bien compris, vous avez déjà un programme en C++ aarduino qui fonctionne (enfin, j'espère). Pourquoi vouloir le porter sur un RPi (qui est plus cher) alors que les niveaux de tension ne sont pas forcement compatibles (le RPi peut être endommagé par des tenssions entre 3.3 et 5v , dont un Arduino UNO ou Mega se régale... vous risquez de compliquer l'electronique avec des adaptations)
Certes l'Arduino ne gère pas le ouifi ni le nain ternet, alors que les RPi s'en accomodent très bien).
Mais il serait assez sain, à mon avis, que vous soyiez bien sûr que votre logiciel sur Arduino marche très bien avant d'envisager de porter (si vous devez porter quelque chose de mal débuggué, vous aurez le gros souci de le debugguer, tout en vous formant à un environnement nouveau: ça a l'air fatiguant).
Il y a même une fonctionnalité (conversion Analog Digitale) que le RPi ne gère pas sans cicuit d'interface exterieur; si, par malheur, vous mesurez une tension analogique sur votre arduino, il vous faudra acheter et prendre en main un circuit d'adaptation ADS115 ou MCP323008, de mémoire; ça fait plein d'efforts qi vous détourneront de votre projet)

A noter qu'une fois que la partie sous Arduino est debugguée, vous pouvez dialoquer entre le RPi (ou n'importe quel calculateur d bureau ayant une interface USB....) et l'Arduino (on peut même se servir d'un RPi pour programmer des arduini: je n'ai plus de PC depuis des lustres, et j'utilise des RPis).
Il suffit d'une bibliothèque python nommée pyserial (je crois qu'elle est fournie avec RPi OS; sinon, apt get install python3-serial doit marcher (cf https://packages.debian.org/bullseye/python3-serial) et elle est preque parfaite (fonctionne depuis des lustres pour moi)
https://electroniqueamateur.blogspot.co ... ec-un.html donne un assez bel exemple de code python qui reçoit de l'info de l'arduino (ce qui était dans la console série de l'IDE Arduino devient des données mâchées par python; naturellement, c'est à vous de définir ce que python doit mâcher, et comment l'envoyer sur le Net!)

A noter que j'ai cherché pendant longtemps (une ou 2 semaines il y a 8 ans..) à avoir un code C qui fasse le même boulot; https://chrisheydrick.com/2012/06/13/ho ... -c-part-2/ était très bien mais beaucoup plus long qu'un script python, ce qui m'a décidé ... à apprendre python (et je crains que , pour la gestion du net, vous ayiez beaucoup plus d'aide en python qu'en C/C++ :regardez les stats de ce forum en anglais....)

Aussi, je penserais plutôt (ce qui est certes hors sujet) pour la solution consistant à
* garder, éventullement perfectionner votre travail sous Arduino et
*communiquer entre l'Arduino et le RPi (ou tout PC, même sous Windows semble-t-il...) par le port USB (programmation et serie)
est moins fastidieuse et plus simple que de tout porter (chaque processeur fait ... ce qu'il fait de mieux).

Edité: en https://forum.arduino.cc/t/creation-dun ... 153153/142 quelqu'un du nom de ludomac (qui a à peu près le même style que vous) a les mêmes préoccupations avec un climatiseur reversible, et arrive à un rythme assez rapide à une solution acceptable;
* les numeros des pattes ne sont pas "gravés dans le code", mais des "const" ce qui est propre (il n'y a aucune raison que C sous RPi ou python ait les conventions de nommage des pattes qu'arduino, ... malheureusement..)
* le bus ONEWIRE des thermomètres) est supporté par le RPi, et le DS1820 peut être alimenté en 3v (une alim. en 5v blesserait ou tuerait un RPi)
-> Il serait faisable de porter ce script, une fois débuggué, ce qui est en bonne voie, sur RPi (python ou C), mais on s'expose à des fautes de frappe, erreurs de câblage et autres.
Une solution provisoire serait de transmettre (disons, une fois toutes les secondes: ce n'est pas plus compliqué que de faire clignoter une LED sans bloquer un Arduino) sur la ligne série l'état du climatiseur (IIRC : il y en aurait 7) , et les valeurs des 4 ou 5 thermomètres ou ... tout ce que vous voulez... et de laisser le RPi mettre en forme ces valeurs et en faire une page ouaibe.... (le RPi ne servirait alors "que" d'afficheur, et ne sert pas à commander... c'est encore un autre problème que de commander son climatiseur depuis sa résidence secondaire ou son bureau; vaut mieux faire petit à petit)

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Thu Dec 07, 2023 4:02 pm

oui c'est bien moi qui ai aussi sur l'arduino , de ce fais mon programme est abouti .
merci de votre aide.
donc pur vous vaut mieux utiliser python sur le raspberry avec ces pins ?

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Thu Dec 07, 2023 4:42 pm

Ce n'est pas une question de pattes, mais une question de simplicité.

Passer d'un truc qu marche bien -et je ne doute pas qu'il marche bien sous Arduino- et le porter est toujours un peu frustrant (on fait des fautes de frappe, on n'a pas compris la structure du noueau calculateur).
Au mois de juillet, il y avait une pénurie plus que genante de RPi (casser un RPi est ... désagréable pour le porte monnaye quand il n'y a pas de pénurie. C'est pire en periode de famine : on paase un temps fou à en chercher). C'est un problème réglé maintenant.

Si votre automate marche bien avec Arduino, j'ai l'impression que vous avez interet à en garder la plus grosse partie ... sous Arduino, et reserver au RPi la partie "commande" et/ou la partie "visualisation de l'état" (je sais que c'est faisable assez simplement sous Python , qui est plus simple et plus populare que C++)

Si vous trouvez un interet (curiosité? perspectives professionneslle?) à jouer avec python, il ne faut pas hesiter à le dire (ou a dire que vous tenez à rester avec C++).

IL y a a un autre détail à règler: si votre automate marche bien sous Arduino, vous pourrriez indiquer où il est (lien internet?) de façon que d'autres que moi (je peux me tromper) puissent juger de la difficulté -ou pas - d'un portage...

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Fri Dec 08, 2023 2:28 pm

https://wokwi.com/projects/381653351884851201
donner moi votre avis .
mais oui arduino j'aime bien aussi .

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Sat Dec 09, 2023 8:34 am

Code: Select all

#include <Wire.h>
#include <hd44780.h>                        // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i2c expander i/o class header
#include <Toggle.h>                         // https://github.com/Dlloydev/Toggle
#include "Sonde.h"

// les leds
const byte ledCa = 50;  // pin Led fonction CANICULE
const byte ledFr = 51;  // pin led fonction FROID
const byte ledCh = 52;  // pin Led fonction CHAUFFAGE

// les relais
const byte relaisComp = 30;          // pin relais Compresseur
const byte relaisV4V = 31;           // pin relais vanne 4 voies
const byte relaisVentUniteExt = 32;  // pin relais Ventilateur Unité Exterieur
const byte relaisVentExt = 33;       // pin relais Ventilateur Exterieur (petite et grande Vitesses)
const byte relaisVentUniteInt = 34;  // pin relais Ventilateur Unité Interieur
const byte relaisVentInt = 35;       // pin relais Ventilateur Interieur (petite et grande Vitesses)
const byte relaisVentIntPv = 36;     // pin relais Ventilatuer Interieur Petite vitesse
const byte relaisVentIntGv = 37;     // pin relais Ventilateur Interieur Grande vitesse

// les capteurs
const byte thermCh1Pin = 8;        // pin thermostat Chambre 1 , bouton poussoir pour test
const byte thermCh2Pin = 9;        // pin thermostat Chambre 2 , ""
const byte thermCh3Pin = 10;       // pin thermostat Chambre 3 , ""
const byte thermCh4Pin = 11;       // pin thermostat Chambre 4 , ""
const byte thermSalonPin = 12;     // pin thermostat Salon , ""
const byte capteurFiltrePin = 40;  // pin capteur de presence porte filtre
Toggle thermCh1;
Toggle thermCh2;
Toggle thermCh3;
Toggle thermCh4;
Toggle thermSalon;
Toggle capteurFiltre;
const unsigned int ms = 200;

// les boutons
const byte menuPin = 22;         // pin bouton Menu
const byte validPin = 23;        // pin bouton Valid
const byte boutonPlusPin = 24;   // pin bouton Plus
const byte boutonMoinsPin = 25;  // pin bouton Moins
Toggle boutonMenu;
Toggle boutonValid;
Toggle boutonPlus;
Toggle boutonMoins;
const unsigned int ms1 = 2000;

// les led boutons
const byte ledBoutonMenu = 42;  // pin Led bouton Menu
const byte ledBoutonValid = 43; // pin led bouton Valid
const byte ledBoutonPlus = 44;  // pin led bouton Plus
const byte ledBoutonMoins = 45; // pin led bouton Moins


// Les sondes
Sonde sondes[] = {
  { 2, "S-Ext:" },  // pin sonde Exterieur
  { 3, "U-Ext:" },  // pin sonde Unite Exterieur
  { 4, "E-Ext:" },  // pin sonde Echangeur Exterieur
  { 5, "U-Int:" },  // pin sonde Unite Interieur
  { 6, "E-Int:" }   // pin sonde Echangeur Interieur
};
constexpr size_t nombreDeSondes = sizeof sondes / sizeof * sondes;
size_t indexSondeEnCours = 0;
float temperatureAffichee = -127;

enum : byte { SondeExterieur,
              UniteExterieur,
              EchangeurExterieur,
              UniteInterieur,
              EchangeurInterieur
            };
#define tempExtLue (sondes[SondeExterieur].temperature())
#define tempUnitExtLue (sondes[UniteExterieur].temperature())
#define tempEchangeurExtLue (sondes[EchangeurExterieur].temperature())
#define tempUnitIntLue (sondes[UniteInterieur].temperature())
#define tempEchangeurIntLue (sondes[EchangeurInterieur].temperature())

// les tempos
unsigned long tempoTempLcd;
unsigned long tempoLcdConsigne;
unsigned long tempoBacklightLcd;

unsigned long tempoV4VFr = 0;
unsigned long tempoCompFr = 0;
unsigned long tempoCompCh = 0;
unsigned long tempoV4VCh = 0;
unsigned long tempoDegCh = 0;
unsigned long tempoDegNat = 0;
unsigned long tempoFinDegNat = 0;
unsigned long tempoEgouttageFr = 0;
unsigned long tempoEgouttageNat = 0;
unsigned long tempoEgouttageEle = 0;
unsigned long tempoFinEgouttageEle = 0;

unsigned long departChronoFiltre;
unsigned long finChronoFiltre;
unsigned long cumulTempsFiltre;
unsigned long nettoyageFiltre;

unsigned long tempoLedFrClignoteDeg = 0;
bool ledFrCl = 0;
unsigned long tempoLedChClignoteDeg = 0;
bool ledChCl = 0;
unsigned long tempoLedCaClignoteDeg = 0;
bool ledCaCl = 0;
unsigned long tempoLedArretCompletProgramClignote = 0;
bool ledChFrCaCl = 0;
unsigned long tempoLedBoutonClignote = 0;
bool ledBoutonMenuCl = 0;
bool ledBoutonMoinsCl = 0;
bool ledBoutonPlusCl = 0;
bool ledBoutonValidCl = 0;

const unsigned long AutoTemp = 30000ul;
static unsigned long chronoAutoTemp = -AutoTemp;

// les consignes
struct Consigne {
  const char* nom;
  float consigne;
};

struct Consigne consignes[] = {
  { "Temp-Ext", 13.5 },    //
  { "T-Canicule", 30.0 },  //
  { "GV-Ext-Fr", 20.0 },   //
  { "Bloc-Ch", 12.0 },     //
  { "GV-Ext-Ch", 5.0 },    //
  { "Deg-Na-Ch", 5.0 },    //
  { "Deg-Ele-Ch", -3.0 },  //
  { "Fin-Deg-Ch", 12.5 },  //
  { "PV-Int-Fr", 25.0 },   //
  { "PV-Int-Ch", 20.0 },   //
  { "D-V-Int-Ch", 30.0 },  //
  { "Deg-Fr", -1.0 },      //
  { "Fin-Deg-Fr", 15.0 },  //
  { "Temp-Delta", 6 }      //
};
constexpr size_t nombreDeConsignes = sizeof consignes / sizeof * consignes;
size_t indexConsigneEnCours = 0;

float consigneIntCa;

// les hysteresis
struct Hysteresi {
  const char* nom;
  float hysteresis;
};

struct Hysteresi hysteresis[] = {
  { "hyst-Ext", 1.0 },
  { "hyst-U-Ext", 0.5 },
  { "hyst-U-Int", 0.5 },
  { "hyst-Ca", 0.5 }
};
constexpr size_t nombreDeHysteresis = sizeof hysteresis / sizeof * hysteresis;
size_t indexHysteresiEnCours = 0;

bool tempIntCa = false;
bool tempExt = false;
bool tempVentIntCh = false;
bool tempVentExtCh = false;
bool tempVentIntFr = false;
bool tempVentExtFr = false;

// les vitesses interieur
struct Vitesse {
  const char* nom;
  byte vitesse;
};

struct Vitesse vitesses[] = {
  { "V-Auto", 0 },
  { "P-Vitesse", 1 },
  { "P-Vitesse", 2 },
  { "G-Vitesse", 3 },
  { "G-Vitesse", 4 }
};
constexpr size_t nombreDeVitesses = sizeof vitesses / sizeof * vitesses;
size_t indexVitesseEnCours = 0;
bool modifVitesseInt = false;
byte vitesseTravail = vitesses[indexVitesseEnCours].vitesse;

bool departGainable = false; // active ou desactive la machine a etasGainable (arret)

// le lcd
const uint8_t nbColonnes = 16;
const uint8_t nbLignes = 2;
hd44780_I2Cexp lcd;

// les caracteres speciaux
byte flecheFr[8] = {
  B00100,
  B00100,
  B00100,
  B00100,
  B00100,
  B10101,
  B01110,
  B00100
};
byte flecheCh[8] = {
  B00100,
  B01110,
  B10101,
  B00100,
  B00100,
  B00100,
  B00100,
  B00100
};
byte goutte[8] = {
  B00100,
  B00100,
  B00100,
  B01010,
  B10001,
  B10001,
  B10001,
  B01110
};
byte degivrage[8] = {
  B11111,
  B10001,
  B00100,
  B00100,
  B01010,
  B10001,
  B10001,
  B01110
};
byte eclaire[8] = {
  B00010,
  B00100,
  B01000,
  B11111,
  B00010,
  B00100,
  B01000,
  B10000
};
byte soleil[8] = {
  B00100,
  B10101,
  B01110,
  B11010,
  B01011,
  B01110,
  B10101,
  B00100
};
byte ventilateur[8] = {
  B01100,
  B01100,
  B00101,
  B11011,
  B11011,
  B10100,
  B00110,
  B00110
};

// les fonctions de l'affichages
void affiche(const char* etiquette, float valeur, byte ligne, byte precision = 2, byte tailleValeur = 6) {
  // on a nbColonnes-tailleValeur cases pour le nom - cadré à gauche
  // tailleValeur cases pour la consigne - cadrée à droite
  char bufferEtiquette[nbColonnes - tailleValeur + 1];
  strlcpy(bufferEtiquette, etiquette, sizeof bufferEtiquette);  // ça va tronquer l'étiquette si nécesaire
  lcd.setCursor(0, ligne);
  lcd.print(bufferEtiquette);

  char bufferValeur[nbColonnes];                           // suffisamment grand  pour nos besoins
  dtostrf(valeur, tailleValeur, precision, bufferValeur);  // conversion en chaîne de caractères

  byte nbEspaces = nbColonnes - (strlen(bufferEtiquette) + strlen(bufferValeur));
  for (byte i = 0; i < nbEspaces; i++) lcd.write(' ');  // pour cadrer la valeur à droite

  lcd.print(bufferValeur);
  static uint32_t x = 0;
}

void affiche(const char* etiquette, int valeur, byte ligne, byte tailleValeur = 6) {
  // on a nbColonnes-tailleValeur cases pour le nom - cadré à gauche
  // tailleValeur cases pour la consigne - cadrée à droite
  char bufferEtiquette[nbColonnes - tailleValeur + 1];
  strlcpy(bufferEtiquette, etiquette, sizeof bufferEtiquette);  // ça va tronquer l'étiquette si nécesaire
  lcd.setCursor(0, ligne);
  lcd.print(bufferEtiquette);

  char bufferValeur[nbColonnes];   // suffisamment grand  pour nos besoins
  itoa(valeur, bufferValeur, 10);  // conversion en chaîne de caractères

  byte nbEspaces = nbColonnes - (strlen(bufferEtiquette) + strlen(bufferValeur));
  for (byte i = 0; i < nbEspaces; i++) lcd.write(' ');  // pour cadrer la valeur à droite

  lcd.print(bufferValeur);
}

void affiche(const char* etiquette, byte ligne) {

  char bufferEtiquette[nbColonnes];
  strlcpy(bufferEtiquette, etiquette, sizeof bufferEtiquette);
  lcd.setCursor(0, ligne);
  lcd.print(bufferEtiquette);
}

// les consignes en fonction des sondes
void consigneSonde() { // affichage consignes par rapport au sondes sur la ligne 0
  if (indexConsigneEnCours == 0 || indexConsigneEnCours == 1) { // si : consigne 0 (13.5°C) ou consigne 1 (30.0°C)
    affiche(consignes[indexConsigneEnCours].nom, sondes[0].temperature(), 0); // affiche la consigne en cours et affiche la temperature lue par la sonde 0 (Temp-Ext) a la ligne 0
  } else if (indexConsigneEnCours == 2 || indexConsigneEnCours == 3 || indexConsigneEnCours == 4 || indexConsigneEnCours == 5) {
    affiche(consignes[indexConsigneEnCours].nom, sondes[1].temperature(), 0);
  } else if (indexConsigneEnCours == 6 || indexConsigneEnCours == 7) {
    affiche(consignes[indexConsigneEnCours].nom, sondes[2].temperature(), 0);
  } else if (indexConsigneEnCours == 8 || indexConsigneEnCours == 9) {
    affiche(consignes[indexConsigneEnCours].nom, sondes[3].temperature(), 0);
  } else if (indexConsigneEnCours == 10 || indexConsigneEnCours == 11 || indexConsigneEnCours == 12) {
    affiche(consignes[indexConsigneEnCours].nom, sondes[4].temperature(), 0);
  } else { // si non :
    affiche(consignes[indexConsigneEnCours].nom, consigneIntCa, 0); // affiche la consigne en cours et affiche la consigne canicule
  }
}

// les modification de vitesses interieur
void vitesseInterieur() { // modification des relais ventilateur interieur en fonction de l'affichage pour changer les vitesses
  if (indexVitesseEnCours == 0) { // si : 0 (automatique)
    modifVitesseInt = false; // pas de modification de vitesse
    vitesseTravail = vitesses[indexVitesseEnCours].vitesse;
  } else if (indexVitesseEnCours == 1) { // si non si : 1 (petite vitesse 1)
    modifVitesseInt = true; // modification de vitesse possible
    digitalWrite(relaisVentInt, LOW); // desactive relais ventilateur interieur petite vitesse 1 et grande vitesse 4
    digitalWrite(relaisVentIntPv, LOW); // desactive relais ventilateur interieur petite vitesse 2
    digitalWrite(relaisVentIntGv, LOW); // desactive relais ventilateur interieur grande vitesse 3
    vitesseTravail = vitesses[indexVitesseEnCours].vitesse;
  } else if (indexVitesseEnCours == 2) { // si non si ; 2 (petite vitesse 2)
    modifVitesseInt = true; // modification de vitesse possible
    digitalWrite(relaisVentInt, LOW); // desactive relais ventilateur interieur petite vitesse 1 et grande vitesse 4
    digitalWrite(relaisVentIntPv, HIGH); // active relais ventilateur interieur petite vitesse 2
    digitalWrite(relaisVentIntGv, LOW); // desactive relais ventilateur interieur grande vitesse 3
    vitesseTravail = vitesses[indexVitesseEnCours].vitesse;
  } else if (indexVitesseEnCours == 3) {
    modifVitesseInt = true;
    digitalWrite(relaisVentInt, LOW);
    digitalWrite(relaisVentIntPv, LOW);
    digitalWrite(relaisVentIntGv, HIGH);
    vitesseTravail = vitesses[indexVitesseEnCours].vitesse;
  } else if (indexVitesseEnCours == 4) {
    modifVitesseInt = true;
    digitalWrite(relaisVentInt, HIGH);
    digitalWrite(relaisVentIntPv, LOW);
    digitalWrite(relaisVentIntGv, LOW);
    vitesseTravail = vitesses[indexVitesseEnCours].vitesse;
  }
}

// les erreurs
bool erreur = false;

// les erreurs sondes
void erreurs() {
  if (tempExtLue == -127) {
    lcd.backlight();
    lcd.clear();
    affiche("!Er-Sonde-Ext!", 1);
    etablirModeErreur();
  } else if (tempUnitExtLue == -127) {
    lcd.backlight();
    lcd.clear();
    affiche("!Er-Sonde-UExt!", 1);
    etablirModeErreur();
  } else if (tempEchangeurExtLue == -127) {
    lcd.backlight();
    lcd.clear();
    affiche("!Er-Sonde-EExt!", 1);
    etablirModeErreur();
  } else if (tempUnitIntLue == -127) {
    lcd.backlight();
    lcd.clear();
    affiche("!Er-Sonde-UInt!", 1);
    etablirModeErreur();
  } else if (tempEchangeurIntLue == -127) {
    lcd.backlight();
    lcd.clear();
    affiche("!Er-Sonde-EInt!", 1);
    etablirModeErreur();
  }
}

// La machine à état de commande
enum { ETEINT,
       CONSIGNE,
       DETAIL_CONSIGNE,
       HYSTERESIS,
       DETAIL_HYSTERESIS,
       VITESSE,
       ACTIF,
       AUTOMATIQUE,
       ERREUR,
       NETTOYAGE_FILTRE
     } mode = ETEINT;

// les Variables de commande
bool entretienFiltre = false; // active ou desactive
byte ledMaitre = 0;

// les fonctions de commande
void etablirModeEteint() {
  lcd.backlight();
  if (departGainable == true) {
    lcd.clear();
    lcd.print("** ETEINDRE ? **");
  } else {
    lcd.clear();
    lcd.print("***  ETEINT  ***");
  }
  mode = ETEINT;
}

void etablirModeActif() {
  lcd.backlight();
  lcd.clear();
  lcd.print("** DEMARAGE ? **");
  mode = ACTIF;
}

void etablirModeAutomatique() {
  lcd.backlight();
  lcd.clear();
  departGainable = true;
  tempoBacklightLcd = millis();
  mode = AUTOMATIQUE;
}

void etablirModeConsigne() {
  lcd.backlight();
  lcd.clear();
  lcd.print("*** CONSIGNE ***");
  affiche(consignes[indexConsigneEnCours].nom, consignes[indexConsigneEnCours].consigne, 1);
  tempoLcdConsigne = millis();
  mode = CONSIGNE;
}

void etablirModeDetailConsigne() {
  lcd.backlight();
  lcd.clear();
  consigneSonde();
  affiche("consigne:", consignes[indexConsigneEnCours].consigne, 1);
  tempoLcdConsigne = millis();
  mode = DETAIL_CONSIGNE;
}

void etablirModeHysteresis() {
  lcd.backlight();
  lcd.clear();
  lcd.print("** HYSTERESIS **");
  affiche(hysteresis[indexHysteresiEnCours].nom, hysteresis[indexHysteresiEnCours].hysteresis, 1);
  mode = HYSTERESIS;
}

void etablirModeDetailHysteresis() {
  lcd.backlight();
  lcd.clear();
  affiche(hysteresis[indexHysteresiEnCours].nom, hysteresis[indexHysteresiEnCours].hysteresis, 0);
  affiche("hysteresis:", hysteresis[indexHysteresiEnCours].hysteresis, 1);
  tempoLcdConsigne = millis();
  mode = DETAIL_HYSTERESIS;
}

void etablirModeVitesse() {
  lcd.backlight();
  lcd.clear();
  lcd.print("*** VITESSES ***");
  affiche(vitesses[indexVitesseEnCours].nom, vitesses[indexVitesseEnCours].vitesse, 1);
  mode = VITESSE;
}

void etablirModeNettoyageFiltre() {
  lcd.backlight();
  lcd.clear();
  mode = NETTOYAGE_FILTRE;
}

void etablirModeErreur() {
  affiche(" **  ERREUR  **", 0);
  departGainable = false;
  mode = ERREUR;
}

void majBoutons() {
  boutonMenu.poll();
  boutonValid.poll();
  boutonPlus.poll();
  boutonMoins.poll();
}

void majCapteurs() {
  thermCh1.poll();
  thermCh2.poll();
  thermCh3.poll();
  thermCh4.poll();
  thermSalon.poll();
  capteurFiltre.poll();
}

// les leds clignotantes des boutons
void activeLedBoutonClignote() { // clignotement des leds des boutons a la meme cadence
  if (millis() - tempoLedBoutonClignote > 300) {
    ledMaitre = !ledMaitre;
    if (mode == ETEINT && departGainable == true)
    {
      digitalWrite(ledBoutonMenu, ledMaitre);
      digitalWrite(ledBoutonValid, ledMaitre);
      digitalWrite(ledBoutonPlus, LOW);
      digitalWrite(ledBoutonMoins, LOW);
    } else if (mode == ETEINT) {
      digitalWrite(ledBoutonMenu, ledMaitre);
      digitalWrite(ledBoutonValid, LOW);
      digitalWrite(ledBoutonPlus, LOW);
      digitalWrite(ledBoutonMoins, LOW);
    } else if (mode == CONSIGNE || mode == HYSTERESIS || mode == VITESSE || mode == ERREUR) {
      digitalWrite(ledBoutonMenu, ledMaitre);
      digitalWrite(ledBoutonValid, ledMaitre);
      digitalWrite(ledBoutonMoins, ledMaitre);
      digitalWrite(ledBoutonPlus, ledMaitre);
    } else if (mode == DETAIL_CONSIGNE || mode == DETAIL_HYSTERESIS) {
      digitalWrite(ledBoutonValid, ledMaitre);
      digitalWrite(ledBoutonMoins, ledMaitre);
      digitalWrite(ledBoutonPlus, ledMaitre);
      digitalWrite(ledBoutonMenu, LOW);
    } else if (mode == ACTIF) {
      digitalWrite(ledBoutonMenu, ledMaitre);
      digitalWrite(ledBoutonValid, ledMaitre);
      digitalWrite(ledBoutonPlus, LOW);
      digitalWrite(ledBoutonMoins, LOW);
    } else if (mode == AUTOMATIQUE) {
      digitalWrite(ledBoutonMenu, ledMaitre);
      digitalWrite(ledBoutonValid, LOW);
      digitalWrite(ledBoutonPlus, LOW);
      digitalWrite(ledBoutonMoins, LOW);
    }
    tempoLedBoutonClignote = millis();
  }
}

// le mode
void gestionEtat() {

  majBoutons();
  gainable();

  switch (mode) {

    case ETEINT:

      if (departGainable == true) {
        if (millis() - tempoLcdConsigne >= 15000) {
          etablirModeAutomatique();
          tempoBacklightLcd = millis();
        }
        if (boutonMenu.onPress()) {
          etablirModeAutomatique();
        } else if (boutonValid.onPress()) {
          departGainable = false;
          etablirModeEteint();
        } else {
          activeLedBoutonClignote();
        }
      } else if (boutonMenu.onPress() && !capteurFiltre.pressedFor(ms)) {
        etablirModeActif();
      } else {
        activeLedBoutonClignote();
      }
      break;

    case CONSIGNE:

      if (millis() - tempoLcdConsigne >= 15000) {
        etablirModeAutomatique();
        tempoBacklightLcd = millis();
      }
      if (boutonPlus.onPress()) {
        tempoLcdConsigne = millis();
        indexConsigneEnCours = (indexConsigneEnCours + 1) % nombreDeConsignes;
        affiche(consignes[indexConsigneEnCours].nom, consignes[indexConsigneEnCours].consigne, 1);
      } else if (boutonMoins.onPress()) {
        tempoLcdConsigne = millis();
        if (indexConsigneEnCours == 0) indexConsigneEnCours = nombreDeConsignes - 1;
        else indexConsigneEnCours--;
        affiche(consignes[indexConsigneEnCours].nom, consignes[indexConsigneEnCours].consigne, 1);
      } else if (boutonValid.onPress()) {
        etablirModeDetailConsigne();
      } else if (boutonMenu.onPress()) {
        etablirModeHysteresis();
      } else {
        activeLedBoutonClignote();
      }
      break;

    case DETAIL_CONSIGNE:

      {
        if (millis() - tempoLcdConsigne >= 15000) {
          etablirModeAutomatique();
          tempoBacklightLcd = millis();
        }
        consigneSonde();
        if (boutonPlus.onPress()) {
          tempoLcdConsigne = millis();
          consignes[indexConsigneEnCours].consigne += 0.5;
          affiche("consigne:", consignes[indexConsigneEnCours].consigne, 1);
        } else if (boutonMoins.onPress()) {
          tempoLcdConsigne = millis();
          consignes[indexConsigneEnCours].consigne -= 0.5;
          affiche("consigne:", consignes[indexConsigneEnCours].consigne, 1);
        } else if (boutonValid.onPress()) {
          etablirModeConsigne();
        } else {
          activeLedBoutonClignote();
        }
      }
      break;

    case HYSTERESIS:

      if (millis() - tempoLcdConsigne >= 15000) {
        etablirModeAutomatique();
        tempoBacklightLcd = millis();
      }
      if (boutonPlus.onPress()) {
        tempoLcdConsigne = millis();
        indexHysteresiEnCours = (indexHysteresiEnCours + 1) % nombreDeHysteresis;
        affiche(hysteresis[indexHysteresiEnCours].nom, hysteresis[indexHysteresiEnCours].hysteresis, 1);
      } else if (boutonMoins.onPress()) {
        tempoLcdConsigne = millis();
        if (indexHysteresiEnCours == 0) indexHysteresiEnCours = nombreDeHysteresis - 1;
        else indexHysteresiEnCours--;
        affiche(hysteresis[indexHysteresiEnCours].nom, hysteresis[indexHysteresiEnCours].hysteresis, 1);
      } else if (boutonValid.onPress()) {
        etablirModeDetailHysteresis();
      } else if (boutonMenu.onPress()) {
        vitesseGainable();
      } else {
        activeLedBoutonClignote();
      }
    case DETAIL_HYSTERESIS:

      if (millis() - tempoLcdConsigne >= 15000) {
        etablirModeAutomatique();
        tempoBacklightLcd = millis();
      }
      if (boutonPlus.onPress()) {
        tempoLcdConsigne = millis();
        hysteresis[indexHysteresiEnCours].hysteresis += 0.5;
        affiche("hysteresis:", hysteresis[indexHysteresiEnCours].hysteresis, 1);
      } else if (boutonMoins.onPress()) {
        tempoLcdConsigne = millis();
        hysteresis[indexHysteresiEnCours].hysteresis -= 0.5;
        affiche("hysteresis:", hysteresis[indexHysteresiEnCours].hysteresis, 1);
      } else if (boutonValid.onPress()) {
        etablirModeHysteresis();
      } else {
        activeLedBoutonClignote();
      }

      break;

    case VITESSE:

      {
        if (millis() - tempoLcdConsigne >= 15000) {
          etablirModeAutomatique();
          tempoBacklightLcd = millis();
        }
        vitesseInterieur();
        if (boutonPlus.onPress()) {
          tempoLcdConsigne = millis();
          indexVitesseEnCours = (indexVitesseEnCours + 1) % nombreDeVitesses;
          affiche(vitesses[indexVitesseEnCours].nom, vitesses[indexVitesseEnCours].vitesse, 1);
        } else if (boutonMoins.onPress()) {
          tempoLcdConsigne = millis();
          if (indexVitesseEnCours == 0) indexVitesseEnCours = nombreDeVitesses - 1;
          else indexVitesseEnCours--;
          affiche(vitesses[indexVitesseEnCours].nom, vitesses[indexVitesseEnCours].vitesse, 1);
        } else if (boutonMenu.onPress()) {
          etablirModeEteint();
        } else if (boutonValid.onPress()) {
          etablirModeAutomatique();
        } else {
          tempoBacklightLcd = millis();
          activeLedBoutonClignote();
        }
      }
      break;

    case ACTIF:

      if (boutonValid.onPress()) {
        etablirModeAutomatique();
        tempoBacklightLcd = millis();
      } else if (boutonMenu.onPress()) {
        digitalWrite(ledBoutonValid, LOW);
        etablirModeEteint();
      } else {
        activeLedBoutonClignote();
      }
      break;

    case AUTOMATIQUE:

      {
        const unsigned long Auto = 10000ul;
        static unsigned long chronoAuto = -Auto;
        bool noBacklightLcd = false;

        if (entretienFiltre == true) {
          etablirModeNettoyageFiltre();
        } else if (millis() - chronoAuto >= Auto) {
          indexSondeEnCours = (indexSondeEnCours + 1) % nombreDeSondes;
          chronoAuto = millis();
        } else {
          afficheEtatsGainable();
          affiche(sondes[indexSondeEnCours].nom, sondes[indexSondeEnCours].temperature(), 1);
        }
        if (millis() - tempoBacklightLcd >= 60000) {
          digitalWrite(ledBoutonValid, LOW);
          digitalWrite(ledBoutonPlus, LOW);
          digitalWrite(ledBoutonMoins, LOW);
          digitalWrite(ledBoutonMenu, LOW);
          lcd.noBacklight();
          noBacklightLcd = true;
          if (boutonMenu.onPress()) {
            lcd.backlight();
            tempoBacklightLcd = millis();
            noBacklightLcd = false;
          }
        } else if (boutonMenu.onPress() && noBacklightLcd == false) {
          etablirModeConsigne();
        } else {
          activeLedBoutonClignote();
          erreurs();
        }
      }
      break;

    case ERREUR:

      if (boutonMenu.onPress()) {
        erreurs();
      } else if (boutonValid.onPress()) {
        etablirModeEteint();
      } else {
        activeLedBoutonClignote();
      }
      break;

    case NETTOYAGE_FILTRE:

      lcd.backlight();
      affiche("Nettoyage       ", 0);
      affiche("         Filtre", 1);

      break;
  }
}

// la machine à état gainable
enum { ARRET,
       DEPART,
       COMMANDE_FROID,
       TEMPO_V4V,
       TEMPO_COMPRESSEUR_FROID,
       COMPRESSEUR_FROID,
       DEGIVRAGE_FROID,
       EGOUTTAGE_FROID,
       COMMANDE_CHAUFFAGE,
       TEMPO_COMPRESSEUR_CHAUFFAGE,
       TEMPO_DEGIVRAGE,
       MODE_DEGIVRAGE,
       DEGIVRAGE_NATUREL,
       EGOUTTAGE_NATUREL,
       TEMPO_DEG_V4V,
       TEMPO_DEG_COMPRESSEUR,
       DEGIVRAGE_ELECTRIC,
       EGOUTTAGE_CHAUFFAGE,
       FIN_EGOUTTAGE_CHAUFFAGE,
       COMMANDE_CANICULE,
       TEMPO_V4V_CANICULE,
       TEMPO_COMPRESSEUR_CANICULE,
       COMPRESSEUR_CANICULE,
       FILTRE
     } etatsGainable;

// les variables de gainable
bool fonctionFr = false;
bool fonctionCh = false;
bool fonctionCa = false;
bool caniculeLed = false;

// les commandes des fonctions
void commandeFroid() {
  majCapteurs();
  if (thermSalon.pressedFor(ms) || thermCh1.pressedFor(ms) || thermCh2.pressedFor(ms) || thermCh3.pressedFor(ms) || thermCh4.pressedFor(ms)) {
    fonctionFr = true;
  } else {
    fonctionFr = false;
  }
}

void commandeChauffage() {
  majCapteurs();
  if (tempUnitExtLue >= consignes[3].consigne) {
    fonctionCh = false;
  } else if (!thermSalon.pressedFor(ms) || !thermCh1.pressedFor(ms) || !thermCh2.pressedFor(ms) || !thermCh3.pressedFor(ms) || !thermCh4.pressedFor(ms)) {
    fonctionCh = true;
  } else {
    fonctionCh = false;
  }
}

void commandeCanicule() {
  majCapteurs();
  if (tempIntCa && (thermSalon.pressedFor(ms) || thermCh1.pressedFor(ms) || thermCh2.pressedFor(ms) || thermCh3.pressedFor(ms) || thermCh4.pressedFor(ms))) {
    fonctionCa = true;
  } else {
    fonctionCa = false;
  }
}

// les hysteresis de consignes
void hysteresisTempExt() {
  if (tempExt) {
    tempExt = (tempExtLue <= (consignes[0].consigne + hysteresis[0].hysteresis));
  } else {
    tempExt = (tempExtLue <= (consignes[0].consigne - hysteresis[0].hysteresis));
  }
}

void hysteresisTempIntCa() {
  if (tempIntCa) {
    tempIntCa = (tempUnitIntLue >= (consigneIntCa - hysteresis[3].hysteresis));
  } else {
    tempIntCa = (tempUnitIntLue >= (consigneIntCa + hysteresis[3].hysteresis));
  }
}

void hysteresisTempVentIntCh() {
  if (tempVentIntCh) {
    tempVentIntCh = (tempUnitIntLue >= (consignes[9].consigne - hysteresis[2].hysteresis));
  } else {
    tempVentIntCh = (tempUnitIntLue >= (consignes[9].consigne + hysteresis[2].hysteresis));
  }
}

void hysteresisTempVentExtCh() {
  if (tempVentExtCh) {
    tempVentExtCh = (tempUnitExtLue <= (consignes[4].consigne + hysteresis[1].hysteresis));
  } else {
    tempVentExtCh = (tempUnitExtLue <= (consignes[4].consigne - hysteresis[1].hysteresis));
  }
}

void hysteresisTempVentIntFr() {
  if (tempVentIntFr) {
    tempVentIntFr = (tempUnitIntLue <= (consignes[8].consigne + hysteresis[2].hysteresis));
  } else {
    tempVentIntFr = (tempUnitIntLue <= (consignes[8].consigne - hysteresis[2].hysteresis));
  }
}

void hysteresisTempVentExtFr() {
  if (tempVentExtFr) {
    tempVentExtFr = (tempUnitExtLue >= (consignes[2].consigne - hysteresis[1].hysteresis));
  } else {
    tempVentExtFr = (tempUnitExtLue >= (consignes[2].consigne + hysteresis[1].hysteresis));
  }
}

// les clignotements des leds des fonctions en degivrage
void activeLedDegivrageFr() {
  if (caniculeLed == true) {
    digitalWrite(ledFr, LOW);
  } else {
    if (millis() - tempoLedFrClignoteDeg > 300) {
      tempoLedFrClignoteDeg = millis();
      ledFrCl = (ledFrCl == HIGH) ? LOW : HIGH;
      digitalWrite(ledFr, ledFrCl);
    }
  }
}

void activeLedDegivrageCa() {
  if (caniculeLed == true) {
    if (millis() - tempoLedCaClignoteDeg > 300) {
      tempoLedCaClignoteDeg = millis();
      ledCaCl = (ledCaCl == HIGH) ? LOW : HIGH;
      digitalWrite(ledCa, ledCaCl);
    }
  }
}

void activeLedDegivrageCh() {
  if (millis() - tempoLedChClignoteDeg > 300) {
    tempoLedChClignoteDeg = millis();
    ledChCl = (ledChCl == HIGH) ? LOW : HIGH;
    digitalWrite(ledCh, ledChCl);
  }
}

// les relais des ventilateurs interieur et exterieur
void activeRelaisVentIntFroid() {      // controle ventilateurs en FROID
  if (tempVentIntFr) {                 // si : la temperature unite interieur est inferieur a consigne Petite vitesse interieur 25°C a l'aspiration
    digitalWrite(relaisVentInt, LOW);  // ventilateur interieur petite vitesse
  } else {                             // si non :
    digitalWrite(relaisVentInt, HIGH);  // ventilateur interieur grande vitesse
  }
}

void activeRelaisVentExtFroid() {           // controle ventilateur exterieur en froid
  digitalWrite(relaisVentUniteExt, HIGH);  // relais ventilateur unité exterieur oN
  if (tempVentExtFr) {                     // si : la temperature unite exterieur est superieur a consigne grande vitesse exterieur 20°C ambiante
    digitalWrite(relaisVentExt, HIGH);     // ventilateur exterieur grande vitesse
  } else {                                 // si non :
    digitalWrite(relaisVentExt, LOW);      // ventilateur exterieur petite vitesse
  }
}

void activeRelaisVentIntCh() { // controle ventilateur interieur en chauffage
  if (tempVentIntCh) {                 // si : la temperature unite interieur est superieur a consigne petite vitesse 20°C a l'aspiration
    digitalWrite(relaisVentInt, LOW);  // petit vitesse
  } else {                             // si non :
    digitalWrite(relaisVentInt, HIGH);  // grande vitesse
  }
}

void activeRelaisVentExtCh() {             // controle ventilateur exterieur en chauffage
  digitalWrite(relaisVentUniteExt, HIGH);  // relais ventilateur de l'unité exterieur oN
  if (tempVentExtCh) {                     // si : la temperature unite exterieur est inferieur a 5°C ambiante
    digitalWrite(relaisVentExt, HIGH);     // ventilateur exterieur grande vitesse
  } else {                                 // si non :
    digitalWrite(relaisVentExt, LOW);      // ventilateur exterieur petite vitesse
  }
}

void activeRelaisVentsCanicule() {  // controle ventilateurs Interieur Canicule
  digitalWrite(relaisVentUniteInt, HIGH); // relais ventilateur unite interieur oN (coupure au neutre)
  digitalWrite(relaisVentInt, HIGH);  // relais ventilateur interieur grande vitesse
  digitalWrite(relaisVentUniteExt, HIGH); // relais ventilateur unite exterieur oN (coupure au neutre en serie avec pressostat Hp2)
  digitalWrite(relaisVentExt, HIGH); // relais ventilateur exterieur Grande vitesse
}

void activeRelaisVentIntDegFr() { // controle ventilatuer interieur en degivrage froid
  digitalWrite(relaisVentUniteInt, HIGH);  // relais ventilateur de l'unité interieur oN
  digitalWrite(relaisVentInt, HIGH);       // relais ventilateur interieur grande vitesse oN
}

void activeRelaisVentExtDegCh() { // controle ventilateur exterieur en degivrage chauffage
  digitalWrite(relaisVentUniteExt, HIGH); // relais ventilateur unite exterieur oN (coupure au neutre en serie avec pressostat Hp2)
  digitalWrite(relaisVentExt, HIGH); // relais ventilateur exterieur Grande vitesse
}

// l'arret des relais
void desactiveTousRelais() {
  digitalWrite(relaisVentUniteExt, LOW);
  digitalWrite(relaisVentUniteInt, LOW);
  digitalWrite(relaisVentInt, LOW);
  digitalWrite(relaisVentIntPv, LOW);
  digitalWrite(relaisVentIntGv, LOW);
  digitalWrite(relaisVentExt, LOW);
  digitalWrite(relaisComp, LOW);
  digitalWrite(relaisV4V, LOW);
}

// les affichages des etats du gainable
void afficheEtatsGainable() {
  if (etatsGainable == COMMANDE_FROID) {
    affiche("Froid oFF       ", 0);
    lcd.setCursor(15, 0);
    lcd.print(" ");
  } else if (etatsGainable == TEMPO_V4V || etatsGainable == TEMPO_COMPRESSEUR_FROID || etatsGainable == COMPRESSEUR_FROID) {
    affiche("Froid        ", 0);
    lcd.setCursor(13, 0);
    lcd.print(char(8));
    if (modifVitesseInt == true) {
      lcd.setCursor(14, 0);
      lcd.print(vitesseTravail);
    } else if (digitalRead(relaisVentInt)) {
      lcd.setCursor(14, 0);
      lcd.print("4");
    } else {
      lcd.setCursor(14, 0);
      lcd.print("1");
    }
    if (digitalRead(relaisComp)) {
      lcd.setCursor(15, 0);
      lcd.print(char(1));
    }
  } else if (etatsGainable == DEGIVRAGE_FROID) {
    affiche("Degivrage       ", 0);
    lcd.setCursor(15, 0);
    lcd.print(char(4));
  } else if (etatsGainable == EGOUTTAGE_FROID) {
    affiche("Egouttage       ", 0);
    lcd.setCursor(15, 0);
    lcd.print(char(5));
  } else if (etatsGainable == COMMANDE_CHAUFFAGE) {
    affiche("Chauffage oFF   ", 0);
    lcd.setCursor(13, 0);
    lcd.print("   ");
  } else if (etatsGainable == TEMPO_COMPRESSEUR_CHAUFFAGE || etatsGainable == TEMPO_DEGIVRAGE || etatsGainable == MODE_DEGIVRAGE) {
    affiche("Chauffage    ", 0);
    if (digitalRead(relaisComp)) {
      lcd.setCursor(13, 0);
      lcd.print(char(8));
      if (modifVitesseInt == true) {
        lcd.setCursor(14, 0);
        lcd.print(vitesseTravail);
      } else if (digitalRead(relaisVentInt)) {
        lcd.setCursor(14, 0);
        lcd.print("4");
      } else {
        lcd.setCursor(14, 0);
        lcd.print("1");
      }
      lcd.setCursor(15, 0);
      lcd.print(char(2));
    }
  } else if (etatsGainable == DEGIVRAGE_NATUREL) {
    affiche("Degivrage      ", 0);
    lcd.setCursor(15, 0);
    lcd.print(char(4));
  } else if (etatsGainable == EGOUTTAGE_NATUREL) {
    affiche("Egouttage      ", 0);
    lcd.setCursor(15, 0);
    lcd.print(char(5));
  } else if (etatsGainable == TEMPO_DEG_V4V || etatsGainable == TEMPO_DEG_COMPRESSEUR || etatsGainable == DEGIVRAGE_ELECTRIC) {
    affiche("Degivrage     ", 0);
    lcd.setCursor(14, 0);
    lcd.print(char(6));
    lcd.setCursor(15, 0);
    lcd.print(char(4));
  } else if (etatsGainable == EGOUTTAGE_CHAUFFAGE) {
    affiche("Egouttage    ", 0);
    lcd.setCursor(14, 0);
    lcd.print(char(6));
    lcd.setCursor(15, 0);
    lcd.print(char(5));
  } else if (etatsGainable == FIN_EGOUTTAGE_CHAUFFAGE) {
    affiche("Fin Egouttage ", 0);
    lcd.setCursor(14, 0);
    lcd.print(char(6));
    lcd.setCursor(15, 0);
    lcd.print(char(5));
  } else if (etatsGainable == COMMANDE_CANICULE) {
    affiche("Canicul oFF ", 0);
    lcd.setCursor(12, 0);
    lcd.print(char(7));
    lcd.setCursor(13, 0);
    lcd.print("   ");
  } else if (etatsGainable == TEMPO_V4V_CANICULE || etatsGainable == TEMPO_COMPRESSEUR_CANICULE || etatsGainable == COMPRESSEUR_CANICULE) {
    affiche("Canicul    ", 0);
    lcd.setCursor(12, 0);
    lcd.print(char(7));
    lcd.setCursor(13, 0);
    lcd.print(char(8));
    if (digitalRead(relaisVentInt)) {
      lcd.setCursor(14, 0);
      lcd.print("4");
    }
    if (digitalRead(relaisComp)) {
      lcd.setCursor(15, 0);
      lcd.print(char(1));
    }
  }
}

// les etats du gainable possible pour modifier les vitesses interieur
void vitesseGainable() { // les etats Gainable pour modifier vitesse ventilateur interieur
  if (etatsGainable == TEMPO_V4V || etatsGainable == TEMPO_COMPRESSEUR_FROID || etatsGainable == COMPRESSEUR_FROID || etatsGainable == TEMPO_DEGIVRAGE || etatsGainable == MODE_DEGIVRAGE) {
    etablirModeVitesse();
  } else {
    etablirModeEteint();
  }
}

// controle des temperatures pour l'automatisation
void controleTemperature() { // temporisation de controle temperature exterieur
  if (millis() - chronoAutoTemp >= AutoTemp) {
    etatsGainable = DEPART;
    chronoAutoTemp = millis();
  }
}

// le gainable
void gainable() {

  if (departGainable == false) {
    etatsGainable = ARRET;
  }

  switch (etatsGainable) {

    case ARRET:

      if (departGainable == true) {
        etatsGainable = DEPART;
      } else {
        desactiveTousRelais();
        digitalWrite(ledCa, LOW);
        digitalWrite(ledFr, LOW);
        digitalWrite(ledCh, LOW);
      }

      break;

    case DEPART:  // controle automatique de la temperature pour selectionner le mode

      if (nettoyageFiltre >= 4250000ul) {  // 49 jours de fonctionnement du compresseur 4250000000ms
        entretienFiltre = true;
        etatsGainable = FILTRE;
      } else if (tempExt) {  // si la temperature exterieur est inferieur as la consigne (12.5°C)
        etatsGainable = COMMANDE_CHAUFFAGE;              // j'active le CHAUFFAGE
      } else if (tempExtLue <= consignes[1].consigne) {  // si non si la temperature exterieur est inferieur a la consigne canicule (30°C)
        etatsGainable = COMMANDE_FROID;
      } else {  // si non
        etatsGainable = COMMANDE_CANICULE;  // j'active FROID CANICULE
      }
      break;

    case COMMANDE_FROID:

      if (fonctionFr == true) {
        tempoV4VFr = millis();
        etatsGainable = TEMPO_V4V;
      } else {
        digitalWrite(ledFr, LOW);
        desactiveTousRelais();
        controleTemperature();
      }
      break;

    case TEMPO_V4V:

      if (fonctionFr == false) {
        etatsGainable = COMMANDE_FROID;
        chronoAutoTemp = millis();
      } else if (millis() - tempoV4VFr >= 6000ul) {  // temporisation de 1 minute
        tempoCompFr = millis();
        digitalWrite(relaisV4V, HIGH);
        etatsGainable = TEMPO_COMPRESSEUR_FROID;
      } else {
        activeRelaisVentExtFroid();
        digitalWrite(ledFr, HIGH);
        digitalWrite(relaisVentUniteInt, HIGH);  // relais ventilateur unité interieur oN
        if (modifVitesseInt == false) {
          activeRelaisVentIntFroid();
        }  else {
          vitesseInterieur();
        }
      }
      break;

    case TEMPO_COMPRESSEUR_FROID:

      if (fonctionFr == false) {
        etatsGainable = COMMANDE_FROID;
        chronoAutoTemp = millis();
      } else if (millis() - tempoCompFr >= 18000ul) {  // temporisation de 3 minutes avant demarrage compresseur 180000ul
        departChronoFiltre = millis();
        etatsGainable = COMPRESSEUR_FROID;
      } else {
        activeRelaisVentExtFroid();
        if (modifVitesseInt == false) {
          activeRelaisVentIntFroid();
        }  else {
          vitesseInterieur();
        }
      }
      break;

    case COMPRESSEUR_FROID:

      if (fonctionFr == false) {
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        desactiveTousRelais();
        digitalWrite(ledFr, LOW);
        chronoAutoTemp = millis();
        etatsGainable = COMMANDE_FROID;
      } else if (tempEchangeurIntLue <= consignes[11].consigne) {  // si : temperature echangeur interieur est inferieur ou egal 1°C (lancement degivrage)
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        vitesseTravail = vitesses[0].vitesse;
        etatsGainable = DEGIVRAGE_FROID;
      } else {
        activeRelaisVentExtFroid();
        digitalWrite(relaisComp, HIGH);
        if (modifVitesseInt == false) {
          activeRelaisVentIntFroid();
        }  else {
          vitesseInterieur();
        }
      }
      break;

    case DEGIVRAGE_FROID:

      if (tempEchangeurIntLue >= consignes[12].consigne) {
        tempoEgouttageFr = millis();
        etatsGainable = EGOUTTAGE_FROID;
      } else {
        desactiveTousRelais();
        activeLedDegivrageFr();
        activeLedDegivrageCa();
      }
      break;

    case EGOUTTAGE_FROID:  // etat EGOUTTAGE_FR

      if (millis() - tempoEgouttageFr >= 120000ul) {  // 120000ms = 2 minutes
        desactiveTousRelais();
        caniculeLed = false;
        etatsGainable = DEPART;
      } else {
        activeLedDegivrageFr();
        activeLedDegivrageCa();
        activeRelaisVentIntDegFr();
      }
      break;

    case COMMANDE_CHAUFFAGE:

      if (fonctionCh == true) {
        tempoCompCh = millis();
        etatsGainable = TEMPO_COMPRESSEUR_CHAUFFAGE;
      } else {
        digitalWrite(ledCh, LOW);
        desactiveTousRelais();
        controleTemperature();
      }
      break;

    case TEMPO_COMPRESSEUR_CHAUFFAGE:

      if (fonctionCh == false) {
        chronoAutoTemp = millis();
        etatsGainable = COMMANDE_CHAUFFAGE;
      } else if (millis() - tempoCompCh >= 18000ul) {  // temporisation de 3 minutes avant le demarage du compresseur 180000ul
        departChronoFiltre = millis();
        tempoDegCh = millis();
        etatsGainable = TEMPO_DEGIVRAGE;
      } else {
        digitalWrite(ledCh, HIGH);
      }
      break;

    case TEMPO_DEGIVRAGE:

      if (fonctionCh == false) {
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        chronoAutoTemp = millis();
        etatsGainable = COMMANDE_CHAUFFAGE;
      } else if (millis() - tempoDegCh >= 2400000ul) {  // si : la temporisation de 40 minutes (si compresseur a fonctionner pendant 40 minutes) 2400000ul
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        etatsGainable = MODE_DEGIVRAGE;
      } else {
        digitalWrite(relaisComp, HIGH);
        activeRelaisVentExtCh();
        if (tempEchangeurIntLue >= consignes[10].consigne) {  //
          digitalWrite(relaisVentUniteInt, HIGH);             // demarrage ventilateur interieur a 30°C coupure au neutre
        }
        if (modifVitesseInt == false) {
          activeRelaisVentIntCh();
        } else {
          vitesseInterieur();
        }
      }
      break;

    case MODE_DEGIVRAGE:

      if (tempUnitExtLue >= consignes[5].consigne) {  // si : la temperature de l'unité exterieur est supperieur ou egal a 5°C ( lancement degivrage naturel )
        tempoDegNat = millis();
        etatsGainable = DEGIVRAGE_NATUREL;                        // passe a l'etat DEGIVRAGE_NATUREL
      } else if (tempEchangeurExtLue <= consignes[6].consigne) {  // si : la temperature de l'Echangeur exterieur est inferieur ou egal a -3°C ( degivrage gaz chaud inversion de cycle )
        tempoV4VCh = millis();
        etatsGainable = TEMPO_DEG_V4V;
      } else {
        activeRelaisVentExtCh();
        if (modifVitesseInt == false) {
          activeRelaisVentIntCh();
        } else {
          vitesseInterieur();
        }
      }
      break;

    case DEGIVRAGE_NATUREL:  // etat DEGIVRAGE_NATUREL

      if (millis() - tempoDegNat >= 600000ul) {  // 600000ms = 10 minutes
        tempoEgouttageNat = millis();
        etatsGainable = EGOUTTAGE_NATUREL;  // passe a l'etat EGOUTTAGE_NATUREL
      } else {
        desactiveTousRelais();
        activeLedDegivrageCh();
      }
      break;

    case EGOUTTAGE_NATUREL:  // etat EGOUTTAGE_NATUREL

      if (millis() - tempoEgouttageNat >= 300000ul) {  // 300000ms
        desactiveTousRelais();
        etatsGainable = DEPART;  // passe a l'etat
      } else {
        vitesseTravail = vitesses[0].vitesse;
        activeRelaisVentExtDegCh();
        activeLedDegivrageCh();
      }
      break;

    case TEMPO_DEG_V4V:  // etat DEGIVRAGE_ELECTRIC

      if (millis() - tempoV4VCh >= 78000ul) {  // 78000ms 1 minute 30 secondes
        tempoCompCh = millis();
        etatsGainable = TEMPO_DEG_COMPRESSEUR;
      } else {
        desactiveTousRelais();
        activeLedDegivrageCh();
      }
      break;

    case TEMPO_DEG_COMPRESSEUR:

      if (millis() - tempoCompCh >= 120000ul) {  // 120000ms 2 minutes
        etatsGainable = DEGIVRAGE_ELECTRIC;
      } else {
        digitalWrite(relaisV4V, HIGH);  // relais vanne 4 voies oN
        activeLedDegivrageCh();
      }
      break;

    case DEGIVRAGE_ELECTRIC:

      if (tempEchangeurExtLue >= consignes[7].consigne) {  // si la temperature est superieur ou egal a 12.5°C ( fin de degivrage )
        tempoEgouttageEle = millis();
        etatsGainable = EGOUTTAGE_CHAUFFAGE;  // passe a l'etat EGOUTTAGE_ELECTRIC
      } else {
        digitalWrite(relaisComp, HIGH);  // relais compresseur oN
        activeLedDegivrageCh();
      }
      break;

    case EGOUTTAGE_CHAUFFAGE:  // etat EGOUTTAGE_ELECTRIC

      if (millis() - tempoEgouttageEle >= 300000ul) {  // 300000ms = 5 minutes
        tempoFinEgouttageEle = millis();
        etatsGainable = FIN_EGOUTTAGE_CHAUFFAGE;  // passe a l'etat FIN_EGOUTTAGE_ELE
      } else {
        desactiveTousRelais();
        activeLedDegivrageCh();
      }
      break;

    case FIN_EGOUTTAGE_CHAUFFAGE:  // etat FIN_EGOUTTAGE_ELE

      if (millis() - tempoFinEgouttageEle >= 150000ul) {  // 150000ms = 2.5 minutes
        desactiveTousRelais();
        etatsGainable = DEPART;  // passe a l'etat
      } else {
        vitesseTravail = vitesses[0].vitesse;
        activeRelaisVentExtDegCh();
        activeLedDegivrageCh();
      }
      break;

    case COMMANDE_CANICULE:  // etat FROID_CANICULE

      if (fonctionCa == true) {
        tempoV4VFr = millis();
        etatsGainable = TEMPO_V4V_CANICULE;
      } else {
        digitalWrite(ledCa, LOW);
        controleTemperature();
        desactiveTousRelais();
      }
      break;

    case TEMPO_V4V_CANICULE:

      if (fonctionCa == false) {
        etatsGainable = COMMANDE_CANICULE;
        chronoAutoTemp = millis();
      } else if (millis() - tempoV4VFr >= 6000ul) {  // temporisation de 1 minute
        tempoCompFr = millis();
        digitalWrite(relaisV4V, HIGH);
        etatsGainable = TEMPO_COMPRESSEUR_CANICULE;
      } else {
        activeRelaisVentsCanicule();
        digitalWrite(ledCa, HIGH);
        digitalWrite(relaisVentUniteInt, HIGH);  // relais ventilateur unité interieur oN
      }
      break;

    case TEMPO_COMPRESSEUR_CANICULE:

      if (fonctionCa == false) {
        chronoAutoTemp = millis();
        etatsGainable = COMMANDE_CANICULE;
      } else if (millis() - tempoCompFr >= 18000ul) {  // temporisation de 3 minutes avant demarrage compresseur
        departChronoFiltre = millis();
        etatsGainable = COMPRESSEUR_CANICULE;
      } else {
        activeRelaisVentsCanicule();
      }
      break;

    case COMPRESSEUR_CANICULE:

      if (fonctionCa == false) {
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        digitalWrite(ledCa, LOW);
        chronoAutoTemp = millis();
        etatsGainable = COMMANDE_CANICULE;
      } else if (tempEchangeurIntLue <= consignes[11].consigne) {  // si : temperature echangeur interieur est inferieur ou egal 1°C (lancement degivrage)
        finChronoFiltre = millis();
        nettoyageFiltre = (finChronoFiltre - departChronoFiltre) + nettoyageFiltre;
        caniculeLed = true;
        etatsGainable = DEGIVRAGE_FROID;
      } else {
        digitalWrite(relaisComp, HIGH);
      }
      break;

    case FILTRE:

      if (millis() - tempoLedArretCompletProgramClignote >= 500) {
        tempoLedArretCompletProgramClignote = millis();
        ledChFrCaCl = (ledChFrCaCl == HIGH) ? LOW : HIGH;
        digitalWrite(ledFr, ledChFrCaCl);
        digitalWrite(ledCa, ledChFrCaCl);
        digitalWrite(ledCh, ledChFrCaCl);
        if (capteurFiltre.pressedFor(ms)) {
          entretienFiltre = false;
          nettoyageFiltre = 0;
          etablirModeEteint();
        }
      }
      break;
  }
}

// la fonction calculs
void autresCalculs() {
  hysteresisTempIntCa();
  hysteresisTempExt();
  hysteresisTempVentIntCh();
  hysteresisTempVentExtCh();
  hysteresisTempVentIntFr();
  hysteresisTempVentExtFr();
  commandeFroid();
  commandeChauffage();
  commandeCanicule();
  consigneIntCa = tempExtLue - consignes[13].consigne;  // consigne interieur canicule est egale a la temperature exterieur (NORD) - temperature delta (-6)
}

// l'affichage Serial
void imprimeTout() {
  const unsigned long deltaT = 2500ul;    // en ms, toutes les 2,5 secondes
  static unsigned long chrono = -deltaT;  // valeur initiale pour affichage au premier appel

  if (millis() - chrono >= deltaT) {
    Serial.print(F("Temperature EXT °C = "));
    Serial.print(tempExtLue);
    Serial.print(F(" ; Temperature Unite EXT °C = "));
    Serial.print(tempUnitExtLue);
    Serial.print(F(" ; Temperature Echangeur EXT °C = "));
    Serial.println(tempEchangeurExtLue);
    Serial.print(F(" ; Temperature Unite INT °C = "));
    Serial.print(tempUnitIntLue);
    Serial.print(F(" ; Temperature Echangeur INT °C = "));
    Serial.println(tempEchangeurIntLue);

    const char* nomDesEtats[] = { "ARRET", "DEPART", "COMMANDE_FROID", "TEMPO_V4V", "TEMPO_COMPRESSEUR_FROID", "COMPRESSEUR_FROID", "DEGIVRAGE_FROID", "EGOUTTAGE_FROID", "COMMANDE_CHAUFFAGE", "TEMPO_COMPRESSEUR_CHAUFFAGE", "TEMPO_DEGIVRAGE", "MODE_DEGIVRAGE", "DEGIVRAGE_NATUREL", "EGOUTTAGE_NATUREL", "TEMPO_DEG_V4V", "TEMPO_DEG_COMPRESSEUR", "DEGIVRAGE_ELECTRIC", "EGOUTTAGE_CHAUFFAGE", "FIN_EGOUTTAGE_CHAUFFAGE", "COMMANDE_CANICULE", "TEMPO_V4V_CANICULE", "TEMPO_COMPRESSEUR_CANICULE", "COMPRESSEUR_CANICULE", "FILTRE" };
    Serial.print(F("ETAT: "));
    Serial.println(nomDesEtats[etatsGainable]);

    const char* nomDesEtatsMode[] = { "ETEINT", "CONSIGNE", "DETAIL_CONSIGNE", "HYSTERESIS", "DETAIL_HYSTERESIS", "VITESSE", "ACTIF", "AUTOMATIQUE", "ERREUR", "NETTOYAGE_FILTRE" };
    Serial.print(F("MODE: "));
    Serial.println(nomDesEtatsMode[mode]);

    Serial.print(F("CHRONO = "));
    Serial.println(millis());

    Serial.print(F("Nettoyage filtre = "));
    Serial.println(nettoyageFiltre);

    Serial.print(F("Consigne Interieur CANICULE °C = "));
    Serial.println(consigneIntCa);

    Serial.print(F("tempo Controle Temp = "));
    Serial.println(chronoAutoTemp);

    Serial.print(F("modifVitesse = "));
    Serial.println(modifVitesseInt);

    Serial.print(F("vitesseTravail = "));
    Serial.println(vitesseTravail);

    Serial.print(F("consigne vent Int = "));
    Serial.println(consignes[8].consigne);

    chrono = millis();
  }
}

// le setup
void setup() {

  pinMode(relaisComp, OUTPUT);            // Relais Compresseur
  pinMode(relaisV4V, OUTPUT);             // Relais Vanne 4 Voies
  pinMode(relaisVentUniteExt, OUTPUT);    // Relais Ventilateur Unité Exterieur oN/oFF ( coupur au neutre )
  pinMode(relaisVentExt, OUTPUT);         // Relais Ventilateur Exterieur grande et petite vitesse
  pinMode(relaisVentUniteInt, OUTPUT);    // Relais Ventilateur unité interieur oN/oFF  ( coupure au neutre )
  pinMode(relaisVentInt, OUTPUT);         // Relais Ventilateur Interieur grande et petite vitesse
  pinMode(relaisVentIntPv, OUTPUT);       // Relais ventilateur Interieur petite vitesse
  pinMode(relaisVentIntGv, OUTPUT);       // Relais ventilateur Interieur Grande Vitesse
  pinMode(ledCa, OUTPUT);                 // LED fonction HP
  pinMode(ledFr, OUTPUT);                 // LED fonction Froid
  pinMode(ledCh, OUTPUT);                 // LED fonction Chauffage
  pinMode(ledBoutonMenu, OUTPUT);         // LED Bouton Menu
  pinMode(ledBoutonValid, OUTPUT);        // LED Bouton Valid
  pinMode(ledBoutonPlus, OUTPUT);         // LED Bouton Plus
  pinMode(ledBoutonMoins, OUTPUT);        // LED Bouton Moins
  thermCh1.begin(thermCh1Pin);            // Contact NC/NO Thermostat Radio Chambre 1
  thermCh2.begin(thermCh2Pin);            // Contact NC/NO Thermostat Radio Chambre 2
  thermCh3.begin(thermCh3Pin);            // Contact NC/NO Thermostat Radio Chambre 3
  thermCh4.begin(thermCh4Pin);            // Contact NC/NO Thermostat Radio Chambre 4
  thermSalon.begin(thermSalonPin);        // Contact NC/NO Thermostat Radio Salon
  capteurFiltre.begin(capteurFiltrePin);  // Capteur Presence Filtre
  boutonMenu.begin(menuPin);              // Bouton Menu
  boutonValid.begin(validPin);            // Bouton Valid
  boutonPlus.begin(boutonPlusPin);        // Bouton Plus
  boutonMoins.begin(boutonMoinsPin);      // Bouton Moins

  for (Sonde& s : sondes) s.begin();

  for (size_t indice = 0; indice < nombreDeConsignes; indice++)
    ;

  Serial.begin(115200);

  int result = lcd.begin(nbColonnes, nbLignes);
  if (result) {
    Serial.print("LCD initialization failed: ");
    Serial.println(result);
    hd44780::fatalError(result);
  }

  lcd.createChar(1, flecheFr);
  lcd.createChar(2, flecheCh);
  lcd.createChar(4, degivrage);
  lcd.createChar(5, goutte);
  lcd.createChar(6, eclaire);
  lcd.createChar(7, soleil);
  lcd.createChar(8, ventilateur);

  etablirModeEteint();
}

// la loop
void loop() {

  gestionEtat();
  autresCalculs();
  imprimeTout();
}
Bonn, votre projet a l'air plutôt très bien écrit, et je ne doute pas qu'il vous donne satisfaction.

On peut le porter, soit en C++, soit en Python sur un RPi, mais c'est long et frustrant . (risque de fautes de frappe)

Je pensais plutôt à
* transmettre au RPi l'état du système (temperatures un peu partout; quelles que soient les commandes prises en compte), le RPi ayant ses propres affichages et moyens de stockage.Ca a l(-air de doublonner imprimeTout, mais certains sont plus à l'aise avec un écan d'ordinateur/une page ouaibe qu'avec un affichuer LCD...

* éventuellement, et dans une seconde étape, commander l'automate depuis le RPi (par glissières ou saisie clavier) au lieu d'utiliser des appuis boutons. (quelqu'un devant son ordinateur peut ne pas vouloir se deplacer pour commander un climatiseur)

En gros, ces deux etapes , **utilisant la jonction série,** permettraient de vous familiariser avec python (la gestion de la ligne série en C est faisable, mais très difficile) sans le moindre achat de materiel (si vous voulez gerer des DS1820 depuis un RPi, c'est tout a fait faisable, mais, pour les tests, il faut acheter des frais de porc et des ds1820) ni le moindre risque de court circuit du connecteur de votre RPi...

Edité: votre schema sous wokwi contient
* 8 relais
*4 puosssoirs et leurs LEDs témoins
* 5 ds1820
* 6 interrupteurs ordinaires
* 3 LEDS ordinaires
Ca fait déjà 29 pattes (je n'ai pas compté les 6 fils de commande de l'afficheur LCD)
Or, le RPi n'a que 26 pattes de disponibles.

On peut reduire ce nombre de pattes par multiplexage, mais ça complique pas mal le logiciel, où qu'il soit, augmente les coûts et les risques de panne
Aussi je suis un peu conforté dans l'idée de commencer par piloter (ou tout simplement afficher) depuis un RPi (ou u PC sous Linux) votre automate, sans toucher au câblage

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Sun Dec 10, 2023 4:33 pm

oui c'est ce que j'ai vue effectivement au niveau des pattes disponible!
de ce fais je pense supprimer les leds étant donner qu' il y aurais le lcd qui afficherais les infos .
de plus les inters sont en faite les 5 contacts (nc / no) des thermostats radio deja present sur mon installation
et le 6eme sera un capteur de presence filtre (nc / no)

faut allé etape par etape .
deja je pense par apprendre python

mais bon vue vos connaissance , je pense que vous saurez d'un grand aide .
ca reste un projet fou.
surtout pour un debutant .
mais bon j'aime bien .
pour l'instant c'est pour moi perso. voir les possibiliter qui s'offre sur ce materiel .

j'aime bien avoir le moniteur serie pour commencer car c'a m'aide pour trouver des erreurs .
mais ces vrai qu'as l'ecran d'un ordinateur serais bien aussi .

etape par etape :D

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Sun Dec 10, 2023 7:06 pm

au sujet des pins ! qu'elle sont celle qui ne doivent absolument pas servirent en IN ou OUT ?

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Sun Dec 10, 2023 8:18 pm

Normalement, il y a deux pins(de memoire, je ne sais pas lesquelles) qu'il vaut mieux ne pas utiliser viewtopic.php?t=81733 me sussure que c'est un second bus I2C reservé au systeme... Mais je maintiens qu'il y en a 26 d'utilisables.

PS: il y a des façons asssez agressives de se dépatouiller avec peu de pattes:
* tous vos 8 relais peuvent être çablés sur un PCF8574 (I2C <-> nivaux) ou un demi MC23017 (I2C ou SPI <-> 16 niveaux logiques): on gagne 6 fils (plus, avec le MCP23017 et en mettant 3 LEDs et 5 interrupteurs sur l'autre moitié)
* vos DS1820 peuvent être câblés en étoile, et gérés par le numéro de série(monopolisent 1 fil; par contre, c'est coton de s'en depatouiller quand on ajoute un DS1820 -et c'est comme ça que le RPi les gère)

Ce genre de manips sur le materiel est assez facile (et moins coûteux- à experimenter sur Arduino.ou picoPi...

danjperron
Posts: 4689
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: gpio c++

Sun Dec 10, 2023 10:06 pm

Un petit bémol avec le Pi5!

Les gpios pour le PI5 est un peut différent. La meilleure façon en C c'est d'utiliser une librarie. pigpio ne fonctionne as sur le Pi5!

J'ai fait quelque tests avec la librairie gpiod et elle fonctionne assez bien. J'ai réussi de lire un DS18B20 avec le gpio directement sans passer par un "driver"

En python il faut utiliser gpiozero puisque la librairie RPi.gpio ne fonctionne pas.

Exemple de mon projet avec la librairie gpiod.
https://github.com/danjperron/BitBangingDS18B20

JumpZero
Posts: 1420
Joined: Thu Mar 28, 2013 7:35 pm
Location: Arcachon, France

Re: gpio c++

Mon Dec 11, 2023 1:44 am

Attention avec le cablage du bus 1wire (DS18B20) la topologie du bus n'est pas sans incidence:
lire ici

danjperron
Posts: 4689
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: gpio c++

Mon Dec 11, 2023 4:59 pm

Attention avec le cablage du bus 1wire (DS18B20) la topologie du bus n'est pas sans incidence:
@JumZero Nous parlons de la manipulation des GPIOs. Mon utilisation du DS18B20 n'est qu'un exemple.


En passant essaie de faire cela avec le pilote(driver) pour le ds18B20. Lire 30 capteurs en moins d'une seconde!
https://dl.dropboxusercontent.com/s/nnu ... S18B20.mp4

JumpZero
Posts: 1420
Joined: Thu Mar 28, 2013 7:35 pm
Location: Arcachon, France

Re: gpio c++

Mon Dec 11, 2023 6:47 pm

danjperron wrote:
Mon Dec 11, 2023 4:59 pm
Attention avec le cablage du bus 1wire (DS18B20) la topologie du bus n'est pas sans incidence:
@JumZero Nous parlons de la manipulation des GPIOs. Mon utilisation du DS18B20 n'est qu'un exemple.


En passant essaie de faire cela avec le pilote(driver) pour le ds18B20. Lire 30 capteurs en moins d'une seconde!
https://dl.dropboxusercontent.com/s/nnu ... S18B20.mp4
@danjperron oui, oui, d'accord, mais mon conseil pour le réseau 1wire s'addressait à ludoraspberry (mais j'ai omis de le dire)
A oui très bien ton bitbanging de ds18B20 en lire 30 en moins d'1 seconde en lançant la conversion simultanée et résolution 9bits (93ms); J'ai vu ta vidéo. Excellent.

De mon coté j'ai crée un décodeur sigrok/pulseview pour analyseur logique:
https://github.com/villeneuve/libsigrokdecode-ds18b20

Ah ces DS18B20 ;)

Edit: désolé je suis hors sujet

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Wed Dec 13, 2023 5:22 pm

salut je rencontre cette erreur , je comprend pas !

Traceback (most recent call last):
File "/home/ludo/Desktop/TEST/Test.py", line 9, in <module>
GPIO.setup(5,GPIO.OUT)
RuntimeError: No access to /dev/mem. Try running as root!

le code
#coding:utf-8
# Import necessary libraries for communication and display use
import drivers
import time
import RPi.GPIO as GPIO # Ajouter la gestion des GPIO avec cette lib ou une autre

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(5,GPIO.OUT)

tempHI = 23
tempLOW = 21



# Load the driver and set it to "display"
display = drivers.Lcd()


# Lecture température DS18B20
def lireFichier(emplacement):
fichTemp = open(emplacement)
contenu = fichTemp.read()
fichTemp.close()
return contenu

# Recuperation temperature DS18B20
def recupTemp(contenuFich):
secondeLigne = contenuFich.split("\n")[1]
temperatureData = secondeLigne.split(" ")[9]
temperature = float(temperatureData[2:5])
temperature = temperature / 10
return temperature


# Main body of code
try:
while True:
print("Affichage date et temperature sur lcd i2c") # Affichage texte d'information dans le terminal
# Mettre le 28-xxxxxxxxx de votre sonde
contenuFich = lireFichier("/sys/bus/w1/devices/28-01192ee0cfef/w1_slave") # contenuFich la temperature
temperature = recupTemp(contenuFich)
# Affichage date/heure sur la 1ere ligne
display.lcd_display_string("%s" %time.strftime("%d/%m/%y %H:%M"), 1)
# Affichage de la température sur la 2eme ligne
display.lcd_display_string("Temp: "+str(temperature)+chr(223)+"C", 2)
if temperature > tempHI:
GPIO.output(5, GPIO.LOW) #turn GPIO pin
elif temperature < tempLOW:
GPIO.output(5, GPIO.HIGH) #Turn GPIO pin
except KeyboardInterrupt: # Press ctrl+c pour stopper le programme et effacer le lcd
# If there is a KeyboardInterrupt (when you press CTRL + C), exit the program and cleanup
print("Exit and cleaning up!")
display.lcd_clear()
# Make sure backlight is on / turn on by leaving
display.lcd_backlight(0)

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Wed Dec 13, 2023 6:04 pm

Excusez ma curiosité, mais est ce que votre lcd (sur bus i2C) affiche quelque chose?
Parce que là, vous testez *Deux periferiques, alors que les test devraient se faire l'un après l'autre (sinon, on peut se retrouver aces 100000 lignes non testées)

Il y a aussi un gros problème:
vous faites des copies de code python directement; comme les blancs sont partie de la semantique de python, on ne peut rien comprendre à votre code python si vous n'activez pas le bouton code (le 5 ième à partir de la gauche dans le haut de la fenêrte de saisie

ex
while (1 == 1) :
print ("salut")

peut se lire de deux façons

Code: Select all

while (1 == 1) :
print ("salut") # vraisemblablement une erreur
ou

Code: Select all

while (1 == 1) :
    print ("salut") #inonde le terminal de salutations
·
En C (C++, arduino), on peut rattrapper les indentations, c'est TRES desagréable mais pas impossible; en python, c'est impossible, semant le désordre dans la lecture et le depannage...

Quand à votre erreur, il faudrait tester en mode superutilisateur

Code: Select all

sudo python3 script-ds1820_et_lcdTWI 
doit faire l'affaire (ou declencher des bugs un peu plus loin, je ne peux pas lire)
Nota: la correction que je propose (utilisation de sudo ) peut horrifier à juste titre les puristes... mais pour commencer à depanner (ou a chercher la bug suivante), ça suffit...(elle est évoquée dans https://raspberrypi.stackexchange.com/q ... ng-as-root)

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Wed Dec 13, 2023 7:17 pm

oui j'ais suivi http://emery.claude.free.fr/raspberry-lcd-i2c.html pour le lcd .
c'est que le debut pour moi sur python et j'avoue que l'écriture parait simple visuellement . et j'aime bien voir du code en meme temps que je l'apprend

le rpi4 est brancher dessu : https://www.waveshare.com/wiki/RPi_Rela ... ll_library . j'ai suivi ce qui marque et ce code avait marcher sans erreur .
et des que j'ai brancher l'ecran et la sonde ou j'arrivait bien as afficher l'heure ,la date et la temperature .
j'ai ressayer les relais avec le code qui donne dans l'exemple du site waveshare
et la c'est la merde patron !! :D

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Wed Dec 13, 2023 7:20 pm

Code: Select all

#coding:utf-8
# Import necessary libraries for communication and display use
import drivers
import time
import RPi.GPIO as GPIO # Ajouter la gestion des GPIO avec cette lib ou une autre

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(5,GPIO.OUT)

tempHI = 23  

tempLOW = 21



# Load the driver and set it to "display"
display = drivers.Lcd()


# Lecture température DS18B20
def lireFichier(emplacement):
    fichTemp = open(emplacement)
    contenu = fichTemp.read()
    fichTemp.close()
    return contenu
    
# Recuperation temperature DS18B20
def recupTemp(contenuFich):
    secondeLigne = contenuFich.split("\n")[1]
    temperatureData = secondeLigne.split(" ")[9]
    temperature = float(temperatureData[2:5])
    temperature = temperature / 10
    return temperature


# Main body of code
try:
    while True:
        print("Affichage date et temperature sur lcd i2c")  # Affichage texte d'information dans le terminal
        # Mettre le 28-xxxxxxxxx de votre sonde
        contenuFich = lireFichier("/sys/bus/w1/devices/28-01192ee0cfef/w1_slave")  # contenuFich la temperature
        temperature = recupTemp(contenuFich)
        # Affichage date/heure sur la 1ere ligne
        display.lcd_display_string("%s" %time.strftime("%d/%m/%y  %H:%M"), 1)
        # Affichage de la température sur la 2eme ligne
        display.lcd_display_string("Temp: "+str(temperature)+chr(223)+"C", 2)
        if temperature > tempHI:
                GPIO.output(5, GPIO.LOW) #turn GPIO
        elif temperature < tempLOW:
                GPIO.output(5, GPIO.HIGH) #Turn GPIO
except KeyboardInterrupt: # Press ctrl+c pour stopper le programme et effacer le lcd
    # If there is a KeyboardInterrupt (when you press CTRL + C), exit the program and cleanup
    print("Exit and cleaning up!")
    display.lcd_clear()
    # Make sure backlight is on / turn on by leaving
    display.lcd_backlight(0)/code]

ludoraspberry
Posts: 141
Joined: Fri Jul 03, 2020 10:03 pm

Re: gpio c++

Wed Dec 13, 2023 7:25 pm

j'ai fait ca : sudo chown root.gpio /dev/gpiomem
sudo chmod g+rw /dev/gpiomem
donc c'est bon pour le code demo relay ca roule ! merci

j'ai essayer le code plus haut pout tester , et quand la temperature descent vers le 10 ° et la pouffe la temperature afficher remonte as 70°C

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Wed Dec 13, 2023 9:10 pm

Code: Select all

def recupTemp(contenuFich)
     print(type(contenuFic)) # debugging par print
    print(contenuFic) # debugging par print
    secondeLigne = contenuFich.split("\n")[1]
    temperatureData = secondeLigne.split(" ")[9] # au vu des formats, j'aurais découpé sur "t="
    temperature = float(temperatureData[2:5])
    temperature = temperature / 10
    return temperature
J'ai rajouté deux lignes de dépannage pour savoir ce qu'est contenuFic (je na'i pas de ds1820)
Normalement, lttps://www.analog.com/media/en/technical-docum ... S18S20.pdf , le DS1820 gère les températures négatives -ce n'estpas le cas du DHT11 ...!- et le noyau linux copie de façon réaliste la valeur dans /sys/bus/w1/devices/28-code_Du_Capteur/w1_slave

une autre façon de faire est de taper

Code: Select all

cat /sys/bus/w1/devices/28-code_Du_Capteur/w1_slave
depuis un terminal.
J'ai lu https://www.framboise314.fr/mesure-de-t ... de_DS18B20
qui est daté de 2014... ce qui est vieux (le format peut avoir changé)
Si votre programme de démo a mal découpé la seconde ligne, on y verra un peu plus clair....avec ces deux lignes de débugging (pas besoin de le mettre dans une boucle infinie).
PS: vous auriez interet à mettre un time.sleep(0.5) pour ralentir les affichages -on peut alors les lire- et ne pas trop charger le CPU (chauffera moins: ça peut effrayer les gens)

PhL91
Posts: 287
Joined: Thu Dec 16, 2021 10:21 am

Re: gpio c++

Thu Dec 14, 2023 9:02 am

Hello,

J'ai un ds18b20 installé sur Rpi. Celui-ci utilise directement le bus 1wire et la temperature peut être lue avec:

Code: Select all

$ cat /sys/bus/w1/devices/28-XXXXXX/temperature
La réponse est en millième de degrés !

Si, juste la temperature vous intéresse, alors c'est un peu plus simple. Évidemment, je peux comprendre que vous vouliez progresser en programmation et donc que vous soyez intéressés à faire fonctionner votre code.

dbrion1
Posts: 450
Joined: Tue May 30, 2023 4:42 pm
Location: North of France

Re: gpio c++

Thu Dec 14, 2023 11:13 am

Code: Select all

J'ai un ds18b20 installé sur Rpi. Celui-ci utilise directement le bus 1wire et la temperature peut être lue avec:
Code: Select all

$ cat /sys/bus/w1/devices/28-XXXXXX/temperature
La réponse est en millième de degrés !
Quelle version du RPi? (OS, type??)
On peut lire les fichiers émis par le driver OneWire dans tous les langages possibles (je saurais le lire en R, et Fortran), on sait le lire sous bas -utile pour savoir le format exact), en Python (dans toute une ménagerie de versions), en C, php, awk.
Tant que OP n'a pas donné le format exact de son fichier créé par le noyau, on ne peut pas tenter de le debugguer en dessous de 10 C ... ce qui a l'air du souci du moment

Nota la commande

Code: Select all

cat Fichier_au_format_inconnu
sert enormement à debugguer

PhL91
Posts: 287
Joined: Thu Dec 16, 2021 10:21 am

Re: gpio c++

Thu Dec 14, 2023 11:34 am

Je suis sous Rpi OS bookworm aarch64 et le bus 1wire fonctionnait de la même manière avec la version précédente.

Pour les températures négatives, je pense qu'il n'y a que le signe "-" qui est ajouté à la valeur de la température.
Comme exemple, en cas température négative sur mon Rpi, la ligne suivante:

Code: Select all

$ echo $(date +'%F %H:%M'),$(echo "scale=1; $(cat ${infile})/1000" |bc -l)
me donne des valeurs comme:
2023-12-03 09:55,-1.2
2023-12-03 10:00,-1.1
2023-12-03 10:05,-1.0
2023-12-03 10:10,-1.0
2023-12-03 10:15,-.9
2023-12-03 10:20,-.8
Et comme je n'ai pas pu m'empêcher de tester en python, voici un code qui fonctionne chez moi (hors lcd que je n'ai pas):

Code: Select all

#!/usr/bin/python
#
import time
# Lecture température DS18B20
def lireFichier(emplacement):
	fichTemp = open(emplacement)
	contenu = fichTemp.read()
	fichTemp.close()
	return contenu

# Recuperation temperature DS18B20
def recupTemp(contenuFich):
	temperature = float(contenuFich)
	temperature = temperature / 1000
	return temperature

try:
	while True:
		print("Affichage date et temperature sur lcd i2c") # Affichage texte d'information dans le terminal
		contenuFich = lireFichier("/sys/bus/w1/devices/28-0416939921ff/temperature")
		temperature = recupTemp(contenuFich)
		print("%s" %time.strftime("%d/%m/%y %H:%M"))
		print("Temp: "+str(temperature)+"°C")
		time.sleep(5)
except KeyboardInterrupt:
	print("Exit and cleaning up!")
exit

PhL91
Posts: 287
Joined: Thu Dec 16, 2021 10:21 am

Re: gpio c++

Thu Dec 14, 2023 1:00 pm

ludoraspberry wrote:
Wed Dec 13, 2023 7:25 pm
...
j'ai essayer le code plus haut pout tester , et quand la temperature descent vers le 10 ° et la pouffe la temperature afficher remonte as 70°C
Je soupçonne que c'est la ligne suivante qui provoque le pb:

Code: Select all

temperature = float(temperatureData[2:5])
Il ne faudrait pas se limiter au ":5" mais prendre toute la ligne à partir de la colonne 2. En effet, quand on passe de 9° à 10° un caractère se rajoute et comme seulement 3 caractères sont pris, le calcul devient faux (il en serait de même pour les températures négatives où un signe "-" se rajoute)

En remplaçant la ligne ci-dessus par:

Code: Select all

temperature = float(temperatureData[2:])
ça devrait le faire!

Return to “Français”