Régulation par DS18B20 et débitmètre

Bonjour,

Je bosse sur un système de régulation de température de l'eau par train d'onde en partie basé sur ce code

#define PERIODE_PWM 5100L               //période pwm en ms
#define SORTIE_RELAIS 12                //pin 12
int duty = 0;                           //impulsion PWM (0<= duty <= 255)
long millisActuel = 0L;                 //millis actuel

/* ************************************************************************** */
void cdePwm(){
/* ************************************************************************** */
/*simule une sortie pwm pour la commande du relais statique*/
  millisActuel = millis();              //sauve millis()
  if(millisActuel - millisPrecedentPwm >= PERIODE_PWM){
    millisPrecedentPwm = millisActuel;  //sauve millis()
    digitalWrite(SORTIE_RELAIS, HIGH);  //sortie active
  }else if(millisActuel - millisPrecedentPwm >= PERIODE_PWM * duty / 255){
                                        //fin duty > à durée impulsion
    if(duty == 255){                    //chauffage total
      digitalWrite(SORTIE_RELAIS, HIGH);//sortie active
    } else {
      digitalWrite(SORTIE_RELAIS, LOW); //sortie inactive
    }
  }
}

C'est pour un chauffe-eau instantanné, je dispose de 2 sonde de température DS18B20 ainsi que d'un débitmètre WPSE470 .

Tout d'abord une question très bête. Avec la librairie Dallas Temperature, sous quelle forme nous est retournée la mesure ? Directement en degrés celsius ? Je demande car dans tous les tutos que j'ai pu lire qui utilise cette sonde et cette librairire ce n'est pas clair.

Si je demande ça c'est parce que je ne souhaites pas faire de régulation par PID car ce n'est pas adapté au temps de chauffe et à la réactivité de la sonde. Pour chauffer de l'eau en instantanné, la puissance nécessaire est proportionnelle au débit. Si c'était aussi simple je ferais une simple multiplication du débit par une consigne fixe. Sauf que la température de l'eau du réseau change selon les saisons (entre 9 et 21°C selon l'Ademe). Du coup la consigne d'hiver serait trop forte en été et la mesure du débit ne corrige en rien cette variation, certes étalée sur plusieurs mois, mais bien réelle.
J'ai un second mode de régulation de la température par mitigation avec de l'eau froide en sortie du chauffe-eau (mon débit de sortie est de 1L/min donc ça me permet de réguler de manière très rapide la température de sortie). Avec un simple robinet qu'on ouvre ou ferme on règle le mix d'eau chaude/froide.

Le synoptique du système de base

Du coup je souhaite faire une régulation simple

Consigne = (ΔT - Débit) * K1

avec
ΔT = Teau chauffée - Teau réseau
Débit, le débit d'eau froide
K1, une constante déterminée empiriquement

Si l'utilisateur veut baisser la température, il ouvre le robinet d'eau froide à sa convenance, ce qui fait baisser le débit passant par le chauffe-eau. Pour éviter la surchauffe de l'appareil, la consigne doit diminuer (proportionnellement au débit d'eau froide).

Sauf qu'étant donné que je ne sais pas sous quelle forme je récupère la température et donc quelles valeurs attribuer dans mon calcul.

Mais en écrivant mon texte, je me rend compte que je m'en remet totalement à la précision du débitmètre pour atteindre la consigne de température souhaitée. Du coup je me demande si rajouter une sonde en sortie de chauffe-eau serait plus sûre. A ce moment là ça donnerait un calcul du genre :

Τconsigne = (ΔT2 - ΔT3 - Débit) * K3

avec
ΔT2 = T eau chaude- Teau froide
ΔT3 = 50 - Tchaud
Débit = le débit d'eau froide
K2 = une constante déterminée arbitrairement

Ca semble plus classique, sauf que si le débit varie rapidement, cela va entrainer une variation en sortie de chauffe-eau et je ne sais pas si la sonde aura la précision ou la stabilité pour ne pas faire foirer la régulation.
Imaginons qu'on ouvre le débit d'eau froide, le débit d'eau chaude va baisser brusquement mais pas la température. Du coup ΔT3 va venir ralentir fortement le calcul vu que Teau chaude ne baissera que lentement, ce que je veux éviter.

Je sais qu'en régulation tout l'enjeu réside dans l'équilibre vitesse, précision et stabilité. Mais vu que c'est la première fois que je bosse concrètement sur de la régulation je ne sais pas ce qui serait le plus adapté dans ce cas.

Le second système donnerait ça en gros

Ah oui juste au cas où, la température sera inférieur à 55°C mais aucun risque de bactéries. En amont du système j'ai du traitement UV ainsi qu'un filtre 20µm, l'eau ne reste pas suffisamment longtemps dans les canalisations pour que les bactéries prolifèrent et j'ai même un système de purge en aval pour évacuer le max de flotte chauffée.

Bonjour,

Si la librairie lit les registres du DS18B20 sans manipulation particulière, la température retournée est exprimée en degrés Celsius entre -55°C et +125°C
Cf. la datasheet du DS18B20 - Page 6/20 - Table 1. Temperature/Data Relationship

ça dépend quelle fonction de la bibliothèque vous appelez....

Merci pour vos réponses.

J'ai une seconde question mais concernant le débitmètre : un WPSE470.
Dans les faits c'est un compteur d'eau vu qu'il envoie des impulsions tous les 3.33ml. Du coup c'est à moi de faire la base de temps pour calculer le débit et je me demandait quelle durée prendre.
Ce débitmètre va me permettre de réguler la puissance de chauffe (vu que P = Qv.Cp.ΔT).

L'amplitude de débit que le débitmètre va voir passer est [0.5 ; 1] l/min (soit [500 ; 1000] cm³), ce qui donne :

  • à Qmax 300 impulsions/min (ou 5 imp/sec)
  • à Qmin 150 impulsions/min (soit 2.5 imp/sec)

Sachant que le débit peut passer de 1l/min à 0.5l/min en une seconde je me dis que la période pour la mesure du débit ne doit pas être trop longue pour réagir suffisamment vite mais pas trop courte non plus pour éviter les erreurs de comptage de l'arduino.

A 4sec de période ça donne 20 imp à Qmax et 10 imp à Qmin. Si deux impulsions sont ratées sur ces 4 sec, à Qmax c'est 10% de marge d'erreur mais à Qmin c'est 20%.

Comme je début sur Arduino, je ne sais pas si ces erreurs d'acquisitions de données sont fréquentes et si une erreur de 20% c'est tolérable. Le truc c'est que si je double la période ben pendant 8sec j'ai mon chauffe-eau qui chauffe plein pot alors qu'il devrait pas. J'ai beau me dire que c'est le chauffage d'un flux d'eau et non pas d'un stock (de l'eau fraiche circule toujours autour de la résistance) ça me fait un peu réfléchir.

Ah oui également. D'un point de vu écriture du code, je me pose toujours cette question si je dois mettre ça dans void loop () ou bien si je dois créer une fonction à part et faire un return de ce résultat vers la loop().

Bonjour,

Mon petit cerveau lent a eut du mal à cerner ton probléme.

J'ai peut être faux, j'ai compris que celui ci était:

Comment réguler la puissance de chauffe de ton chauffe eau pour avoir en sortie une eau à 50°C?
Sachant que:
Le débit d'eaù traversant le chauffe eau varie!

Est-ce bien cela?

Quelque part, tu écris

Il faut en physique que les choses soient cohérentes.

Je présume que

Tconsigne = des °C
ΔT2 = des °C
ΔT2 = des °C
Débit = des l/mn
K3 = constante

Au nom de la cohérence, tu ne peux malheureusement pas soustraire des l/mn à des °C, ce n'est pas cohérent, ça ne veut rien dire.

Autre part, tu définis
P = QvCpΔT

Si j'ai bien compris la formule
P en Watts = débit d'eau Qv X coef Cp X la différence de température entre aprés chauffage et avant, sachant que aprés = 50°C

Donc pour que se soit cohérent, il faut
Watts = l/mn X Coef X °C
C'est à dire coef a pour unité [Watts/(l/mnX°C)].

Ça devient cohérent

Et toi, pour réguler la puissance en W du chauffe pour avoir 50°C en sortie, tu n'aurais besoin que de mesurer le débit dans le chauffe eau et la température de l'eau entrant dans le chauffe eau, et éventuellement celle en sortie
En bref, j'ai l'impression que le débitmétre devrait servir à mesurer le débit dans le chauffe eau.

Il faut aussi connaître Cp, à moins d'utiliser T2?

Par contre, ce que tu n'explique pas, c'est comment, où, sous quelle forme tu entre Tconsigne?

C'est primordial pour savoir comment traiter cette consigne et représenter ta boucle de régulation de température!

Oui désolé, la formule dans mon petit post était claqué au sol vu que je n'ai pas trop d'idées sur comment faire cette boucle de régulation.

P = Qv . Cp . ΔT

P en watt
Qv en litre/min
Cp une constante en J/Kg °C égale à 4183
ΔT la différence entre la température (fixe) que l'on souhaite atteindre en sortie de chauffe-eau (45°C) et la température de l'eau du réseaux (qui varie entre 9 et 21°C au fil des mois).

Le ΔT est là pour que la consigne s'adapte au changement de température qui a lieu tout au long de l'année.
Le Qv est là pour que la consigne s'adapte aux changements de débit qui en sortie de chauffe-eau.

Qv = Qf + Qc = 1l/min
avec
Qv le débit total
Qf le débit du circuit froid
Qc le débit du circuit chaud

Le point de puisage à un débit max de 1l/min. Que l'eau passe par le chauffe-eau ou non, la pression est la même, la seule chose qui change c'est le niveau d'ouverture du robinet sur le circuit froid. Si le robinet d'eau froide est fermé, le circuit chaud voit passer 1l/min. S'il est complètement ouvert, débit chaud = débit froid = 0.5l/min (simple système de vase communiquant).
Un relevant un des deux débit (chaud ou froid) j'ai forcément le second par soustraction et je peux donc adapter la consigne de chauffage proportionnellement à ce nouveau débit.

Ma première question concernait l'ajout d'une mesure de la tempéature en sortie de chauffe-eau pour intégrer cette donnée dans mon calcul de consigne. J'avais peur qu'en ne me basant que sur la mesure du débit la température atteinte ne soit pas fiable. Mais après réflexion, avec le temps de réaction de la sonde DS18B20 je me dis que ça ne ferait que ralentir le calcul. Sachant qu'il peut y avoir des changements brusques de débit et donc de consigne de chauffage nécessaire, la balance avantage/inconvénient ne me semblait plus très intéressante.

Dans ce cas il vaut mieux mesurer la période plutôt que la fréquence c'est-à-dire compter le temps entre 2 impulsions plutôt que compter les impulsions sur une période de temps. En mesurant la période, tu peux avoir la réponse après chaque impulsion si tu le veux.

Excuse moi, mais tu n'as pas répondu à ma question , pour ma culture technique:

@fdufnews ah oui pas bête, je sais pas trop comment on fait ça mais je vais chercher. pulseln() peut fonctionner pour ce genre de cas ? Tu regarde pulseln(), ça enclenche millis() jusqu'au prochain pulseln() et tu fais la différence entre les deux ?

@jef59 Tconsigne je vais le rentrer en °C, au final je ne récupère plus la température de l'eau chaude mais uniquement celle de l'arrivée d'eau et du coup ΔT devient Tconsigne - Tréseau.
Désolé, ma première explication était plus que lacunaire :sweat:.
Ce que je cherchais à savoir initialement c'était si mesurer l'erreur entre la température de consigne et la température à la sortie du chauffe-eau pour ensuite adapter la consigne de chauffage présentait un intérêt selon vous. Parce que si ça ralentit le calcul de manière importante, j'ai peur que l'utilisateur pète un câble si quand il souhaite réguler la température en réglant la mitigation eau chaude/eau froide ben l'inertie soit trop importante à cause du temps de réaction de la sonde.

Le problème de pulseIn() c'est qu'elle mesure soit l'état haut, soit l'état bas mais pas la période du signal. Et si le signal n'est pas carré la mesure ne vaut rien.

Voir ici à partir de ce post et jusqu'à la fin

Excuse moi de revenir là dessus, si tu connais un peu les asservissement, tu pourrais par exemple considérer que tu entre une consigne sous la forme d'une info en volts, et que ces volts pilotent une electrovanne d'ouverture de gaz, pour le chauffe eau.
Ce n'est qu'un exemple.

Mais là tu nous dis que tu rentre des °C comme consigne...
Comment?

En numérique avec un clavier et un écran?

Ton thermométre, il va te retourner quoi, des volts, une valeur numérique?

C'est nécessaire de le savoir je crois pour comparer la consigne à la valeur obtenue pour asservit au mieux.

A propos d'inertie, où que tu mette le thermométre, il aura la même inertie de toute façon, sauf si tu n'utilise pas son info température pour réguler.

Le chauffeau-eau est électrique, je vais réguler la température par train d'onde grâce un code comme ça :

//train d'onde
#define PERIODE_PWM 5100L              //période pwm en ms
#define SORTIE_HEAT1 A1                //HEAT1 pin 24 ATMega
#define SORTIE_HEAT2 A0                //HEAT2 pin 23 ATMega
#define SORTIE_HEAT3 9                 //HEAT3 pin 15 ATMega
int duty = 0;                          //impulsion PWM (0<= duty <= 255)

long millisActuel = 0L;                //millis actuel
long millisPrecedentPwm = 0L;

/* ************************************************************************** */
//code pour le taux de commutation d'un triac, à refaire pour chaque triac. Il est nécessaire d'intégrer la boucle de régulation consigne = ΔTc - 1/débit
void cdePwm(){
/* ************************************************************************** */

//consigne de température 
duty = 

/*simule une sortie pwm pour la commande du relais statique*/
  millisActuel = millis();              //sauve millis()
  if(millisActuel - millisPrecedentPwm >= PERIODE_PWM){
    millisPrecedentPwm = millisActuel;  //sauve millis()
    digitalWrite(SORTIE_HEAT1, HIGH);  //sortie active
    digitalWrite(SORTIE_HEAT2, HIGH);  //sortie active
    digitalWrite(SORTIE_HEAT3, HIGH);  //sortie active
  }else if(millisActuel - millisPrecedentPwm >= PERIODE_PWM * duty / 255){
                                        //fin duty > à durée impulsion
    if(duty == 255){                    //chauffage total
      digitalWrite(SORTIE_HEAT1, HIGH); //sortie active
      digitalWrite(SORTIE_HEAT2, HIGH); //sortie active
      digitalWrite(SORTIE_HEAT3, HIGH); //sortie active
    } else {
      digitalWrite(SORTIE_HEAT1, LOW); //sortie inactive
      digitalWrite(SORTIE_HEAT2, LOW); //sortie inactive
      digitalWrite(SORTIE_HEAT3, LOW); //sortie inactive
    }
  }

Dans les faits l'utilisateur final n'a pas la main sur la consigne de chauffage du chauffe-eau, la seule chose qu'il peut faire c'est ouvrir ou fermer plus ou moins le robinet d'eau froide pour régler la mitigation chaud/froid qui se situe après le chauffe-eau. J'ai pensé que c'était un moyen simple et robuste et qui donc convenait à l'usage que je souhaite en faire. La régulation que je souhaite faire ne concerne que l'adaptation de la consigne de température du chauffe-eau selon la fluctuation du débit d'eau chaude.

Si je prend la température de l'eau froide en entrée du chauffe-eau, je la considère comme stable car elle n'évolue qu'au fil des mois. Le 3 juillet elle sera à 21°C, le 4 juillet aussi; mais par contre le 15 janvier ce sera une autre histoire (genre 9°C). L'amplitude est grande mais le pas est très faible.

Je reviens au calcul de la puissance : P = Qv . Cp . ΔT
Qv je le récupère avec le débitmètre, Cp est une constante et ΔT avec la sonde placée sur l'entrée d'eau froide.
Une fois cette puissance obtenue, je l'intègre dans le code que je viens de donner pour moduler le taux de fermeture/ouverture de mon triac. On est loin des systèmes classiques de régulation par PID qui cherchent à éviter les dépassements ou une trop grande fluctuation. Etant donné que l'eau n'est pas stockée, il n'y a pas (ou de manière négligeable) de chauffage de l'eau par conduction par l'eau elle-même, seulement par convection de la résistance plongeante.

@fdufnews j'avais vu ce topic, mais je n'ai pas encore eu le temps de réfléchir et de comprendre tout ce qui est proposé.

Je pense à un truc. Dans l'exemple que tu me donnes ça peut fonctionner car le temps entre chaque impulsion est lent. Sauf que dans mon cas à débit max (soit 1l/min) il y a 5 imp/s soit 1 impulsion toutes les 20ms.
Est-ce que je ne risque pas de louper des impulsions pendant que le programme fait sa boucle ?
J'ai l'impression que c'est ce que disait vileroi dans le topic dont tu as mis le lien.

Bonjour,

Merci je commence à y voir plus clair.

Ton idée générale me semble tout à fait cohérente.

Il ne te reste plus qu'à compléter dans le code

Par contre, je ne suis pas sûr que ta partie de code servant à générer un signal PWM fonctionne, mais je peux me tromper.

Pour moi ça genere un PWM que dans le cas où

C'est à dire que ça ne marche que dans le cas où c'est "plein pot" je crois.

EDIT
J'ai oublié un truc important.

Un relais statique, ça s'allume de façon callé à la fréquence du réseau, si on lui demande de s'allumer ET au passage par 0 du courant alternatif.

La fréquence du PWM doit donc tenir compte de cela, si elle est dupérieur à celle du réseau EDF, ça ne va pas marcher avec le relais statique!

c'est pas plutôt toutes les 200ms ?

Le comptage utilise une interruption donc c'est le traitement de l'interruption qui bloquera ta boucle et non l'inverse. C'est pourquoi le traitement des interruptions doit toujours être le plus court possible. En l’occurrence ici cela consiste juste à mémoriser la valeur de millis() au moment de l'interruption et à faire une soustraction.

Peut-être que ça répond à ta question, mais sur ma carte électronique j'ai mis une fonction ZVS en couplant le triac avec un opto-triac


Le triac ne déclenchera qu'au passage à zéro du réseau ET si la commande de fermeture (que je cherche à mettre au point) est présente.

Le code que je viens de poster avec le PWM est basé sur ce topic. Ca ne fait qu'une semaine que je bosse sur le code de la régulation et comme vous pouvez le constater pleins d'incohérences sont là parce que j'ai pris des trucs à droite à gauche qui me semblait pouvoir fonctionner ensemble.
Pour la commande duty de ce code, ça me paraissait étrange le == qui est du coup très restrictif.

@fdufnews Je viens de trouver un tuto où ils parlent de ce problème d'interruption qu'il semble régler grâce à attachInterrupt() (dont il était fait mention sur le premier topic de débitmètre).
Dans mon cas ça donnerait ça pour les attachInterrupt() et detachInterrupt() ?

void setup()
{
  attachInterrupt (digitalPinToInterrupt(6),compteurImpulsions,FALLING);
}    
void loop()
{
  unsigned long temps;
  unsigned long mesureTempo = 1000;
  volatile int compteurImpulsions;
  int consommation = 0;
  float  debit = 0;
  unsigned long mesureMillis =0L;
  if ((temps = millis() - mesureMillis )>= mesureTempo)     // Si fin de periode de mesures
  {
      detachInterrupt(digitalPinToInterrupt(6)); 
      int comptage = compteurImpulsions;
      compteurImpulsions = 0;
      consommation+= comptage;
      Serial.print("consommation = " ); Serial.print(consommation); Serial.println(" L");
    
      debit = (float) comptage/temps*600.0;
      Serial.println("Debit = "); Serial.print(debit,3); Serial.println(" L/min");
      Serial.println();
      
      mesureMillis = millis();     // Redémarrage du chrono
    
     attachInterrupt (digitalPinToInterrupt(6),compteurImpulsions,FALLING);

  }
}

On initialise l'interruption dans le void setup, on le désactive au début de la mesure de débit pour le réactiver ensuite pour que le reste du programme ne pollue pas la mesure en faisant rater des impulsions ?

Par contre je viens de me rendre compte que la fonction interrupt ne fonctionne que sur les pins 2, 3 sur mon Arduino Uno. Semblerait que je doivent (encore modifier ma PCB)

Je ne te fait aucun reproche à ce sujet, au contraire, c'est en cherchant, testant et corrigeant ses "incohérences" qu'on progresse.
:+1:

Les interruptions ne doivent être masquées que lorsque tu accèdes aux variables déclarées volatile, puisqu'elles sont partagées avec la routine d'interruption .
Donc tu devrais plutôt avoir quelque chose comme ça

  if ((temps = millis() - mesureMillis )>= mesureTempo)     // Si fin de periode de mesures
  {
      detachInterrupt(digitalPinToInterrupt(6)); 
      int comptage = compteurImpulsions;
      compteurImpulsions = 0;
      mesureMillis = millis(); 
      attachInterrupt (digitalPinToInterrupt(6),compteurImpulsions,FALLING);
    .....

Et pour réduire le temps où les interruptions sont bloquées, tu ferais mieux de déclarer comptage en global.
Le reste du programme ne peut pas faire rater des impulsions c'est le principe de l'interruption qui justement prend la main sur le programme qui tourne en tâche de fond. Et, pour que la gestion des interruptions ne perturbe pas le fonctionnement de la tâche de fond, la routine d'interruption doit être réduite au minimum.