Contrôle de chauffage ECS avec Raspberry Pi3 et Domoticz

Vous avez créé un script LUA dont vous êtes fier, un .sh génial, un programme Python hors du commun, un Tuto, c'est ici que vous pouvez les partager.
daniel4860
Messages : 84
Inscription : 26 sept. 2016, 18:31
Localisation : Lapoutroie Haut-Rhin

Contrôle de chauffage ECS avec Raspberry Pi3 et Domoticz

Message par daniel4860 »

Il s’agit de pouvoir mettre en marche une chaudière et commander un circulateur qui va réchauffer l’eau chaude sanitaire d’un ballon de 300L.
La commande de la chaudière se fait avec soit un bouton physique ou un interrupteur dans Domoticz.
La commande de la régulation se fait soit avec un bouton physique ou un interrupteur dans Domoticz.
Le bouton physique de commande de la chaudière est câblée sur la GPIO17 et commande un relais Chaudière sur la sortie GPIO24 ainsi que le l’interrupteur de Domoticz.
Le bouton physique de commande de la Régulation est câblée sur la GPIO22 et commande la mise en fonction de la régulation de chauffage de l’eau sanitaire par l’intermédiaire du Circulateur commande par le relais câblée sur la sortie GPIO23.
Et j’ai intégrer un écran de 4x20 caractères.
Le matériel:
Raspberry Pi 3 B+, Debian 13 Trixie
Module de 2 relais 5v
3 Sondes de température DS18B20
Ecran HD44780 LCD bundle vert 4x20 caractères.

Câblage matériel
GPIO utilisés
GPIO-----Fonction-----------Type-------Notes
17 -------Bouton Chaudière---Input------Pull-up interne
22-------Bouton Régulation---Input------Pull-up interne
23-------Relais Circulateur---Output-----Logique inversée
24-------Relais Chaudière----Output-----Logique inversée
2-(SDA)---LCD I2C------------I2C--------Bus I2C
3-(SCL)---LCD I2C------------I2C--------Bus I2C
4--------Sondes DS18B20-----1-Wire-----3 sondes en parallèle

Schéma de principe
Boutons (GPIO 17, 22):
[3.3V] ----[Bouton]---- [GPIO] ---- [GND]

Pull-up interne

Relais (GPIO 23, 24):
[GPIO] ---- [Module Relais] ---- [Charge]
(Logique inversée)

LCD I2C:
[5V] ---- [VCC]
[GND] --- [GND]
[GPIO2] - [SDA]
[GPIO3] - [SCL]

Sondes DS18B20:
[3.3V] ---- [VCC] ---- (3x sondes en parallèle)
[GPIO4] --- [DATA] --- (résistance 4.7kΩ vers 3.3V)
[GND] ----- [GND]
Logique inversée des relais
• GPIO HIGH (1) = Relais OFF
• GPIO LOW (0) = Relais ON

Préparation du système
Après avoir installer l’OS sur la carte SD avec Raspberry pi imager sur un Raspberry pi3 B+.
L’OS installer étant Raspberry PI OS Lite (64-bit). Debian Trixie sans environnement de bureau.
Après l’insertion de la carte SD dans le raspberry :

Mise à jour su systéme

Code : Tout sélectionner

sudo apt update
sudo apt upgrade -y
Installations des packages nécessaires

Code : Tout sélectionner

sudo apt install -y python3 python3-pip python3-venv python3-full \
                    git i2c-tools gpiod python3-libgpiod
Activation des interfaces

Code : Tout sélectionner

sudo  nano /boot/firmware/config.txt
On ajoute ces lignes à la fin du fichier:

Code : Tout sélectionner

dtparam=i2c_arm=on
dtoverlay=w1-gpio
Activation de l’i2c
Aprés mettre rendue compte que sans cela je ne trouvait pas l'écran alors je suis passé par:

Code : Tout sélectionner

sudo raspi-config
Dans le menue Raspberry Pi Software Configuration Tool (raspi-config)
Sélectionner le menue 3 Interface Options Configurations to peripherals
Puis I5 I2C Enable/Disable automatic loading of SPI kernel module
Répondre Yes à la fenêtre. Would you like the ARM I2C interface to be enabled ?

Redémarrer

Code : Tout sélectionner

sudo reboot
Vérification du matériel
Après redémarrage, vérifier :
I2C (écran LCD) :

Code : Tout sélectionner

sudo i2cdetect -y 1
→ Vous devez voir une adresse (0x27 ou 0x3F)
Sondes DS18B20 :

Code : Tout sélectionner

ls /sys/bus/w1/devices/
→ Vous devez voir 3 sondes (28-xxxxxxxxxxxx)
GPIO :

Code : Tout sélectionner

gpioinfo | grep -E "GPIO(17|22|23|24)"
Installation de Python
On créer l’environnement virtuel car pour utilisez les Gpio et dialoguer avec Domoticz cela est nécessaire.

Code : Tout sélectionner

cd ~
python3 -m venv projet_chauffage
cd projet_chauffage
source bin/activate
On se retrouve donc:

Code : Tout sélectionner

(projet_chauffage) pi@raspberrypi:~/projet_chauffage $ 
Et on installe les ibrairies:

Code : Tout sélectionner

pip install gpiod RPLCD smbus2 flask flask-cors requests
Installation de Domoticz
Il faut sortir de l'environnement virtuel:

Code : Tout sélectionner

deactivate
On installe Domoticz:

Code : Tout sélectionner

sudo bash -c "$(curl -sSfL https://install.domoticz.com)"
Après quelques minutes on a accès a Domoticz à partir d'un navigateur:

Code : Tout sélectionner

http://IP_RASPBERRY:8080


Configuration de Domoticz
1. Ouvrir http://IP_RASPBERRY:8080
2. Setup →Settings → System
3. Configurer la localisation
Je me suis aperçu que sans la configuration de la localisation cela ne fonctionnait pas.
4. Setup → Settings → Security
5. Activer API Protection Sinon les GPIO ne fonctionne pas il y a erreur 401.
Configurer utilisateur/mot de passe
De base admin/domoticz Celui que j'ai laissé pour le teste
6. Apply Settings

On crée le matériel virtuel

1. Setup → Hardware → Add
2. Paramètres :
◦ Name : Chauffage Virtuel
◦ Type : Dummy (Does nothing, use for virtual switches only)
◦ ☑ Create Virtual Sensors
3. Add

Créeation des capteurs de température
Dans "Create Virtual Sensor" :
• Sensor Type : Temperature
• Créer 3 capteurs :
◦ Température Chaudière
◦ Température ECS Bas
◦ Température ECS Haut

Créer les switches
Dans "Create Virtual Sensor" :
• Sensor Type : Switch
• Créer 3 switches :
◦ Chaudière
◦ Régulation Auto
◦ Circulateur

Ajouter les sondes physiques
1. Setup → Hardware → Add
2. Paramètres :
◦ Name : Sonde Temperature
◦ Type : 1-Wire (System)
3. Add
Les 3 sondes apparaissent automatiquement est indique 0°C

Il faut noter les IDX des différents capteurs
Par exemple:
IDX 1 - Chaudière
IDX 2 - Régulation Auto
IDX 3- Circulateur
IDX 5 - Température Chaudière
IDX 4 - Température ECS Bas
IDX 6 - Température ECS Haut

Installation du script Python
On va créer le fichier, on retourne dans le projet chauffage.

Code : Tout sélectionner

cd ~/projet_chauffage
source bin/activate
nano systeme_domoticz.py
On copie le script complet ci dessous et on termine par un ctrl+X, Y, Enter.

Code : Tout sélectionner

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Intégration Domoticz pour système de chauffage
Raspberry Pi - Debian 13 Trixie
"""

import time
import gpiod
from gpiod.line import Direction, Value, Bias, Edge
from RPLCD.i2c import CharLCD
import glob
import threading
import requests
import json

# Configuration GPIO
GPIO_INPUT_1 = 17
GPIO_INPUT_2 = 22
GPIO_OUTPUT_CHAUDIERE = 24
GPIO_OUTPUT_CIRCULATEUR = 23

# Paramètres de régulation
TEMP_MIN_CHAUDIERE = 55.0
HYSTERESIS_CHAUDIERE = 5.0
HYSTERESIS_ECS = 2.0

# Configuration LCD
LCD_ADDRESS = 0x27

# Configuration Domoticz
DOMOTICZ_IP = "127.0.0.1"  # localhost si Domoticz est sur le même Raspberry
DOMOTICZ_PORT = 8080

# Authentification Domoticz (laisser vide si pas d'authentification)
DOMOTICZ_USERNAME = ""  # Votre nom d'utilisateur Domoticz
DOMOTICZ_PASSWORD = ""  # Votre mot de passe Domoticz

# IDX des dispositifs Domoticz (configurés selon votre installation)
IDX_TEMP_CHAUDIERE = 5
IDX_TEMP_ECS_BAS = 4
IDX_TEMP_ECS_HAUT = 3
IDX_SWITCH_CHAUDIERE = 2
IDX_SWITCH_REGULATION = 6
IDX_SWITCH_CIRCULATEUR = 1

class SystemeChauffageDomoticz:
    def __init__(self):
        self.chip = gpiod.Chip('/dev/gpiochip0')
        
        self.chaudiere_active = False
        self.regulation_active = False
        self.circulateur_on = False
        self.lock = threading.Lock()
        
        self.temperatures = {'chaudiere': 0.0, 'esc_bas': 0.0, 'esc_haut': 0.0}
        
        # Configuration GPIO
        self.inputs = self.chip.request_lines(
            consumer="systeme_chauffage",
            config={
                GPIO_INPUT_1: gpiod.LineSettings(
                    direction=Direction.INPUT,
                    bias=Bias.PULL_UP,
                    edge_detection=Edge.FALLING
                ),
                GPIO_INPUT_2: gpiod.LineSettings(
                    direction=Direction.INPUT,
                    bias=Bias.PULL_UP,
                    edge_detection=Edge.FALLING
                )
            }
        )
        
        self.output_chaudiere = self.chip.request_lines(
            consumer="systeme_chauffage",
            config={
                GPIO_OUTPUT_CHAUDIERE: gpiod.LineSettings(
                    direction=Direction.OUTPUT,
                    output_value=Value.ACTIVE
                )
            }
        )
        
        self.output_circulateur = self.chip.request_lines(
            consumer="systeme_chauffage",
            config={
                GPIO_OUTPUT_CIRCULATEUR: gpiod.LineSettings(
                    direction=Direction.OUTPUT,
                    output_value=Value.ACTIVE
                )
            }
        )
        
        # LCD
        try:
            self.lcd = CharLCD(i2c_expander='PCF8574', 
                              address=LCD_ADDRESS, 
                              port=1,
                              cols=20, 
                              rows=4)
            self.lcd.clear()
            self.lcd_ok = True
            print("LCD initialisé")
        except Exception as e:
            print(f"LCD non disponible: {e}")
            self.lcd_ok = False
        
        self.sondes = self.trouver_sondes()
        print("Système initialisé")
        
    def trouver_sondes(self):
        base_dir = '/sys/bus/w1/devices/'
        devices = glob.glob(base_dir + '28*')
        
        if len(devices) == 0:
            print("ATTENTION: Aucune sonde DS18B20 trouvée!")
            return {}
        
        sondes = {}
        noms = ['chaudiere', 'esc_bas', 'esc_haut']
        for i, device in enumerate(devices[:3]):
            sondes[noms[i]] = device
            print(f"Sonde {noms[i]}: {device}")
        
        return sondes
    
    def lire_temperature(self, device_path):
        try:
            with open(device_path + '/w1_slave', 'r') as f:
                lines = f.readlines()
            
            if lines[0].strip()[-3:] != 'YES':
                return None
            
            equals_pos = lines[1].find('t=')
            if equals_pos != -1:
                temp_string = lines[1][equals_pos+2:]
                temp_c = float(temp_string) / 1000.0
                return round(temp_c, 1)
        except:
            return None
    
    def lire_toutes_temperatures(self):
        temperatures = {}
        for nom, path in self.sondes.items():
            temp = self.lire_temperature(path)
            temperatures[nom] = temp if temp is not None else 0.0
        
        with self.lock:
            self.temperatures = temperatures
        
        return temperatures
    
    def controler_chaudiere(self, etat):
        with self.lock:
            valeur = Value.INACTIVE if etat else Value.ACTIVE
            self.output_chaudiere.set_value(GPIO_OUTPUT_CHAUDIERE, valeur)
            self.chaudiere_active = etat
            print(f">>> CHAUDIERE = {'ON' if etat else 'OFF'}")
        
        # Mise à jour immédiate du LCD
        self.maj_lcd()
        
        # Mise à jour Domoticz
        self.update_domoticz_switch(IDX_SWITCH_CHAUDIERE, etat)
    
    def controler_circulateur(self, etat):
        with self.lock:
            valeur = Value.INACTIVE if etat else Value.ACTIVE
            self.output_circulateur.set_value(GPIO_OUTPUT_CIRCULATEUR, valeur)
            self.circulateur_on = etat
            print(f">>> CIRCULATEUR = {'ON' if etat else 'OFF'}")
        
        # Mise à jour immédiate du LCD
        self.maj_lcd()
        
        # Mise à jour Domoticz
        self.update_domoticz_switch(IDX_SWITCH_CIRCULATEUR, etat)
    
    def regulation_temperature(self):
        if not self.regulation_active:
            if self.circulateur_on:
                self.controler_circulateur(False)
            return
        
        temp_chaudiere = self.temperatures.get('chaudiere', 0)
        temp_ecs_haut = self.temperatures.get('esc_haut', 0)
        
        # Debug - afficher les températures
        print(f"[REG] Chaud:{temp_chaudiere}°C, ECS:{temp_ecs_haut}°C, Circ:{'ON' if self.circulateur_on else 'OFF'}")
        
        # Vérifier si les températures sont valides
        if temp_chaudiere == 0 or temp_ecs_haut == 0:
            print("ATTENTION: Températures non disponibles")
            return
        
        # Logique avec hystérésis
        if self.circulateur_on:
            # Circulateur déjà ON - conditions d'arrêt
            if temp_chaudiere < (TEMP_MIN_CHAUDIERE - HYSTERESIS_CHAUDIERE):
                print(f"[REG] Arrêt circulateur - Chaudière trop froide ({temp_chaudiere}°C < {TEMP_MIN_CHAUDIERE - HYSTERESIS_CHAUDIERE}°C)")
                self.controler_circulateur(False)
            elif temp_ecs_haut >= (temp_chaudiere - HYSTERESIS_ECS):
                print(f"[REG] Arrêt circulateur - ECS haut proche chaudière (ECS:{temp_ecs_haut}°C >= {temp_chaudiere - HYSTERESIS_ECS}°C)")
                self.controler_circulateur(False)
            else:
                print(f"[REG] Circulateur maintenu ON")
        else:
            # Circulateur OFF - conditions de démarrage
            condition1 = temp_chaudiere >= TEMP_MIN_CHAUDIERE
            condition2 = temp_ecs_haut < (temp_chaudiere - HYSTERESIS_ECS)
            
            print(f"[REG] Test démarrage: Chaud>={TEMP_MIN_CHAUDIERE}? {condition1}, ECS<Chaud-{HYSTERESIS_ECS}? {condition2}")
            
            if condition1 and condition2:
                print(f"[REG] ✓ Démarrage circulateur - Conditions OK")
                self.controler_circulateur(True)
            else:
                print(f"[REG] ✗ Circulateur reste OFF - Conditions non remplies")
    
    def update_domoticz_temp(self, idx, temperature):
        """Envoie une température à Domoticz"""
        try:
            url = f"http://{DOMOTICZ_IP}:{DOMOTICZ_PORT}/json.htm"
            params = {
                'type': 'command',
                'param': 'udevice',
                'idx': idx,
                'nvalue': 0,
                'svalue': str(temperature)
            }
            
            # Authentification si configurée
            auth = None
            if DOMOTICZ_USERNAME and DOMOTICZ_PASSWORD:
                auth = (DOMOTICZ_USERNAME, DOMOTICZ_PASSWORD)
            
            response = requests.get(url, params=params, auth=auth, timeout=2)
            if response.status_code != 200:
                print(f"Erreur Domoticz temp {idx}: {response.status_code}")
        except Exception as e:
            print(f"Erreur connexion Domoticz: {e}")
    
    def update_domoticz_switch(self, idx, etat):
        """Met à jour un switch dans Domoticz"""
        try:
            url = f"http://{DOMOTICZ_IP}:{DOMOTICZ_PORT}/json.htm"
            params = {
                'type': 'command',
                'param': 'switchlight',
                'idx': idx,
                'switchcmd': 'On' if etat else 'Off'
            }
            
            # Authentification si configurée
            auth = None
            if DOMOTICZ_USERNAME and DOMOTICZ_PASSWORD:
                auth = (DOMOTICZ_USERNAME, DOMOTICZ_PASSWORD)
            
            response = requests.get(url, params=params, auth=auth, timeout=2)
            if response.status_code != 200:
                print(f"Erreur Domoticz switch {idx}: {response.status_code}")
        except Exception as e:
            print(f"Erreur connexion Domoticz: {e}")
    
    def check_domoticz_commands(self):
        """Vérifie si des commandes ont été envoyées depuis Domoticz"""
        try:
            # Authentification
            auth = None
            if DOMOTICZ_USERNAME and DOMOTICZ_PASSWORD:
                auth = (DOMOTICZ_USERNAME, DOMOTICZ_PASSWORD)
            
            # URL qui fonctionne avec Domoticz 2025.2
            url = f"http://{DOMOTICZ_IP}:{DOMOTICZ_PORT}/json.htm?type=command&param=getdevices"
            
            response = requests.get(url, auth=auth, timeout=2)
            
            if response.status_code != 200:
                return
            
            data = response.json()
            if 'result' not in data:
                return
            
            # Parcourir les dispositifs
            for device in data['result']:
                idx = device.get('idx', '')
                # idx peut être string ou int selon version Domoticz
                try:
                    idx = int(idx)
                except:
                    continue
                
                status = device.get('Status', 'Off')
                
                # Switch Chaudière
                if idx == IDX_SWITCH_CHAUDIERE:
                    nouvel_etat = (status == 'On')
                    with self.lock:
                        etat_actuel = self.chaudiere_active
                    
                    if nouvel_etat != etat_actuel:
                        print(f"[DOMOTICZ] ⚡ Commande Chaudière = {status}")
                        self.controler_chaudiere(nouvel_etat)
                
                # Switch Régulation
                elif idx == IDX_SWITCH_REGULATION:
                    nouvel_etat = (status == 'On')
                    with self.lock:
                        etat_actuel = self.regulation_active
                    
                    if nouvel_etat != etat_actuel:
                        print(f"[DOMOTICZ] ⚡ Commande Régulation = {status}")
                        with self.lock:
                            self.regulation_active = nouvel_etat
                        self.maj_lcd()
                        if nouvel_etat:
                            self.regulation_temperature()
        
        except Exception as e:
            pass  # Silencieux
    
    def maj_lcd(self):
        if not self.lcd_ok:
            return
        with self.lock:
            ligne1 = f"Chd:{self.temperatures.get('chaudiere', 0)}C"
            if self.chaudiere_active:
                ligne1 += " ON"
            ligne2 = f"Bas:{self.temperatures.get('esc_bas', 0)}C"
            ligne3 = f"Ht:{self.temperatures.get('esc_haut', 0)}C"
            ligne4 = f"Reg:{'ON' if self.regulation_active else 'OFF'} Cir:{'ON' if self.circulateur_on else 'OFF'}"
        
        try:
            self.lcd.clear()
            self.lcd.cursor_pos = (0, 0)
            self.lcd.write_string(ligne1[:20])
            self.lcd.cursor_pos = (1, 0)
            self.lcd.write_string(ligne2[:20])
            self.lcd.cursor_pos = (2, 0)
            self.lcd.write_string(ligne3[:20])
            self.lcd.cursor_pos = (3, 0)
            self.lcd.write_string(ligne4[:20])
        except:
            pass
    
    def nettoyer(self):
        print("\nNettoyage...")
        self.controler_chaudiere(False)
        self.controler_circulateur(False)
        self.inputs.release()
        self.output_chaudiere.release()
        self.output_circulateur.release()
        self.chip.close()
        if self.lcd_ok:
            self.lcd.clear()
            self.lcd.close()

def thread_boutons(systeme, stop_event):
    print("Thread boutons actif")
    derniere_detection = {GPIO_INPUT_1: 0, GPIO_INPUT_2: 0}
    anti_rebond = 0.2
    
    while not stop_event.is_set():
        try:
            if systeme.inputs.wait_edge_events(timeout=0.5):
                for event in systeme.inputs.read_edge_events():
                    temps_actuel = time.time()
                    gpio = event.line_offset
                    
                    if temps_actuel - derniere_detection[gpio] < anti_rebond:
                        continue
                    
                    derniere_detection[gpio] = temps_actuel
                    
                    if gpio == GPIO_INPUT_1:
                        with systeme.lock:
                            nouvel_etat = not systeme.chaudiere_active
                        print(f"\n!!! BOUTON CHAUDIERE !!!")
                        systeme.controler_chaudiere(nouvel_etat)
                    
                    elif gpio == GPIO_INPUT_2:
                        with systeme.lock:
                            nouvel_etat = not systeme.regulation_active
                            systeme.regulation_active = nouvel_etat
                        print(f"\n!!! BOUTON REGULATION !!!")
                        # Mise à jour immédiate LCD
                        systeme.maj_lcd()
                        # Mise à jour Domoticz
                        systeme.update_domoticz_switch(IDX_SWITCH_REGULATION, nouvel_etat)
                        if nouvel_etat:
                            systeme.regulation_temperature()
        except:
            if not stop_event.is_set():
                time.sleep(0.1)

def thread_regulation_et_domoticz(systeme, stop_event):
    print("Thread régulation et Domoticz actif")
    compteur = 0
    
    while not stop_event.is_set():
        try:
            # Lecture températures
            systeme.lire_toutes_temperatures()
            
            # Mise à jour Domoticz températures (toutes les 30 secondes)
            if compteur % 6 == 0:
                systeme.update_domoticz_temp(IDX_TEMP_CHAUDIERE, systeme.temperatures['chaudiere'])
                systeme.update_domoticz_temp(IDX_TEMP_ECS_BAS, systeme.temperatures['esc_bas'])
                systeme.update_domoticz_temp(IDX_TEMP_ECS_HAUT, systeme.temperatures['esc_haut'])
            
            # Vérifier commandes Domoticz (toutes les 2 secondes = chaque boucle)
            systeme.check_domoticz_commands()
            
            # Régulation
            systeme.regulation_temperature()
            
            # LCD
            systeme.maj_lcd()
            
            compteur += 1
            time.sleep(2)  # Réduit à 2 secondes pour plus de réactivité
        except Exception as e:
            print(f"Erreur thread: {e}")
            time.sleep(1)

def main():
    print("="*50)
    print("Intégration Domoticz - Système Chauffage")
    print("="*50)
    print(f"\nDomoticz: http://{DOMOTICZ_IP}:{DOMOTICZ_PORT}")
    print(f"\nIMPORTANT: Configurez les IDX dans le script:")
    print(f"  - Températures: {IDX_TEMP_CHAUDIERE}, {IDX_TEMP_ECS_BAS}, {IDX_TEMP_ECS_HAUT}")
    print(f"  - Switches: {IDX_SWITCH_CHAUDIERE}, {IDX_SWITCH_REGULATION}, {IDX_SWITCH_CIRCULATEUR}")
    print("\nCtrl+C pour arrêter\n")
    
    systeme = SystemeChauffageDomoticz()
    stop_event = threading.Event()
    
    thread_btn = threading.Thread(target=thread_boutons, args=(systeme, stop_event), daemon=True)
    thread_reg = threading.Thread(target=thread_regulation_et_domoticz, args=(systeme, stop_event), daemon=True)
    
    thread_btn.start()
    thread_reg.start()
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        print("\n\nArrêt...")
    finally:
        stop_event.set()
        thread_btn.join(timeout=2)
        thread_reg.join(timeout=2)
        systeme.nettoyer()
        print("Terminé")

if __name__ == "__main__":
    main()
Configuration du script
Il faut mettre le bon admin mot de passe, le bon port si vous l'avez changer, ainsi que les bon IDX:

Edition des paramétres

Code : Tout sélectionner

nano systeme_domoticz.py
Modifier ces valeurs
# Configuration Domoticz
DOMOTICZ_IP = "127.0.0.1"
DOMOTICZ_PORT = 8080

# Authentification
DOMOTICZ_USERNAME = "admin"
DOMOTICZ_PASSWORD = "votre_mot_de_passe"

# IDX (selon vos valeurs)
IDX_TEMP_CHAUDIERE = 5
IDX_TEMP_ECS_BAS = 4
IDX_TEMP_ECS_HAUT = 3
IDX_SWITCH_CHAUDIERE = 2
IDX_SWITCH_REGULATION = 6
IDX_SWITCH_CIRCULATEUR = 1

# LCD I2C
LCD_ADDRESS = 0x27 # ou 0x3F

# Régulation
TEMP_MIN_CHAUDIERE = 55.0
HYSTERESIS_CHAUDIERE = 5.0
HYSTERESIS_ECS = 2.0

Test du systéme
Lancer le script:

Code : Tout sélectionner

cd ~/projet_chauffage
source bin/activate
python3 systeme_domoticz.py
Vérifications
Si tout va bien on doit avoir:
✓ LCD affiche les températures
✓ Boutons GPIO 17/22 fonctionnent
✓ Switches Domoticz contrôlent les relais
✓ Températures remontent dans Domoticz
Arrêter : Ctrl+C

Démarrage automatique

L'on crée le service systemd

Code : Tout sélectionner

sudo nano /etc/systemd/system/chauffage.service
Contenu du service

Code : Tout sélectionner

[Unit]
Description=Systeme Chauffage Domoticz
After=network.target domoticz.service

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/projet_chauffage
ExecStart=/home/pi/projet_chauffage/bin/python3 /home/pi/projet_chauffage/systeme_domoticz.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
Activer le service

Code : Tout sélectionner

sudo systemctl daemon-reload
sudo systemctl enable chauffage.service
sudo systemctl start chauffage.service
On vérifie

Code : Tout sélectionner

sudo systemctl status chauffage.service
sudo journalctl -u chauffage.service -f
Et si il y a des erreurs petit DEPANNAGE :
Le script ne démarre pas
Erreur : "Device or resource busy"

Code : Tout sélectionner

pkill -f systeme_domoticz.py
# Puis relancer

Erreur : Module manquant

Code : Tout sélectionner

cd ~/projet_chauffage
source bin/activate
pip install gpiod RPLCD smbus2 requests flask flask-cors
Domoticz erreur 401[\b]
Vérifier l'authentification dans le script :

DOMOTICZ_USERNAME = "admin"
DOMOTICZ_PASSWORD = "correct_password"

Et configurer la localisation dans Domoticz ainsi qu’il faut valider API Protection.

LCD ne s'affiche pas
Vérifier l'adresse I2C :

Code : Tout sélectionner

sudo i2cdetect -y 1
Modifier LCD_ADDRESS dans le script.

Sondes non détectées

Code : Tout sélectionner

ls /sys/bus/w1/devices/
Si vide, vérifier

Code : Tout sélectionner

sudo nano /boot/firmware/config.txt

Qu'il y a bien a la fin du fichier:
dtoverlay=w1-gpio

Relais ne réagit pas
Test manuel :
# Activer (LOW)

Code : Tout sélectionner

gpioset -c gpiochip0 24=0
# Désactiver (HIGH)

Code : Tout sélectionner

gpioset -c gpiochip0 24=1
Voir les logs
# Logs du service

Code : Tout sélectionner

sudo journalctl -u chauffage.service -n 100
# Logs en temps réel

Code : Tout sélectionner

sudo journalctl -u chauffage.service -f
Commandes utiles
Contrôle du service[\b]
# Démarrer

Code : Tout sélectionner

sudo systemctl start chauffage.service
# Arrêter

Code : Tout sélectionner

sudo systemctl stop chauffage.service
# Redémarrer

Code : Tout sélectionner

sudo systemctl restart chauffage.service
# État

Code : Tout sélectionner

sudo systemctl status chauffage.service
# Désactiver au démarrage

Code : Tout sélectionner

sudo systemctl disable chauffage.service
# Réactiver au démarrage

Code : Tout sélectionner

sudo systemctl enable chauffage.service
Mode manuel

Code : Tout sélectionner

cd ~/projet_chauffage
source bin/activate
python3 systeme_domoticz.py
Test des GPIO

# Lister tous les GPIO

Code : Tout sélectionner

gpioinfo
# Tester une entrée

Code : Tout sélectionner

gpioget -c gpiochip0 17
# Tester une sortie

Code : Tout sélectionner

gpioset -c gpiochip0 -i 23=0  # Maintient à LOW
Fonctionnalités

Contrôle
✓ Boutons physiques GPIO 17 et 22
✓ Interface Domoticz web/mobile
✓ Synchronisation bidirectionnelle

Affichage
✓ LCD 20x4 temps réel
✓ Domoticz avec historique et graphiques

Régulation
✓ Automatique avec hystérésis
✓ Paramètres configurables
✓ Logs détaillés

Paramètres de régulation
Démarrage circulateur si :
• Chaudière ≥ 55°C
• ET ECS Haut < (Chaudière - 2°C)
Arrêt circulateur si :
• Chaudière < 50°C (55 - 5)
• OU ECS Haut ≥ (Chaudière - 2°C)

Accès distant

Configuration box internet
1. Accéder à l'interface de votre box
2. Configurer redirection de port :
◦ Port externe : 8080
◦ Port interne : 8080
◦ IP locale : IP du Raspberry Pi

Accès
http://VOTRE_IP_PUBLIQUE:8080

Sécurité
• Utilisez un mot de passe fort dans Domoticz
• Changez le port par défaut si possible
• Envisagez un VPN pour plus de sécurité

Support et maintenance
Mise à jour du script

Code : Tout sélectionner

cd ~/projet_chauffage
source bin/activate
nano systeme_domoticz.py
# Modifier

Code : Tout sélectionner

sudo systemctl restart chauffage.service
[b}Sauvegarde[/b]
# Sauvegarder la configuration

Code : Tout sélectionner

cp ~/projet_chauffage/systeme_domoticz.py ~/backup_script.py
# Sauvegarder Domoticz

Code : Tout sélectionner

sudo systemctl stop domoticz.service
sudo cp -r /home/pi/domoticz/domoticz.db ~/backup_domoticz.db
sudo systemctl start domoticz.service
Restauration
# Restaurer le script

Code : Tout sélectionner

cp ~/backup_script.py ~/projet_chauffage/systeme_domoticz.py
# Restaurer Domoticz

Code : Tout sélectionner

sudo systemctl stop domoticz.service
sudo cp ~/backup_domoticz.db /home/pi/domoticz/domoticz.db
sudo systemctl start domoticz.service
Voilà arriver au bout de mon contrôle de chauffage ECS, avec beaucoup de temps et de recherche avant que cela fonctionne. Je ne suis pas un pro de linux ni de python ni de programmation, mais c'est en forgeant que l'on devient forgeron...
Domoticz sur Raspberry sur lequel 3 sondes prennent la température.
Une Gpio Out fait basculer un relais pour enclencher un circulateur.
lost
Messages : 1511
Inscription : 12 nov. 2016, 11:01

Re: Contrôle de chauffage ECS avec Raspberry Pi3 et Domoticz

Message par lost »

Hello, merci pour ce tuto détaillé mais j'aurais une remarque pour les accès externes: Un mdp fort c'est bien, mais en https c'est mieux. Bon, cela oblige à se faire un domaine, y accrocher un certificat letsencrypt pour ne pas rester sur celui autosigné par défaut... ou à un VPN en effet, mais en perdant la possibilité de se connecter de toute machine qui n'est pas à soi (et sans le VPN).
En http le mdp robuste passe en clair.
daniel4860
Messages : 84
Inscription : 26 sept. 2016, 18:31
Localisation : Lapoutroie Haut-Rhin

Re: Contrôle de chauffage ECS avec Raspberry Pi3 et Domoticz

Message par daniel4860 »

Oui c'est certain que pour un accès externe il faut être prudent. Et l'on peut toujours améliorer ce tuto que j'ai mis pas mal de temps à arriver au bout pour que cela fonctionne. Bonne lecture.
Domoticz sur Raspberry sur lequel 3 sondes prennent la température.
Une Gpio Out fait basculer un relais pour enclencher un circulateur.
Répondre