Définition de fonction non autorisé

Bonjour,

À la ligne 810 du programme de contrôle de mon spa , j'ai le message d'errur suivant :
Une définition de fonction n'est pas autorisé içi avant {.

Comment puis-je corriger cela?

void envoieHC06(){//Bluetooth envoi de donnée
HC06.print("*T"+String(celsiusNet)+"*");
HC06.print("*C"+String(consigneTemperature)+"*");
if (chauffageResistance==1) {
HC06.print("*RR255G0B0*");}
else {HC06.print("*RR255G255B255*");}

 if (memoireJet==1) {HC06.print("*MNettoyage filtre Jet*");}
 if (memoireHP==1) {HC06.print("*MNettoyage filtre HP*");}
 if (filtration==1) {HC06.print("*MFiltration en cours*");}
 if (aeration==1) {HC06.print("*MAeration en cours*");}
 if (memoireJet==0 && memoireHP==0 && filtration==0 && aeration==0){HC06.print("*MSpaduino*");}
   return;
}

Merçi de votre aide.

Post mis dans la mauvaise section, on parle anglais dans les forums généraux, je viens de déplacer le post dans la section francophone.

Merci de prendre en compte les recommandations listées dans "Les bonnes pratiques du Forum Francophone".

il nous faudrait tout le code car le bug n'est pas dans cette fonction mais sans doute dans ce qu'il y avant.

est-ce un .ino ou un fichier .cpp complémentaire ?

Bonjour,

ci-joint le fichier .ino

SpaDuino-v1_7.ino (31,9 Ko)

c'est le bazar dans votre fichier

je vois ligne 107

void setup() {

et un peu plus loin, ligne 171 à nouveau

  void setup() {

et il semble que tout le code soit dans le premier setup...

vous ne pouvez avoir qu'une seule fonction setup et revérifiez les paires d'accolades {} vous avez dû rater un truc quand vous avez mis des commentaires peut être.

Bonjour J-M-L

Je corrige les lignes 107 et 171 et vous revient.

Merçi.

J'ai fait le ménage du programme et j'ai conservé le premier void setup() {

Maintenant, j'ai l'erreur suivante à la ligne 686:
Exit status 1
'lcd_4x20' was not déclared in this scope
Erreur durant le téléchargement de https://arduino.esp8266.com/stable/package_esp8266cpm_index.json

Je suis sur la version 1.8.19 de l'IDE d'arduino, est-ce que je devrais prendre la version la plus récente.

Merçi de votre aide.

SpaDuino-v1_71.ino (32,0 Ko)

Non ça changera rien - il doit y avoir encore un souci sur la déclaration de la variable donc pb peut être encore de {} ou définition pas globale

Je ne pense pas que passer à la version 2 changera quelque chose.

Néanmoins, la branche 1.8.x n'évolue plus, il faudra bien un jour passer à la branche 2.

Celui où la fonction loop() est placé à l'intérieur de setup() ?

Je confirme.

Dans le fichier final, main.cpp, celui que tu ne vois pas et que l'IDE construit pour toi, la fonction setup() s'exécute une seule fois. Setup() est pour les initialisations.
Par contre la fonction loop() s'exécute en boucle (loop se traduit par boucle).
Sitôt loop exécutée, loop redémare.

Si loop() est dans setup() elle ne sera exécutée qu'une seule fois.

La branche 2.x n’évolue plus non plus…

Pas surprenant.
Toutes les déclarations de variables sont faites dans setup() donc elles sont toutes inconnues dans le reste du code.

Je suggère un CTRL+T dans l'IDE pour formater le code correctement. Cela te donnera une vision un peu plus claire de la structure de ton programme.

Cette suite de if avec des if imbriqués dans loop() est une bonne source d'erreurs et rend le code illisible. Il serait sage de faire des petites fonctions pour traiter les cas et de laisser dans loop les tests de plus haut niveau.

oui est des trucs du genre

      Serial.lcd 2004 i2c libraryprint("SpaDuino Version 1.7 ");

ont peu de chance de fonctionner... (pour ne pas dire que c'est n'importe quoi).

Vous avez fait des remplacements automatiques de texte ???

un exemple de restructuration du code pour qu'il soit plus lisible, ne continent pas de while true qui sont toujours un risque, création de fonctions pour simplifier la lecture etc...

Notez que vous utilisez une vous utilisez aussi une version de IRremote qui est obsolète... faudrait passer à la nouvelle API...

GitHub - Arduino-IRremote/Arduino-IRremote: Infrared remote library for Arduino: send and receive infrared signals with multiple protocols · GitHub

// SpaDUino v1.71 — Gestion d'un SPA/Jacuzzi
// Auteur original : Nicolas Lecerf
// Adaptation : François Huot, avril 2026
// Licence : BY NC SA

// ═══════════════════════════════════════════════════════════════════════════
// BIBLIOTHÈQUES
// ═══════════════════════════════════════════════════════════════════════════

#include <DallasTemperature.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <RTClib.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>
#include <OneWire.h>
#include <IRremote.h>

// ═══════════════════════════════════════════════════════════════════════════
// BROCHES
// ═══════════════════════════════════════════════════════════════════════════

const int BROCHE_SONDE_TEMP = 6;
const int BROCHE_IR = 9;
const int BROCHE_BT_RX = 10;
const int BROCHE_BT_TX = 11;

const int BROCHE_POMPE_PV = 2;
const int BROCHE_POMPE_GV = 3;
const int BROCHE_RESISTANCE = 4;
const int BROCHE_COMMUN_POMPE = 5;
const int BROCHE_BULLEUR = A0;
const int BROCHE_LAMPE_INT = A1;
const int BROCHE_LAMPE_EXT = A2;
const int BROCHE_LAMPE_JARDIN = A3;
const int BROCHE_CREPUSCULAIRE = A6;

// ═══════════════════════════════════════════════════════════════════════════
// CONFIGURATION (à ajuster selon installation)
// ═══════════════════════════════════════════════════════════════════════════

const int SEUIL_CREPUSCULAIRE = 300;
const int TEMP_MAXI = 37;
const int TEMP_MINI = 28;
const int TEMP_CONSIGNE_DEFAUT = 32;
const int DUREE_FORCEE_POMPE_MIN = 15;  // minutes
const int DUREE_FORCEE_LAMPE_H = 2;     // heures
const int DELAI_FILTRE_JET_J = 7;       // jours
const int DELAI_FILTRE_HP_J = 21;       // jours
const int HEURE_FIN_ECLAIRAGE = 23;
const int PLAGE_FILTRATION[2][2] = { { 2, 5 }, { 12, 14 } };

const float VERSION = 1.71;

// Codes IR télécommande
const unsigned long IR_MASSAGE1 = 0xFD00FF;
const unsigned long IR_TEMP_PLUS = 0xFD8877;
const unsigned long IR_TEMP_MOINS = 0xFD9867;
const unsigned long IR_MENU = 0xFD609F;
const unsigned long IR_LAMPE_INT = 0xFD10EF;
const unsigned long IR_LAMPE_EXT = 0xFD906F;
const unsigned long IR_JARDIN = 0xFD50AF;
const unsigned long IR_POMPE_PV = 0xFD30CF;
const unsigned long IR_BULLEUR = 0xFDB04F;
const unsigned long IR_POMPE_GV = 0xFD708F;
const unsigned long IR_OK = 0xFDA857;
const unsigned long IR_ECRAN = 0xFD40BF;
const unsigned long IR_BUG = 0xFFFFFFFF;

// ═══════════════════════════════════════════════════════════════════════════
// OBJETS MATÉRIELS
// ═══════════════════════════════════════════════════════════════════════════

OneWire busTemp(BROCHE_SONDE_TEMP);
DallasTemperature sondeTemp(&busTemp);
SoftwareSerial bluetooth(BROCHE_BT_RX, BROCHE_BT_TX);
RTC_DS3231 horloge;
hd44780_I2Cexp ecran;
IRrecv recepteurIR(BROCHE_IR);
decode_results signalIR;

const int LCD_COLS = 20;
const int LCD_LIGNES = 4;

// ═══════════════════════════════════════════════════════════════════════════
// VARIABLES GLOBALES
// ═══════════════════════════════════════════════════════════════════════════

DateTime maintenant;

char nomJours[7][12] = { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" };

int tempActuelle = 0;
int consigneTemp = TEMP_CONSIGNE_DEFAUT;
int valeurCrepuscule = 0;

bool chauffageActif = false;
bool pompeChauffage = false;
bool eclairageActif = false;
bool aerationActive = false;
bool retroEclairage = true;

bool lampeForceeInt = false;
bool lampeForceeExt = false;
bool lampeForceeJardin = false;
bool pumpPvForcee = false;
bool bulleurForce = false;
bool pompeGvForcee = false;

bool pompeGvActive = false;
bool pompePvActive = false;

bool alerteFiltreJet = false;
bool alerteFiltreHP = false;
bool filtrationActive = false;

DateTime finMarcheForceePompe;
DateTime finMarcheForceeLampe;
DateTime prochainNettoyageJet;
DateTime prochainNettoyageHP;

String messageRecu;

// ═══════════════════════════════════════════════════════════════════════════
// UTILITAIRES
// ═══════════════════════════════════════════════════════════════════════════

// Affiche un nombre sur 2 chiffres (avec zéro initial si besoin)
void afficherDeuxChiffres(int valeur) {
  if (valeur <= 9) ecran.print("0");
  ecran.print(valeur);
}

// Imprime hh:mm:ss d'un DateTime sur le LCD
void afficherHeure(DateTime dt) {
  afficherDeuxChiffres(dt.hour());
  ecran.print(":");
  afficherDeuxChiffres(dt.minute());
  ecran.print(":");
  afficherDeuxChiffres(dt.second());
}

// Imprime jj/mm d'un DateTime sur le LCD
void afficherDateCourte(DateTime dt) {
  if (dt.day() <= 9) ecran.print('0');
  ecran.print(dt.day());
  ecran.print('/');
  if (dt.month() <= 9) ecran.print('0');
  ecran.print(dt.month());
}

// Nettoie le signal IR et remet results.value à zéro
void nettoyerIR() {
  Serial.print("nettoyage IR : ");
  Serial.println(signalIR.value, HEX);
  recepteurIR.resume();
  signalIR.value = 0;
  delay(100);
}

// Reçoit un éventuel message Bluetooth dans messageRecu
void lireBluetooth() {
  messageRecu = "";
  while (bluetooth.available()) {
    delay(3);
    messageRecu += (char)bluetooth.read();
  }
  if (messageRecu.length() > 0)
    Serial.println(messageRecu);
}

// Envoie l'état du spa au module Bluetooth
void envoyerBluetooth() {
  bluetooth.print("*T" + String(tempActuelle) + "*");
  bluetooth.print("*C" + String(consigneTemp) + "*");
  bluetooth.print(chauffageActif ? "*RR255G0B0*" : "*RR255G255B255*");

  if (alerteFiltreJet) bluetooth.print("*MNettoyage filtre Jet*");
  else if (alerteFiltreHP) bluetooth.print("*MNettoyage filtre HP*");
  else if (filtrationActive) bluetooth.print("*MFiltration en cours*");
  else if (aerationActive) bluetooth.print("*MAeration en cours*");
  else bluetooth.print("*MSpaduino*");
}

// Lit la température de la sonde et met à jour tempActuelle
void lireTemperature() {
  sondeTemp.requestTemperatures();
  tempActuelle = (int)sondeTemp.getTempCByIndex(0);
}

// ═══════════════════════════════════════════════════════════════════════════
// AFFICHAGE LCD
// ═══════════════════════════════════════════════════════════════════════════

void afficherEtatGeneral() {
  ecran.setCursor(0, 0);
  if (alerteFiltreHP) ecran.print("**Nettoie Filtre HP**");
  else if (alerteFiltreJet) ecran.print("**Nettoie Filtre Jet*");
  else if (filtrationActive) ecran.print("*****Filtration*****");
  else {
    ecran.print("SpaDuino - ");
    ecran.print(nomJours[maintenant.dayOfTheWeek()]);
  }
}

void afficherDateHeure() {
  ecran.setCursor(0, 1);
  ecran.print(maintenant.day());
  ecran.print("/");
  ecran.print(maintenant.month());
  ecran.print("/");
  ecran.print(maintenant.year());
  ecran.print("-");
  afficherDeuxChiffres(maintenant.hour());
  ecran.print(":");
  afficherDeuxChiffres(maintenant.minute());
  ecran.print(":");
  afficherDeuxChiffres(maintenant.second());
}

void afficherTemperatures() {
  ecran.setCursor(0, 2);
  ecran.print("Temp:");
  ecran.print(tempActuelle);
  ecran.print("C/");
  ecran.print(consigneTemp);
  ecran.print("C");
}

void afficherActionneurs() {
  ecran.setCursor(0, 3);
  ecran.print("Ch:");
  ecran.print(chauffageActif);
  ecran.print(" Ecl:");
  ecran.print(eclairageActif);
  ecran.print(" PV:");
  ecran.print(pompePvActive);
  ecran.print(" GV:");
  ecran.print(pompeGvActive);
}

void afficherEcran() {
  if (maintenant.hour() == 0 && maintenant.minute() == 0 && maintenant.second() == 0)
    ecran.clear();

  afficherEtatGeneral();
  afficherDateHeure();
  afficherTemperatures();
  afficherActionneurs();
}

void afficherDebugSerie() {
  Serial.print("Il est : ");
  Serial.print(maintenant.hour());
  Serial.print(":");
  if (maintenant.minute() <= 9) Serial.print("0");
  Serial.print(maintenant.minute());
  Serial.print(":");
  if (maintenant.second() <= 9) Serial.print("0");
  Serial.println(maintenant.second());
  Serial.print("Temperature : ");
  Serial.println(tempActuelle);
  Serial.print("Crepusculaire : ");
  Serial.println(valeurCrepuscule);
}

// ═══════════════════════════════════════════════════════════════════════════
// GESTION FILTRATION
// ═══════════════════════════════════════════════════════════════════════════

void gererAlerteFiltre() {
  if (prochainNettoyageHP.day() == maintenant.day()
      && prochainNettoyageHP.month() == maintenant.month()
      && prochainNettoyageHP.minute() == maintenant.minute())
    alerteFiltreHP = true;

  if (prochainNettoyageJet.day() == maintenant.day()
      && prochainNettoyageJet.month() == maintenant.month()
      && prochainNettoyageJet.minute() == maintenant.minute())
    alerteFiltreJet = true;
}

void gererFiltration() {
  filtrationActive =
    (maintenant.hour() >= PLAGE_FILTRATION[0][0] && maintenant.hour() < PLAGE_FILTRATION[0][1]) || (maintenant.hour() >= PLAGE_FILTRATION[1][0] && maintenant.hour() < PLAGE_FILTRATION[1][1]);
}

// ═══════════════════════════════════════════════════════════════════════════
// GESTION ÉCLAIRAGE
// ═══════════════════════════════════════════════════════════════════════════

void gererEclairage() {
  valeurCrepuscule = analogRead(BROCHE_CREPUSCULAIRE);
  bool nuit = (valeurCrepuscule <= SEUIL_CREPUSCULAIRE
               && maintenant.hour() >= 17
               && maintenant.hour() < HEURE_FIN_ECLAIRAGE);

  // Lampe extérieure spa
  bool allumerExt = nuit || lampeForceeExt;
  digitalWrite(BROCHE_LAMPE_EXT, allumerExt ? LOW : HIGH);

  // Lampe jardin
  bool allumerJardin = nuit || lampeForceeJardin;
  digitalWrite(BROCHE_LAMPE_JARDIN, allumerJardin ? LOW : HIGH);

  eclairageActif = allumerExt || allumerJardin;

  // Arrêt automatique lampes forcées
  if (finMarcheForceeLampe.hour() == maintenant.hour()) {
    digitalWrite(BROCHE_LAMPE_INT, HIGH);
    digitalWrite(BROCHE_LAMPE_EXT, HIGH);
    digitalWrite(BROCHE_LAMPE_JARDIN, HIGH);
    lampeForceeInt = lampeForceeExt = lampeForceeJardin = false;
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// GESTION POMPES
// ═══════════════════════════════════════════════════════════════════════════

void gererPompes() {
  // Arrêt automatique marche forcée pompes
  if (finMarcheForceePompe.minute() == maintenant.minute()) {
    digitalWrite(BROCHE_POMPE_GV, LOW);
    digitalWrite(BROCHE_COMMUN_POMPE, LOW);
    digitalWrite(BROCHE_BULLEUR, HIGH);
    pompeGvActive = pompeGvForcee = pumpPvForcee = bulleurForce = false;
  }

  // Pompe petite vitesse : active si filtration, marche forcée ou chauffage en cours
  if (filtrationActive || pumpPvForcee || pompeChauffage) {
    if (!pompeGvActive) {
      pompePvActive = true;
      digitalWrite(BROCHE_POMPE_PV, HIGH);
      digitalWrite(BROCHE_COMMUN_POMPE, HIGH);
    }
  } else if (!pompeGvForcee) {
    pompePvActive = false;
    digitalWrite(BROCHE_POMPE_PV, LOW);
    digitalWrite(BROCHE_COMMUN_POMPE, LOW);
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// GESTION CHAUFFAGE
// ═══════════════════════════════════════════════════════════════════════════

void gererChauffage() {
  if (tempActuelle <= consigneTemp - 1) {
    pompeChauffage = true;
    if (!chauffageActif) delay(3000);  // pause avant premier allumage résistance
    if (pompePvActive || pompeGvActive) {
      digitalWrite(BROCHE_RESISTANCE, HIGH);
      chauffageActif = true;
    }
  }

  if (tempActuelle >= consigneTemp + 1) {
    if (chauffageActif) delay(5000);  // pause avant extinction résistance
    digitalWrite(BROCHE_RESISTANCE, LOW);
    chauffageActif = false;
    pompeChauffage = false;
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// AÉRATION JOURNALIÈRE
// ═══════════════════════════════════════════════════════════════════════════

void programmeAeration() {
  ecran.clear();
  ecran.setCursor(0, 1);
  ecran.print("SpaDuino - Aeration");
  digitalWrite(BROCHE_POMPE_GV, LOW);
  digitalWrite(BROCHE_BULLEUR, LOW);
  digitalWrite(BROCHE_POMPE_PV, HIGH);
  digitalWrite(BROCHE_COMMUN_POMPE, HIGH);
  delay(59990);
  digitalWrite(BROCHE_BULLEUR, HIGH);
  digitalWrite(BROCHE_POMPE_PV, LOW);
  digitalWrite(BROCHE_COMMUN_POMPE, LOW);
}

void gererAeration() {
  if (pompeGvActive) return;
  if ((maintenant.hour() == 8 && maintenant.minute() == 0) || (maintenant.hour() == 13 && maintenant.minute() == 0) || (maintenant.hour() == 19 && maintenant.minute() == 0))
    programmeAeration();
}

// ═══════════════════════════════════════════════════════════════════════════
// MENU FILTRE
// ═══════════════════════════════════════════════════════════════════════════

void afficherMenuFiltre() {
  ecran.setCursor(0, 0);
  ecran.print("  Nettoyage Filtre  ");
  ecran.setCursor(0, 1);
  ecran.print("Jet:");
  afficherDateCourte(prochainNettoyageJet);
  ecran.print(" - HP:");
  afficherDateCourte(prochainNettoyageHP);
  ecran.setCursor(0, 2);
  ecran.print("1:Jet 2:HP - V");
  ecran.print(VERSION);
  ecran.setCursor(0, 3);
  ecran.print("3:Ecran  OK:Exit    ");
}

void afficherConfirmationRAZ() {
  ecran.clear();
  ecran.setCursor(0, 0);
  ecran.print("confirmer RAZ       ");
  ecran.setCursor(0, 1);
  ecran.print("OK ou 0 pour annuler");
}

// Attend une confirmation OK ou annulation 0 ; retourne true si confirmé
bool attendreConfirmation() {
  bool reponse = false;
  bool enAttente = true;
  while (enAttente) {
    lireBluetooth();
    if (recepteurIR.decode(&signalIR) || messageRecu.length() > 0) {
      if (signalIR.value == IR_OK || messageRecu == "OK") {
        reponse = true;
        enAttente = false;
      } else if (signalIR.value == IR_BULLEUR || messageRecu == "0") {
        reponse = false;
        enAttente = false;
      } else if (signalIR.value == IR_BUG) {
        nettoyerIR();
      }
    }
    delay(100);
  }
  delay(100);
  recepteurIR.resume();
  ecran.clear();
  return reponse;
}

void menuFiltre() {
  DateTime maintenant = horloge.now();
  ecran.clear();
  afficherMenuFiltre();
  nettoyerIR();

  bool dansMenu = true;
  while (dansMenu) {
    lireBluetooth();
    if (recepteurIR.decode(&signalIR) || messageRecu.length() > 0) {

      if (signalIR.value == IR_MASSAGE1 || messageRecu == "1") {  // reset filtre Jet
        nettoyerIR();
        afficherConfirmationRAZ();
        if (attendreConfirmation()) {
          alerteFiltreJet = false;
          prochainNettoyageJet = maintenant + TimeSpan(DELAI_FILTRE_JET_J, 0, 0, 0);
        }
        dansMenu = false;
      } else if (signalIR.value == 0xFD807F || messageRecu == "2") {  // reset filtre HP
        nettoyerIR();
        afficherConfirmationRAZ();
        if (attendreConfirmation()) {
          alerteFiltreHP = false;
          prochainNettoyageHP = maintenant + TimeSpan(DELAI_FILTRE_HP_J, 0, 0, 0);
        }
        dansMenu = false;
      } else if (signalIR.value == IR_OK || messageRecu == "OK") {  // quitter
        nettoyerIR();
        ecran.clear();
        dansMenu = false;
      } else if (signalIR.value == IR_ECRAN || messageRecu == "3") {  // bascule rétroéclairage
        nettoyerIR();
        retroEclairage = !retroEclairage;
        ecran.setBacklight(retroEclairage ? HIGH : LOW);
        ecran.clear();
        dansMenu = false;
      } else if (signalIR.value == IR_BUG) {
        nettoyerIR();
      }
    }
    delay(100);
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// PROGRAMME MASSAGE 1
// ═══════════════════════════════════════════════════════════════════════════

void arreterMassage() {
  digitalWrite(BROCHE_POMPE_GV, LOW);
  digitalWrite(BROCHE_COMMUN_POMPE, LOW);
  pompeGvActive = false;
  digitalWrite(BROCHE_RESISTANCE, LOW);
  digitalWrite(BROCHE_BULLEUR, HIGH);
  ecran.clear();
}

void programmeMassage1() {
  const int DUREE_MASSAGE_MIN = 15;
  const int DUREE_BULLEUR_MARCHE_SEC = 45;
  const int DUREE_BULLEUR_ARRET_MIN = 3;
  const int CONSIGNE_TEMP_MASSAGE = 35;

  messageRecu = "";
  DateTime maintenant = horloge.now();
  DateTime heureFinMassage = maintenant + TimeSpan(0, 0, DUREE_MASSAGE_MIN, 0);
  DateTime prochainDebutBulleur = maintenant + TimeSpan(0, 0, DUREE_BULLEUR_ARRET_MIN, 0);
  DateTime prochainArretBulleur = maintenant;  // initialisé dans le passé immédiat

  ecran.clear();
  ecran.setCursor(0, 0);
  ecran.print("**Cycle Massage 1***");
  delay(200);

  bool massageEnCours = true;
  while (massageEnCours) {
    lireTemperature();
    maintenant = horloge.now();

    // Affichage
    ecran.setCursor(0, 1);
    ecran.print("Il est : ");
    afficherHeure(maintenant);
    ecran.setCursor(0, 2);
    ecran.print("Fin a  : ");
    afficherHeure(heureFinMassage);
    ecran.setCursor(0, 3);
    ecran.print("Temp   : ");
    ecran.print(tempActuelle);
    ecran.print("C/");
    ecran.print(CONSIGNE_TEMP_MASSAGE);
    ecran.print("C");

    // Pompe GV
    digitalWrite(BROCHE_POMPE_GV, HIGH);
    digitalWrite(BROCHE_COMMUN_POMPE, HIGH);

    // Chauffage
    if (tempActuelle <= CONSIGNE_TEMP_MASSAGE - 1) digitalWrite(BROCHE_RESISTANCE, HIGH);
    if (tempActuelle >= CONSIGNE_TEMP_MASSAGE + 1) digitalWrite(BROCHE_RESISTANCE, LOW);

    // Bulleur — allumage
    if (maintenant.minute() == prochainDebutBulleur.minute()
        && maintenant.second() == prochainDebutBulleur.second()) {
      digitalWrite(BROCHE_BULLEUR, LOW);
      prochainArretBulleur = maintenant + TimeSpan(0, 0, 0, DUREE_BULLEUR_MARCHE_SEC);
    }
    // Bulleur — extinction
    if (maintenant.minute() == prochainArretBulleur.minute()
        && maintenant.second() == prochainArretBulleur.second()) {
      digitalWrite(BROCHE_BULLEUR, HIGH);
      prochainDebutBulleur = maintenant + TimeSpan(0, 0, DUREE_BULLEUR_ARRET_MIN, 0);
    }

    lireBluetooth();

    // Fin par le temps
    if (heureFinMassage.minute() == maintenant.minute()
        && heureFinMassage.second() == maintenant.second())
      massageEnCours = false;

    // Annulation télécommande (touche 0) ou BT
    if (recepteurIR.decode(&signalIR) || messageRecu.length() > 0) {
      if (signalIR.value == IR_BULLEUR || messageRecu == "massage1") {
        delay(100);
        recepteurIR.resume();
        massageEnCours = false;
      } else if (signalIR.value == IR_BUG) {
        nettoyerIR();
      }
    }

    messageRecu = "";
    delay(10);
  }

  arreterMassage();
}

// ═══════════════════════════════════════════════════════════════════════════
// GESTION DES COMMANDES IR / BLUETOOTH
// ═══════════════════════════════════════════════════════════════════════════

void gererCommandes() {
  lireBluetooth();
  if (!recepteurIR.decode(&signalIR) && messageRecu.length() == 0) return;

  Serial.print("Code IR : ");
  Serial.println(signalIR.value, HEX);
  Serial.print("Message BT : ");
  Serial.println(messageRecu);

  // ── Massage 1 ────────────────────────────────────────────────────────────
  if (signalIR.value == IR_MASSAGE1 || messageRecu == "massage1") {
    ecran.clear();
    recepteurIR.resume();
    digitalWrite(BROCHE_POMPE_PV, LOW);
    digitalWrite(BROCHE_POMPE_GV, LOW);
    digitalWrite(BROCHE_COMMUN_POMPE, LOW);
    digitalWrite(BROCHE_BULLEUR, HIGH);
    digitalWrite(BROCHE_RESISTANCE, LOW);
    programmeMassage1();
  }

  // ── Consigne température ─────────────────────────────────────────────────
  if (signalIR.value == IR_TEMP_PLUS || messageRecu == "temp+") {
    if (consigneTemp < TEMP_MAXI) consigneTemp++;
  }
  if (signalIR.value == IR_TEMP_MOINS || messageRecu == "temp-") {
    if (consigneTemp > TEMP_MINI) consigneTemp--;
  }

  // ── Menu filtre ──────────────────────────────────────────────────────────
  if (signalIR.value == IR_MENU || messageRecu == "menu") {
    ecran.clear();
    recepteurIR.resume();
    menuFiltre();
  }

  // ── Lampe intérieure ─────────────────────────────────────────────────────
  if (signalIR.value == IR_LAMPE_INT || messageRecu == "lampeSpaInt") {
    lampeForceeInt = !lampeForceeInt;
    if (lampeForceeInt) {
      finMarcheForceeLampe = maintenant + TimeSpan(0, DUREE_FORCEE_LAMPE_H, 0, 0);
      digitalWrite(BROCHE_LAMPE_INT, LOW);
    } else {
      digitalWrite(BROCHE_LAMPE_INT, HIGH);
    }
  }

  // ── Lampe extérieure ─────────────────────────────────────────────────────
  if (signalIR.value == IR_LAMPE_EXT || messageRecu == "lampeSpaExt") {
    lampeForceeExt = !lampeForceeExt;
    if (lampeForceeExt)
      finMarcheForceeLampe = maintenant + TimeSpan(0, DUREE_FORCEE_LAMPE_H, 0, 0);
  }

  // ── Lampe jardin ─────────────────────────────────────────────────────────
  if (signalIR.value == IR_JARDIN || messageRecu == "lampeJardin") {
    lampeForceeJardin = !lampeForceeJardin;
    if (lampeForceeJardin)
      finMarcheForceeLampe = maintenant + TimeSpan(0, DUREE_FORCEE_LAMPE_H, 0, 0);
  }

  // ── Pompe petite vitesse ─────────────────────────────────────────────────
  if (signalIR.value == IR_POMPE_PV || messageRecu == "pompePV") {
    if (!pumpPvForcee && !pompeGvActive) {
      finMarcheForceePompe = maintenant + TimeSpan(0, 0, DUREE_FORCEE_POMPE_MIN, 0);
      pumpPvForcee = true;
    } else {
      pumpPvForcee = false;
    }
  }

  // ── Bulleur ──────────────────────────────────────────────────────────────
  if (signalIR.value == IR_BULLEUR || messageRecu == "bulleur") {
    bulleurForce = !bulleurForce;
    if (bulleurForce) {
      finMarcheForceePompe = maintenant + TimeSpan(0, 0, DUREE_FORCEE_POMPE_MIN, 0);
      digitalWrite(BROCHE_BULLEUR, LOW);
    } else {
      digitalWrite(BROCHE_BULLEUR, HIGH);
    }
  }

  // ── Pompe grande vitesse ─────────────────────────────────────────────────
  if (signalIR.value == IR_POMPE_GV || messageRecu == "pompeGV") {
    pompeGvForcee = !pompeGvForcee;
    if (pompeGvForcee) {
      finMarcheForceePompe = maintenant + TimeSpan(0, 0, DUREE_FORCEE_POMPE_MIN, 0);
      pompeGvActive = true;
      digitalWrite(BROCHE_POMPE_GV, HIGH);
      digitalWrite(BROCHE_COMMUN_POMPE, HIGH);
    } else {
      pompeGvActive = false;
      digitalWrite(BROCHE_POMPE_GV, LOW);
    }
  }

  recepteurIR.resume();
  nettoyerIR();
  messageRecu = "";
}

// ═══════════════════════════════════════════════════════════════════════════
// SETUP
// ═══════════════════════════════════════════════════════════════════════════

void setup() {
  Serial.begin(115200);

  int erreurLcd = ecran.begin(LCD_COLS, LCD_LIGNES);
  if (erreurLcd) {
    Serial.print("Erreur init LCD : ");
    Serial.println(erreurLcd);
    hd44780::fatalError(erreurLcd);
  }
  ecran.setBacklight(HIGH);

  sondeTemp.begin();

  pinMode(BROCHE_POMPE_PV, OUTPUT);
  digitalWrite(BROCHE_POMPE_PV, LOW);
  pinMode(BROCHE_POMPE_GV, OUTPUT);
  digitalWrite(BROCHE_POMPE_GV, LOW);
  pinMode(BROCHE_COMMUN_POMPE, OUTPUT);
  digitalWrite(BROCHE_COMMUN_POMPE, LOW);
  pinMode(BROCHE_RESISTANCE, OUTPUT);
  digitalWrite(BROCHE_RESISTANCE, LOW);
  pinMode(BROCHE_BULLEUR, OUTPUT);
  digitalWrite(BROCHE_BULLEUR, HIGH);
  pinMode(BROCHE_LAMPE_INT, OUTPUT);
  digitalWrite(BROCHE_LAMPE_INT, HIGH);
  pinMode(BROCHE_LAMPE_EXT, OUTPUT);
  digitalWrite(BROCHE_LAMPE_EXT, HIGH);
  pinMode(BROCHE_LAMPE_JARDIN, OUTPUT);
  digitalWrite(BROCHE_LAMPE_JARDIN, HIGH);

  recepteurIR.enableIRIn();
  bluetooth.begin(9600);
  delay(1000);

  // Décommenter une seule fois pour remettre l'horloge à l'heure :
  // horloge.adjust(DateTime(F(__DATE__), F(__TIME__)));

  maintenant = horloge.now();
  prochainNettoyageJet = maintenant + TimeSpan(0, 0, 2, 0);
  prochainNettoyageHP = maintenant + TimeSpan(0, 0, 1, 0);

  Serial.print("SpaDuino Version ");
  Serial.println(VERSION);
  Serial.println("Initialisation OK");
}

// ═══════════════════════════════════════════════════════════════════════════
// LOOP
// ═══════════════════════════════════════════════════════════════════════════

void loop() {
  lireTemperature();
  envoyerBluetooth();

  maintenant = horloge.now();

  afficherEcran();
  afficherDebugSerie();

  gererCommandes();

  gererAlerteFiltre();
  gererFiltration();
  gererEclairage();
  gererPompes();
  gererChauffage();
  gererAeration();
}

(note = j'ai demandé à l'IA, en lui donnant des conseils précis, de restructurer votre code. J'ai pas testé si le résultat est fonctionnel, donc l'objectif est plutôt de vous montrer à quoi peut ressembler un code structuré "correctement")

Bonjour fdufnews,

Merçi pour les commentaires, cela explique bien des choses.

Le projet est plus compliqué que je ne le croyais.

J-M-L m'a fait une proposition que je vais étudier.

Salutations,

Bonjour J-M-L

Oui j'ai aussi trouvé ces erreurs qui sont du à un disfonctionnement de la roulette de ma souris (verdict poubelle).

Salutations,

Bonjour J-M-L
Merçi pour le programme de l'IA selon vos instructions.

Je l'ai vérifié/compilé avec l'IDE et je n'ai pas eu de message d'erreur, seulement cette note:

Le croquis utilise 25406 octets (78%) de l'espace de stockage de programme. Le maximum est de 32256 octets.
Les variables globales utilisent 2001 octets (97%) de mémoire dynamique, ce qui laisse 47 octets pour les variables locales. le maximum est de 2048 octets.
La mémoire disponible faible, des problèmes de stabilité pourraient survenir.

Si je commente le code relatif à l'éclairage du spa int. et ext. de même que jardin est-ce que cela pourrait aider.

Merçi encore.

vous êtes sur UNO ou MEGA ?
Sur UNO c'est clairement trop juste au niveau de la mémoire mais vous pourriez gratter un peu en passant toutes les chaînes d'affichage qui sont constante avec la macro F("xxx") au lieu de "xxx". ça met le texte en mémoire flash et ça libère donc de la ram.

il faudrait aussi mettre

char nomJours[7][12] = { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" };

en mémoire flash - vous gagnez 84 octets sur les données.


Pour info, je viens de demander à claude de ne modifier déjà que les print pour mettre la macro F() et en compilant sur UNO ça dit

Les variables globales utilisent 1473 octets (71%) de mémoire dynamique, ce qui laisse 575 octets pour les variables locales. Le maximum est de 2048 octets.

ça devrait le faire donc.

Bonjour J-M-L

Lorsque tu parles de chaine d'affichage; est-ce que c'est partout ou il y a du code comme:

ecran.print("Temp:"); que je remplace par ecran.print(F("Temp:"));

discussion consulté Chaines de caractères en mémoire Flash et non en RAM

c'est ça

idem pour quand vous écrivez sur Serial

OK, je l'ai également fait pour Bluethoot.print

J'ai un Uno R3

Maintenant, sur cela donne:
Le croquis utilise 25500 octets (79%) de l'espace de stockage de programmes. max, est 32256 octets.
Les variables globales utilises 1423 octets (69%) de mémoire dynamique, ce qui laisse 645 octets pour les variables locales. Max. est de 2048 octets.

Pour :
char nomJours[7][12] = { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" };
Remplacé par:
const PROGMEM char nomJours[7][12] = { "Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi" };

Est-ce correcte.