Une régulation simple

dimanche 3 février 2013
par  Finizi
popularité : 100%

Le but est d’automatiser le fonctionnement d’un chauffe eau solaire avec un régulateur différentiel de température.

La principale fonction du régulateur est de mettre en service une pompe de circulation lorsque les conditions de température sont atteintes et respectées. Il y a deux capteurs de température
- T1, qui indique la température dans le panneau solaire
- T2, qui indique la température dans le bas du ballon d’eau chaude

Principe de fonctionnement
Lorsque T1 dépasse T2 de plus de 8°C, le circulateur est mis en route. Au bout de 10 minutes, le temps que les températures s’équilibrent sur tout le circuit, on commence à vérifier les conditions d’arrêt du circulateur. Il restera en fonctionnement tant que T1 dépassera T2 de plus de 4°C.


 Le matériel nécessaire

Pour faire cette régulation de base, nous allons utiliser :
- Une carte Arduino Uno avec son alimentation
- Un relai 220V, commandé en 5 volts
- Deux sondes de température

Une carte Arduino Uno
C’est la carte électronique que nous allons programmer et qui va commander la mise en route du régulateur.
La principale documentation francophone se trouve sur le site mon-club-elec.fr
Son prix : de 20 à 25 € plus 5 à 10 € pour l’alimentation

Un relai 220V, commandé en 5 volts
Son prix : 8 à 20 €

Deux sondes de température
Il existe plusieurs types de sonde de température :
- Les capteurs résistifs, type PT100 ou PT1000 sont très utilisés dans l’industrie. Il présentent une résistance qui varie selon la température.
- Les capteurs silicium analogiques qui délivrent une tension proportionnelle à la température. Le plus connu est le LM35.
- Les capteurs silicium numériques qui donnent directement la température via un protocole de communication. C’est ce type de capteur, le DS18B20 pour être précis, que nous allons utiliser. Une bonne description en est faite sur le site mon-club-elec.fr. Chaque capteur possède sa propre adresse. Cette page permet de déterminer l’adresse de chaque capteur, adresse qu’il faudra connaitre pour programmer la régulation.
Son prix : de moins de 2 € nu à plus de 15 € monté dans un doigt étanche en inox (capteur câblé).

Une résistance
Une résistance de 4,7 kΩ

A titre d’exemple, le total revient à moins de 62 € port compris chez SnootLab.


 Le montage

On va utiliser le port numérique 3 de l’Arduino pour lire les températures. Le port analogique 4 (A4) sera utilisé pour commander le relai.

PNG - 43.6 ko
Schéma
Schéma de montage d’une régulation simple.

Un des capteurs se trouvant dans les panneaux solaires, souvent sur le toit de la maison, il peut y avoir une certaine distance entre la carte Arduino qui se trouvera en général près du ballon d’eau chaude, et le capteur. La solution la moins onéreuse que j’ai trouvé pour relier les capteurs de température est d’utiliser du câble Ethernet. On en trouve à 35 € la bobine de 100 mètres. J’utilise 3 des 4 paires torsadées à raison d’une paire par patte :
- la paire orange pour la masse, qui correspond au noir du capteur câblé
- la paire verte pour les données, qui correspond au jaune du capteur câblé
- la paire marron pour l’alimentation +5V, qui correspond au rouge du capteur câblé

JPEG - 14 ko
Montage DS18B20
Câblage du capteur DS18B20

 Le programme

Et voici la partie la plus redoutable. Mais vous allez voir, il n’y a rien de très sorcier.
Je n’ai pas l’intention ici de détailler la programmation d’une carte Arduino. Pour cela, vous trouverez toutes les explications et les tutoriaux sur l’excellent site francophone mon-club-elec.fr.
Le code se décompose en 4 parties, dans cet ordre :
- 1. Les déclarations
- 2. L’initialisation
- 3. La boucle
- 4. Les fonctions diverses

1. Les déclarations
C’est ici que seront déclarées les bibliothèques utilisées. L’avantage de la plateforme Arduino est qu’il existe de nombreuses bibliothèques prêtes à l’emploi. Dans le cas qui nous intéresse, c’est toute la gestion des sondes de température qui existe déjà. Il suffira de reprendre des fonctions de la bibliothèque, fonctions que nous n’auront donc pas à écrire.
On va aussi déclarer les variables globales, c’est à dire en gros, les variables toujours disponibles, ainsi que les objets.

2. L’initialisation
C’est la fonction setup(). Cette fonction n’est exécutée qu’une seule fois au démarrage de la carte. En fait, cela sert à initialiser la carte avant de pouvoir s’en servir.
Ici, on va initialiser les broches de la carte, alimenter les capteurs de température, stopper la pompe de circulation si celle-ci est en route. Le but est de mettre l’installation dans un état que l’on va pouvoir ensuite exploiter. Il ne faut pas perdre de vue qu’on ne sait pas à priori dans quel état se trouve l’installation.

3. La boucle
Voici le cœur du programme. C’est la fonction loop(). Une fois l’initialisation terminée, la carte va exécuter cette fonction en boucle infinie. C’est à dire que aussitôt terminée, elle est relancée.
C’est ici que vont être gérés les différents états de l’installation, et que les décisions vont se prendre en fonction des informations renvoyées par les capteurs de température.
En résumé, on commence par lire les capteurs de température, et en fonction de l’état dans lequel se trouve l’installation, une action et / ou un changement d’état peut être décidé.
A la fin, on attend un petit peu, ici une demi seconde, ce qui est très court, car les températures n’évoluent pas si vite.

4. Les fonctions diverses
Le but de ces fonctions est souvent de structurer le code de la boucle. Soit c’est un bout de code utilisé à plusieurs endroits, auquel cas il est intéressant de ne l’écrire qu’une seule fois, soit c’est juste un bout de code que l’on isole et qui aura désormais un nom. Au niveau du programme en lui même, cela ne change rien, par contre, au niveau de la lecture du code et de sa compréhension par l’auteur ou un autre développeur, cela clarifie grandement les choses.

Et voici l’intégralité du code.

  1.  
  2. // --- Programme Arduino ---
  3. // Copyright finizi - Créé le 14/07/2012
  4. // www.DomoEnergyTICs.com
  5. //  Code sous licence GNU GPL :
  6. //  This program is free software: you can redistribute it and/or modify
  7. //  it under the terms of the GNU General Public License as published by
  8. //  the Free Software Foundation, either version 3 of the License,
  9. //  or any later version.
  10. //  This program is distributed in the hope that it will be useful,
  11. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. //  GNU General Public License for more details.
  14. //  You should have received a copy of the GNU General Public License
  15. //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. /*
  18. ******************************************************************************************************************************************************
  19. **  
  20. **      Utilisation des broches
  21. **      
  22. **      Analogiques
  23. **        4 :  => Solar Relai pompe de circulation
  24. **
  25. **       Digitales
  26. **        3 :  => bus OneWire
  27. **
  28. ******************************************************************************************************************************************************
  29. */
  30.  
  31.  
  32. /*
  33. ******************************************************************************************************************************************************
  34. **
  35. **  Début du code
  36. **   . Includes
  37. **   . Defines
  38. **   . Variables globales
  39. **   . Objets
  40. **
  41. ******************************************************************************************************************************************************
  42. */
  43.  
  44. #include <avr/wdt.h>               // Pour le WatchDog
  45. #include <OneWire.h>               // Pour les capteurs de température
  46.  
  47.  
  48. /*
  49. **  Déclaration des constantes et des paramètres
  50. */
  51.  
  52. #define paramLongSun 600000        // en millisecondes : Durée de fonctionnement à partir de laquelle on considère que le fonctionnement est établi depuis longtemps (600000 = 10 minutes)
  53. #define paramDeltaTempStart 8.0    // en degrés : Ecart de température nécessaire pour la mise en route de la pompe
  54. #define paramDeltaTempStop 4.0     // en degrés : Ecart de température minimum pour le fonctionnement de la pompe
  55.  
  56. #define etatInconnu 0              // L'état est inconnu. C'est la valeur par défaut. C'est donc l'état au démarrage
  57. #define etatArret 4                // Le circuit est plein, prêt à fonctionner. La pompe est à l'arrêt
  58. #define etatSoleil 5               // La pompe fonctionne et le soleil chauffe les panneaux
  59. #define etatLongSoleil 6           // La pompe fonctionne et le soleil chauffe les panneaux depuis plus de paramLongSoleil  
  60. #define etatManuOff 8              // Toute l'installation est mise à l'arrêt
  61. #define etatManuPump 9             // La pompe de circulation est en marche forcée
  62.  
  63. #define onewireModeLecture 0xBE    // Code pour initier la lecture des capteurs OneWire
  64. #define onewireModeMesure 0x44     // Code pour déclencher la lecture des capteurs OneWire
  65. #define onewireNb 2                // Nombre de capteurs OneWire gérés
  66.  
  67. const int pinOneWire = 3;          // Broche utilisée pour lire les températures disponibles via des composant One-Wire DS18B20
  68. const int pinRelayPump = A4;       // Commande du relai de pompe de circulation
  69.  
  70. #define relayOn 0
  71. #define relayOff 255
  72.  
  73. /*
  74. ** Déclaration des variables et initialisations
  75. */
  76. int etat = etatInconnu;
  77. int lastEtat = etatInconnu;
  78. float tempPanel = -99.9;
  79. float tempBottom = -99.0;
  80.  
  81. // Adresses matérielles des capteurs One-Wire  <== Là, il faut mettre les adresses des capteurs réellement utilisés
  82. static byte onewireSensorSerial[onewireNb][8] = {
  83.   { 0x28, 0x55, 0x63, 0x02, 0x04, 0x00, 0x00, 0x06 },    // Température à l'intérieur des panneaux solaires
  84.   { 0x28, 0x55, 0x75, 0x02, 0x04, 0x00, 0x00, 0x40 },    // Bas du ballon d'eau chaude
  85. };
  86.  
  87.  
  88. // Chaines d'état à afficher (certaines seront utilisées plus tard)
  89. const String etatsText[10] = {
  90.   "Inconnu",         // #define etatInconnu 0
  91.   "Vide",            // #define etatVide 1
  92.   "Remplissage",     // #define etatRemplissage 2
  93.   "Vidange",         // #define etatVidange 3
  94.   "Arret",           // #define etatArret 4
  95.   "Soleil",          // #define etatSoleil 5
  96.   "SoleilLong",      // #define etatLongSoleil 6
  97.   "ClosingValve",    // #define etatClosingValve 7
  98.   "ManuOff",         // #define etatManuOff 8
  99.   "ManuPompe"        // #define etatManuPump 9
  100. };
  101.  
  102. const float temperatureMini = -40.0;                        // Température en dessous de la quelle elle n'est pas prise en compte
  103. const unsigned long temperatureReadInterval = 10L * 1000L;  // Temps entre 2 lectures de température
  104.  
  105. unsigned long pumpStartTime = 0;       // Temps écoulé, en millisecondes, depuis le démarrage de la pompe de circulation
  106. unsigned long temperatureTime = 0;     // Temps écoulé, en millisecondes, depuis la dernière lecture des températures
  107.  
  108. /*
  109. **  Création des objets
  110. */
  111. OneWire oneWire(pinOneWire);           // On défini la broche utilisée pour OneWire
  112.  
  113.  
  114. /*
  115. ******************************************************************************************************************************************************
  116. **
  117. **  setup  ==>  Fonction obligatoire d'initialisation
  118. **
  119. ******************************************************************************************************************************************************
  120. */
  121. void setup() {
  122.  
  123.   // Initialisation du WatchDog. Se déclenche au bout de 8 secondes d'inactivité
  124.   wdt_enable(WDTO_8S);
  125.  
  126.   // Initialiser les broches pour que le relai soit inactif au démarrage
  127.   analogWrite(pinRelayPump, relayOff);
  128.  
  129.   // Initialiser les autres broches comme broche de commande
  130.   pinMode(pinRelayPump, OUTPUT);
  131.  
  132.   // Ouvrir tous les relais (ne plus rien alimenter)
  133.   stopPump();
  134.  
  135.   // Effectue une première mesure pour alimenter les capteurs de température
  136.   for (int i = 0; i <= onewireNb; i++) {
  137.     onewireMesureTemp(onewireSensorSerial[i]);
  138.   }
  139.   delay(2000);
  140.   for (int i = 0; i <= onewireNb; i++){
  141.     onewireMesureTemp(onewireSensorSerial[i]);
  142.   }
  143.   delay(1000);
  144.  
  145.   etat = etatInconnu;
  146.   lastEtat = etatInconnu;
  147. }
  148.  
  149.  
  150. /*
  151. ******************************************************************************************************************************************************
  152. **
  153. **  loop  ==>  Fonction obligatoire. Boucle infinie de fonctionnement
  154. **
  155. ******************************************************************************************************************************************************
  156. */
  157. void loop() {
  158.  
  159.   // Reset du watchDog : C'est reparti pour 8 secondes
  160.   wdt_reset();
  161.  
  162.   /*************************************************************
  163.   **
  164.   ** Lecture de tous les capteurs
  165.   */
  166.   boolean temperatureRead = 0;
  167.   if ( (temperatureTime == 0) || ( (millis() - temperatureTime > temperatureReadInterval) && (millis() >= temperatureTime) ) ) {
  168.     temperatureTime = millis();
  169.     /// Lecture des températures
  170.     tempPanel = onewireMesureTemp(onewireSensorSerial[0]);
  171.     tempBottom = onewireMesureTemp(onewireSensorSerial[4]);
  172.     temperatureRead = true;
  173.   }
  174.  
  175.   /*************************************************************
  176.   **
  177.   ** Actions à mener en fonction des mesures effectuées et de l'état en cours
  178.   */
  179.   switch(etat) {
  180.    
  181.     case etatManuOff:        // ==> L'installation a été mise à l'arrêt manuellement
  182.       // Ne rien faire et laisser ainsi
  183.       break;  
  184.    
  185.     case etatManuPump:       // ==> La pompe est en marche forcée (mise en marche manuelle)
  186.       // Ne rien faire et laisser ainsi
  187.       break;  
  188.    
  189.     case etatArret:          // ==> la pompe est à l'arrêt
  190.       // Vérifier si les condition de mise en route sont remplies
  191.       if (tempPanel >= tempBottom + paramDeltaTempStart) {
  192.         startPump();
  193.       }
  194.       break;
  195.    
  196.     case etatSoleil:         // ==> La pome de circulation est en route de façon NON prolongée
  197.       // Vérifier si la pompe doit être arrêtée
  198.       if (tempPanel <= tempBottom + paramDeltaTempStop) {
  199.         stopPump();
  200.         etat = etatArret;
  201.       }
  202.       // Vérifier si le régime de croisière est atteint
  203.       if ( (millis() - pumpStartTime > paramLongSun) && (millis() >= pumpStartTime) ) {
  204.         pumpStartTime = 0;
  205.         etat = etatLongSoleil;    
  206.       }
  207.       break;
  208.    
  209.     case etatLongSoleil:     // ==> La pome de circulation est en route de façon prolongée et est donc en régime de croisière
  210.       // Vérifier si l'écart de température entre les panneaux et le bas du ballon d'eau chaude est suffisante
  211.       if (tempPanel <= tempBottom + paramDeltaTempStop) {
  212.         stopPump();
  213.         etat = etatArret;
  214.       }
  215.       break;
  216.    
  217.     case etatInconnu:        // ==> Survient normalement au démarrage
  218.       stopPump();
  219.       etat = etatArret;
  220.       break;
  221.   }
  222.  
  223.   // Enregistrer l'état pour la boucle suivante
  224.   lastEtat = etat;
  225.  
  226.   // Attendre un peu avant de boucler (une demi seconde)
  227.   delay(500);
  228. }          
  229.  
  230.  
  231. /*
  232. ******************************************************************************************************************************************************
  233. **
  234. **      Fonctions diverses
  235. **
  236. ******************************************************************************************************************************************************
  237. */
  238.  
  239. /*
  240. ** Fonction pour arrêter la pompe de circulation. L'installation devient alors à l'arrêt
  241. */
  242. void stopPump() {
  243.   analogWrite(pinRelayPump, relayOff);
  244.   pumpStartTime = 0;
  245.   etat = etatArret;
  246. }
  247.  
  248. /*
  249. ** Fonction pour démarrer la pompe de circulation. L'installation devient alors en fonctionnement
  250. */
  251. boolean startPump() {
  252.   analogWrite(pinRelayPump, relayOn);
  253.   pumpStartTime = millis();
  254.   etat = etatSoleil;
  255.   return(true);  
  256. }
  257.  
  258. /*
  259. ** Fonction pour lire une température sur le capteur dont l'adresse est en paramètre
  260. */
  261. float onewireMesureTemp(byte addr[8]) {
  262.   byte data[12];        // Tableau de 12 octets pour lecture des 9 registres de RAM et des 3 registres d'EEPROM du capteur One Wire
  263.   int tempet = 0;       // variable pour resultat brute de la mesure
  264.   float tempetf = 0.0;  // variable pour resultat à virgule de la mesure
  265.  
  266.   oneWire.reset();
  267.   oneWire.select(addr);
  268.   oneWire.write(onewireModeMesure, 1);    //
  269.  
  270.   oneWire.reset();
  271.   oneWire.select(addr);    
  272.   oneWire.write(onewireModeLecture);      // Récupération des valeurs de la RAM du capteur
  273.  
  274.   // Lire les 9 octets de la RAM (appelé Scratchpad)
  275.   for ( int i = 0; i < 9; i++) {          // 9 octets de RAM stockés dans 9 octets
  276.     data[i] = oneWire.read();             // lecture de l'octet de rang i stocké dans tableau data
  277.    }
  278.   tempetf = ((data[1] << 8) | data[0]) * 0.0625;
  279.   return (tempetf);
  280. }

 Pour aller plus loin

Voilà, les bases sont posées.
Ce petit code à fait tourner pendant 2 mois une installation solaire thermique. Ce n’est qu’un début car il manque beaucoup de choses. Il est en effet assez frustrant de ne pas pouvoir surveiller et contrôler cette installation autrement qu’en débranchant l’alimentation.
Un autre article montrera comment la vidange antigel a été mise en place et comment l’installation a été reliée au réseau local pour sa surveillance et son contrôle...


Commentaires  forum ferme

Logo de Finizi
vendredi 27 septembre 2013 à 23h05 - par  Finizi

Bonne question !
Vu de cet article, on a effectivement l’embarras du choix pour broches à utiliser.
Mais dans des articles à venir, vous découvrirez une installation où toutes les broches sont utilisées.
Pour les commandes de relais, les 2 types de broche sont possibles. J’ai donc utiliser celles qui restaient disponibles.

Logo de Edwin Torisaen
vendredi 27 septembre 2013 à 13h27 - par  Edwin Torisaen

Bonjour
très instructif votre page web.
mais pourquoi avez-vous branché votre relais sur l’entrée analogique de l’arduino ?

Navigation

Mots-clés de l'article