Problème pour brancher 7 sondes DS18B20 sur le même bus

static bool modeSilence = false;
  static uint32_t debutModeSilence = 0;
  static const uint32_t dureeModeSilence = 10000ul;

il faut savoir que dès que la prg démarre, le µ contrôleur compte les milli secondes, il incrémente un “compteur continue” il commence à 0 pour boucler au bout de 40 jours ( il me semble)

pour chronométrer un événement, on initialise une variable avec la valeur du compteur continu

par exemple 123456789

puis, à chaque tour de boucle, on contrôle si le temps alloué est passé.

exemple: en cas d’alarme, on peut faire tire le buzzer pendant 10 secondes, en soustrayant

le compteur continu et le compteur de début

millis()-compteur début >= temps alloué ( pour le buzzer, c’est 10 secondes )

même principe pour le clignotement des leds

pour faire clignoter les leds 1 fois par seconde, la période de la fréquence sera 1000 millisecondes

donc période / 2=500 . d’ ou allumée 500ms et éteinte 500ms

pour 2 clignotements par secondes, tu divises par 4 résultat = 250ms

pour compter le temps pendant lequel la led est allumée, on initialise un “chrono” avec la valeur actuelle du “compteur continu”

exemple: le compteur continue vaut 123456789ms, alors début clignotement est initialisé à 123456789ms

et on allume la led. Puis, à chaque tour de boucle on contrôle si le compteur continu à atteint le nombre de ms que l’on a alloué en demi période

soit par exemple: compteur_continu - début clignotement > = demi période

si oui, on éteint la led.

struct Capteur {
  const char * nom;
  DeviceAddress adresse;
  float derniereTemperature;
  bool besoinAffichage;
};

dans la prg, tu auras autant de structures qu’il y a de sondes

dans la fonction mettre à jour les températures tu as une boucle for qui va

relever la température de la sonde ”i” et sauvegarder cette température si elle est différente de la température relevée précédement pour cette même sonde “i”

for (byte i = 0; i < nbCapteursTemperature; i++) {
    float t = capteurs.getTempC(lesCapteurs[i].adresse);
    if (!isnan(t) && (isnan(lesCapteurs[i].derniereTemperature) || abs(t - lesCapteurs[i].derniereTemperature) >= 0.1)) {
      lesCapteurs[i].derniereTemperature = t;
      lesCapteurs[i].besoinAffichage = true;
      modif = true;
    }
  }

si il y a effectivement une sauvegarde parce que nouvelle température différente de température précédente, on initialise un flag pour demander par la suite une mise à jour de l’affichage.

le code avec des commentaires qui seront sans doute un peu plus pertinents

constexpr byte nbCapteursTemperature = sizeof lesCapteurs / sizeof *lesCapteurs;
// constexpr impose une évaluation à la compilation. Le type byte suffit car le nombre de capteurs est petit et non négatif.
// L’expression sizeof lesCapteurs / sizeof *lesCapteurs calcule le nombre d’éléments du tableau en divisant la taille totale du tableau par la taille d’un élément.

const uint32_t demiPeriodeClignotement = 250;
// uint32_t correspond au type retourné par millis() et évite tout débordement ou conversion implicite.
// La valeur 250 ms représente une demi-période, soit un clignotement complet toutes les 500 ms.

const float seuilHP = 50.0;
// Seuil de température, en degrés Celsius, au-delà duquel l’alarme HP est considérée active.

const byte indiceHP = 0;
// Indice constant du capteur HP dans le tableau lesCapteurs.

const float seuilPC = 50.0;
// Seuil de température du capteur PC déclenchant l’alarme correspondante.

const byte indicePC = 4;
// Indice constant du capteur PC dans le tableau lesCapteurs.

const float seuilECS = 50.0;
// Seuil de température du capteur ECS déclenchant l’alarme correspondante.

const uint8_t indiceECS = 5;
// Indice constant du capteur ECS dans le tableau lesCapteurs.

enum : uint8_t { AUCUNE_ALARME = 0, ALARME_HP = 1, ALARME_PC = 2, ALARME_ECS = 4 };
// Chaque valeur correspond à un bit distinct afin de pouvoir représenter plusieurs alarmes simultanément dans une variable entière.

uint8_t alarmesEnCours = AUCUNE_ALARME;
// Variable bitmask indiquant quelles alarmes sont actives à un instant donné.

void animations() {
  static bool modeSilence = false;
  // Variable persistante conservant l’état du mode silence entre deux appels de la fonction.

  static uint32_t debutModeSilence = 0;
  // Instant, en millisecondes depuis le démarrage, auquel le mode silence a été activé.

  static const uint32_t dureeModeSilence = 10000ul;
  // définition d'une constante pour la durée maximale du mode silence en millisecondes.

  boutonSilence.poll();
  // Mise à jour de l’état interne du bouton silence par appel à la fonction dédiée de la bibliothèque 

  if (boutonSilence.onPress()) {
    // si le bouton est appuyé on rentre dans ce if

    modeSilence = not modeSilence;
    // Inversion explicite de l’état du mode silence à chaque appui.

    digitalWrite(pinLedSilence, modeSilence ? HIGH : LOW);
    // La LED reflète directement l’état logique du mode silence. chercher opérateur ternaire pour la syntaxe du second paramètre 

    if (modeSilence) debutModeSilence = millis();
    // Mémorisation de l’instant d’activation pour mesurer la durée écoulée.
  }

  if (modeSilence && millis() - debutModeSilence >= dureeModeSilence) {
    // si  la durée maximale du mode silence est dépassée.

    modeSilence = false;
    digitalWrite(pinLedSilence, LOW);
    // alors désactivation automatique du mode silence.
  }

  if (lesCapteurs[indiceHP].derniereTemperature >= seuilHP) {
    // si on a dépassé le seuil pour HP.

    static uint32_t chrono = 0;
    // Référence temporelle persistante utilisée pour cadencer le clignotement.

    if (millis() - chrono >= 100) {
      digitalWrite(pinLedHP, digitalRead(pinLedHP) == HIGH ? LOW : HIGH);
      // Inversion de l’état de la LED toutes les 100 ms, ce qui fixe la fréquence de clignotement.

      chrono = millis();
      // on se souvient du moment de la dernière inversion
    }

    alarmesEnCours |= ALARME_HP;
    // Activation du bit correspondant à l’alarme HP sans modifier les autres bits. |= est un OU bit à bit
  } else {
    digitalWrite(pinLedHP, LOW);
    alarmesEnCours &= ~ALARME_HP;
    // Effacement du bit ALARME_HP par masquage binaire, les autres alarmes restent inchangées.
  }

  if (lesCapteurs[indicePC].derniereTemperature >= seuilPC) { // idem pour PC
    static uint32_t chrono = 0;
    if (millis() - chrono >= 100) {
      digitalWrite(pinLedPC, digitalRead(pinLedPC) == HIGH ? LOW : HIGH);
      chrono = millis();
    }

Vos réponses sont complémentaires et me permettent de comprendre la suite de ce programme. Les annotations de JLM ont une précision impressionnante.

Pour essayer de mieux comprendre la ligne (uint8_t alarmesEnCours = AUCUNE_ALARME;) et apprendre à utiliser le moniteur Série :smiley: J'ai modifié momentanément le wokwi de JLM https://wokwi.com/projects/453850070227305473

j'ai écrit dans la loop :

  Serial.print ("alarmesEnCours") ; Serial.println(alarmesEnCours) ;

Résultat :

- Si l'on pousse successivement le curseur de l'une des sonde au-delà de son seuil, on retrouve les valeurs de l'enum : uint8_t {AUCUNE_ALARME = 0, ALARME_HP = 1, ALARME_PC = 2, ALARME_ECS = 4}

- Si l'on pousse plusieurs curseurs on constate que les valeurs s'additionnent (on obtient par exemple 7 lorsque les seuils des 3 sondes sont dépassés ; on obtient 3 si les seuils des deux premières sondes sont dépassés).

Cette lecture sur le moniteur série permet effectivement de mieux comprendre le fonctionnement du programme.

La ligne : constexpr byte nbCapteursTemperature = sizeof lesCapteurs / sizeof * lesCapteurs; semble servir à l'écriture sur le LCD a-t-elle une autre fonction ?

Je n'ai pas compris, dans la pratique, à quoi sert la ligne :

const uint32_t demiPeriodeClignotement = 250;

Etant donné que l'on fixe la durée des clignotements des Led et du Buzzer par la suite dans le programme.

Tout le reste me semble désormais plus accessible, encore merci pour votre pédagogie.

oui c'est le principe du masque informatique. Je prends un octet, donc 8 bits qui peuvent être 0 ou 1 - dont je me sers pour dire si l'alarme est activée (1) ou pas (0).

si les 3 bits sont à 0 (000) on a la valeur AUCUNE_ALARME
si on a par exemple 101, ça fait 5 qui est 4+1 donc on a l'alarme ECS et l'alarme HP qui sont activées

si je ne l'ai pas utilisée, c'est un bug :slight_smile:

J’ai désormais un super programme pour gérer 6 DS18B20, 4 Led, 2 buzzer branchés via un transistor le tout avec un affichage sur LCD I2C 20/5… et surtout un bouton avec lequel je peux suspendre l’alarme durant une période appropriée… sans que le programme soit ralentit par des délais.

Et en plus, j’ai intégré une bonne partie du fonctionnement de ce programme qui me parait désormais accessible.

Merci JLM, merci à vous tous qui m’avez aidé dans cet apprentissage.

Je vais maintenant pouvoir élaborer un nouveau programme pour gérer deux thermocouples avec incrémentation des seuils.

bravo et l'important c'est d'avoir compris comment ça fonctionne !

What you are observing here is a classic 1-Wire bus limitation rather than a simple wiring mistake or software issue.

With multiple DS18B20 sensors distributed over long cables (especially with total lengths like ~40 m and multiple branches), the problem is mainly signal integrity:

  • the cable capacitance increases significantly
  • signal edges become slower
  • reflections occur at branches and impedance changes
  • different cable lengths introduce timing distortions

This combination can easily break communication on a timing-critical protocol like 1-Wire.

The fact that everything works with fewer sensors or shorter cables, and then suddenly fails when all are connected, is a strong indication that the bus has reached its physical limits.

Shortening the cables improves the situation because it reduces capacitance and reflections, which is exactly what you observed.

However, an important point is often overlooked: even in “working” setups, occasional communication errors are normal under real-world conditions. The goal should not be to eliminate them completely (which is often impossible at larger scales), but to design the system to handle them robustly.

This typically includes:

  • detecting invalid readings (e.g. -127°C, 85°C)
  • retrying reads where appropriate
  • validating values before using them
  • implementing a fail-safe strategy for the output

For larger installations, it is also worth reconsidering the hardware architecture (e.g. shorter bus segments, fewer branches, or multiple buses instead of a single long one).

I documented measurements and practical strategies for handling these situations (including error rates over time and recovery behavior) here: