Projet MicroPython ESP32, lire la température sur plusieurs sondes DS18B20 et publier les mesures vers Domoticz

Après plusieurs articles pour présenter les principes du MicroPython sur les cartes ESP8266 et ESP32, il est temps de passer aux choses sérieuses. Je vous propose de porter le code Arduino de lecture de plusieurs sondes de mesure de température Dallas DS18B20 en MicroPython (article précédent). Ici, nous allons utiliser l’éditeur de code uPiCraft déjà présenté précédemment dans ce tutoriel et celui-ci (gestion de la connexion WiFi). uPiCraft contient quelques modules complémentaires dont un module permettant de scanner le bus One-Wire et de lire la température très simplement. Nous allons également découvrir comment se connecter à un serveur Domoticz et publier les mesures. Si vous ne souhaitez pas utilisez uPiCraft, le code source du module DS18x20 est disponible plus bas dans l’article. Si vous n’avez pas encore le firmware MicroPython installé sur une carte ESP8266 ou ESP32, suivez ce tutoriel avec le script esptool.py ou celui-ci à l’aide d’uPiCraft.

Matériel nécessaire

Le code fonctionne a été sur ESP8266 ainsi que sur ESP32. Le bus One-Wire ne nécessite qu’un seul fil pour communiquer avec le micro-contrôleur. Le code permet de récupérer la température de 2 sondes Dallas DS18B20 mais vous pouvez en connecter jusqu’à 100. Ca devrait suffire pour couvrir toute une maison !

Circuit avec plusieurs sondes Dallas DS18B20

Le branchement est très simple. Le bus One-Wire est généralement situé sur un fil jaune. Le rouge est utilisé pour l’alimentation, le noir pour le GND comme d’habitude. Pour que le bus fonctionne, il faut le “déparasiter”. Pour cela, on place une résistance (4K7 en général) entre le +5V et le bus de données. J’ai également essayé d’autres résistances (5K7) ainsi qu’une alimentation +3V3 avec succès. Evidemment, tout dépendra de la longueur de câble. Plus le câblage sera long, plus il faudra être rigoureux avec l’alimentation et le dé-parasitage du signal.

Pour ce tutoriel, le DS18B20 est branché sur la broche D4 de l’ESP8266. Tous les codes proposés dans ce tutoriel peuvent être utilisés sur un Arduino Uno ou un ESP32.

Copier la librairie DS18X20 pour MicroPython sur l’ESP32/ESP8266

L’IDE uPiCraft est livré avec quelques librairies complémentaires aux modules natifs du MicroPython. La libraire DS18X20 est rangée avec les autres librairies dans la barre de navigation latérale dans la section uPi_Lib.

Connectez-vous à votre carte de développement MicroPython. Si vous avez loupé les épisodes précédents, allez dans le menu Tools puis Serial pour choisir indiquer le port COM sur lequel est branché la carte. Ensuite, cliquez sur le bouton connect.

Pour copier la librairie sur la carte, il suffit de faire un glisser-déposer sur le device connecté. L’autre méthode consiste à ouvrir la librairie puis choisir l’option Download dans le menu Tools.

Si vous n’utilisez par l’IDE uPiCraft, créer un nouveau fichier nommé DS18X20.pu et collez le code source. Il est également disponible sur GitHub. Ce driver permet de créer un objet attaché à la broche des sondes One-Wire. Plusieurs méthodes sont disponibles :

  • scan
  • convert_temp
  • read_scratch
  • write_scratch
  • read_temp
# DS18x20 temperature sensor driver for MicroPython.
# MIT license; Copyright (c) 2016 Damien P. George

from micropython import const

_CONVERT = const(0x44)
_RD_SCRATCH = const(0xbe)
_WR_SCRATCH = const(0x4e)

class DS18X20:
    def __init__(self, onewire):
        self.ow = onewire
        self.buf = bytearray(9)

    def scan(self):
        return [rom for rom in self.ow.scan() if rom[0] == 0x10 or rom[0] == 0x28]

    def convert_temp(self):
        self.ow.reset(True)
        self.ow.writebyte(self.ow.SKIP_ROM)
        self.ow.writebyte(_CONVERT)

    def read_scratch(self, rom):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_RD_SCRATCH)
        self.ow.readinto(self.buf)
        if self.ow.crc8(self.buf):
            raise Exception('CRC error')
        return self.buf

    def write_scratch(self, rom, buf):
        self.ow.reset(True)
        self.ow.select_rom(rom)
        self.ow.writebyte(_WR_SCRATCH)
        self.ow.write(buf)

    def read_temp(self, rom):
        buf = self.read_scratch(rom)
        if rom[0] == 0x10:
            if buf[1]:
                t = buf[0] >> 1 | 0x80
                t = -((~t + 1) & 0xff)
            else:
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
        else:
            t = buf[1] << 8 | buf[0]
            if t & 0x8000: # sign bit set
                t = -((t ^ 0xffff) + 1)
            return t / 16

Identifier les sondes Dallas DS18B20 en MicroPython

La première chose à faire est donc d’identifier individuellement les sondes reliées sur le bus One-Wire. C’est tout de même plus pratique de savoir si on mesure la température dans la chambre ou dehors.

Créer un nouveau script et collez le code suivant. Modifiez la broche du bus One-Wire. Enregistrez-le en lui donnant le nom de DS18B20_scanner.py (par exemple) puis appuyez sur F5 pour téléverser et exécuter le script. Le script tourne en boucle en scannant le bus chaque secondes. Pour interrompre le script, placez le curseur dans la console (bas de l’écran) et appuyez sur CTRL +C. Branchez successivement les sondes pour les repérer. On utilisera le dernier code hexa pour identifier séparément les sondes. Il y a très peu de chance que deux sondes possèdent un code identique. Si c’est le cas, utilisez une autre partie du code.

# OneWire scanner 
import machine, onewire, time, ds18x20

# Les sondes One-Wire sont connectées au GPIO12 | The One-Wire probes are connected on Pin GPIO12
data = machine.Pin(4)

# Créé l'objet onewire | create the onewire object
ds = ds18x20.DS18X20(onewire.OneWire(data))

while True:
   # scan for devices on the bus
   roms = ds.scan()
   print('found probes:', roms)
   time.sleep_ms(1000)

Lire la température des sondes DS18B20 en MicroPython

Maintenant que chaque sonde a été identifiée, regardons comment récupérer la température. La méthode scan permet de récupérer les identifiants des sondes attachées au bus OneWire. On aura besoin de chaque identifiant pour lire individuellement la température. Ici, une boucle infinie (while True:) permet de relever toutes les secondes la température de chaque sonde. Pour interrompre la boucle, appuyer sur l’icône stop.

import time, machine, onewire, ds18x20

# the device is on GPIO12
data = machine.Pin(4)

# create the onewire object
ds = ds18x20.DS18X20(onewire.OneWire(data))

# Scan le bus OneWire et recupere l'ID de chaque sonde | scan for devices on the bus
roms = ds.scan()
print('found probes:', roms)

# Bouble infinie qui lit et affiche la température de chaque sonde | Infinite loop than read and print temperature of each probe
while True:
    print('temperatures:', end=' ')
    ds.convert_temp()  
    for rom in roms:
        print(ds.read_temp(rom), end=' ')
    print()
    time.sleep_ms(1000)

Préparer les appareils virtuels sur Domoticz

Allez sur le serveur Domoticz pour créer deux appareils virtuels de type température et récupérez l’Idx de chaque sonde. Suivez ce tutoriel pour apprendre comment faire.

domoticz ds18b20 esp8266 esp32 arduino idx device sensor

Se connecter à Domoticz et publier les mesures à l’aide d’une requête HTTP

Voilà, tout est prêt. Nous savons identifier les sondes Dallas DS18B20, lire la température individuellement. Il ne reste plus qu’à envoyer les mesures à un serveur domotique. Ici, nous allons le publier sur un serveur Domoticz à l’aide de l’API JSON. La documentation technique complète est ici.

J’ai modifié le code source de Ralph publié sur le forum MicroPython et ajouté d’autres grandeurs physiques. Pour faire des requêtes HTTP, on dispose de la librairie (module) socket de MicroPython (documentation en ligne). 5 lignes de code suffisent pour se connecter et envoyer une requête HTTP en MicroPython.

import socket
s = socket.socket()
s.connect((IP,PORT))
s.send(b"requete")
s.close()

Créez un nouveau script et collez le code source suivant. Enregistrez le sous le nom de domoticz.py. Copiez-le sur la carte de développement. Pour utiliser ce module, on devra donc initialiser un objet Domoticz en lui passant l’adresse IP du serveur, le port et la clé d’identification (optionnelle). Ensuite, pour envoyer des mesures, il suffira d’appeler la méthode correspondante à la grandeur mesurée. Pour le DS18B20, on utilisera la méthode setTemperature. Chaque méthode attend

  • L’identifiant (Idx) de l’appareil virtuel
  • La (les) valeur(s)
Attention. Le nom de la librairie doit être différent de la classe (class), sinon le module ne pourra pas être appelé. Vous aurez une erreur object is not callable. Pour cela, vous pouvez jouer mettre une majuscule par exemple

La méthode assemble la requête dans le format attendu par l’API JSON de Domoticz. Ensuite, la méthode sendRequest est appelée pour publier les mesures au serveur. La méthode sendRequest attend le retour du serveur. Si le serveur n’est pas disponible, la méthode renvoi 0. Dans le cas contraire, elle retourne le code HTML du serveur. Si le serveur Domoticz a accepté la requête, le code 200 est renvoyé.

# Domoticz JSON API documentation https://www.domoticz.com/wiki/Domoticz_API
# Code adaptated from orignial Ralph script 
# Code adapté du script original de Ralph publié sur le forum MicroPython
# https://forum.pycom.io/topic/240/example-project-pir-sensor-and-domoticz-api
# projetsdiy.fr - diyprojects.io (dec. 2017)

import socket
class Domoticz:
    def __init__(self, ip, port,  basic):
        self.basic = basic
        self.ip = ip
        self.port = port

    def setLight(self, idx, command):
        return self.sendRequest("type=command&param=switchlight&idx="+idx+"&switchcmd="+command)
    
    def setTemperature(self, idx, value):
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+value) 
    
    def setHumidity(self, idx, value, hum_stat):
        # hum_stat: 0=Normal, 1=Comfortable, 2=Dry, 3=Wet
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue="+value+"&svalue="+hum_stat)
        
    def setBarometer(self, idx, value, bar_for):
        # bar_for (forecast): 0 = Stable, 1 = Sunny, 2 = Cloudy, 3 = Unstable, 4 = Thunderstorm
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+value+";"+bar_for)
    
    def setTemperatureHumidity(self, idx, temp, hum, hum_stat):
        # hum_stat: 0=Normal, 1=Comfortable, 2=Dry, 3=Wet
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+temp+";"+hum+";"+hum_stat)
    
    def setTemperatureHumidityBarometer(self, idx, temp, hum, hum_stat, bar, bar_for):
        # hum_stat: 0=Normal, 1=Comfortable, 2=Dry, 3=Wet
        # bar_for (barometer forecast): 0 = No Info, 1 = Sunny, 2 = Paryly Cloudy, 3 = Cloudy, 4 = Rain
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+temp+";"+hum+";"+hum_stat+";"+bar+";"+bar_for)
    
    def setTemperatureBarometer(self, idx, temp, bar, bar_for, altitude):
        # hum_stat: 0=Normal, 1=Comfortable, 2=Dry, 3=Wet
        # bar_for (barometer forecast): 0 = No Info, 1 = Sunny, 2 = Paryly Cloudy, 3 = Cloudy, 4 = Rain
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue=0&svalue="+temp+";"+bar+";"+bar_for+";"+altitude)
    
    def setAirQuality(self, idx, ppm):
        return self.sendRequest("type=command&param=udevice&idx="+idx+"&nvalue="+ppm)   

    def setVariable(self, name, value):
        return self.sendRequest("type=command&param=updateuservariable&vtype=0&vname="+name+"&vvalue="+value)

    def sendRequest(self, path):
        try:
            s = socket.socket()
            s.connect((self.ip,self.port))
            s.send(b"GET /json.htm?"+path+" HTTP/1.1\r\nHost: pycom.io\r\nAuthorization: Basic "+self.basic+"\r\n\r\n")
            status = str(s.readline(), 'utf8')
            code = status.split(" ")[1]
            s.close()
            return code

        except Exception:
            print("HTTP request failed")
            return 0

Si vous avez suivi le tutoriel précédent sur la gestion de la connexion WiFi en MicroPython, copiez la libraire ConnectWifi.py sur votre carte, sinon allez la récupérer ici.

Maintenant créez un nouveau script et collez le code ci-dessous. Modifiez les paramètres suivants

  • pinDs18b20, broche sur laquelle est connecté le bus OneWire
  • insideTemp, identifiant de la sonde intérieure (par exemple), code trouvé avec le script scanner précédent
  • outsideTemp, idem pour la sonde d’extérieure
  • hold_time_sec, temps d’attente en secondes entre deux publications de mes
  • Idx_inside, Idx Domoticz de la sonde intérieure
  • Idx_outside, idem pour la sonde extérieure
  • domoticzIP, adress IP du serveur Domoticz
  • domoticzPort, le port, par défaut 8080

Pour identifier chaque sonde, on va donc tester la fin de l’identifiant. L’identifiant est stocké dans un tableau d’hexadécimal.

IDX = 0
if rom[7] == insideTemp:
   IDX = Idx_inside
else:
   IDX = Idx_outside

Enfin, avant d’exécuter la requête HTTP,  il est nécessaire de convertir la mesure de type float en une chaîne de caractères (String). Pour cela, on dispose de la fonction format du MicroPython

value = "{:.2f}".format(ds.read_temp(rom))

Code complet du projet

# Import des modules microPython | import MicroPython modules
import time, machine, onewire, socket
# Import des modules utilisateurs | Import user modules
import ConnectWiFi, ds18x20, domoticz

# Verifie si l'ESP32/ESP8266 est connecte au reseau WiFi | check if ESP32 is connected to the WiFi network
ConnectWiFi.connect()

# Executer le script pour identifier les sondes connectees
# Run script several times to find identifier
pinDs18b20    = 4
insideTemp    = 43 # fin de l'identifiant sonde interieure | End of the identifier inside probe
outsideTemp   = 188
hold_time_sec = 10 # Temps attente | wait for n secondes before sending values
last_trigger = -10
Idx_inside   = "24"# Idx domoticz sonde intérieur | Domoticz Idx inside Temp.
Idx_outside  = "25"# == sonde exterieure | == outside probe
domoticzIP   = "192.168.1.24"
domoticzPort = 8080

# Adresse IP du serveur Domoticz | Domoticz server IP address
d = Domoticz(domoticzIP, domoticzPort ,"")

# Les sondes One-Wire sont connectees au GPIO12 | The One-Wire probes are connected on Pin GPIO12
data = machine.Pin(pinDs18b20)

# Cree l'objet onewire | create the onewire object
ds = ds18x20.DS18X20(onewire.OneWire(data))

# Scan le bus OneWire et recupere l'ID de chaque sonde | scan for devices on the bus
roms = ds.scan()
print('found probes:', roms)

# Read temperature and send to domoticz
while True:
  if time.time() - last_trigger > hold_time_sec:
    last_trigger = time.time()
    ds.convert_temp()
    for rom in roms:
        IDX = 0
        if rom[7] == insideTemp:
          IDX = Idx_inside
        else:
          IDX = Idx_outside  
        
        try:
            print(ds.read_temp(rom), end=' ')
            value = "{:.2f}".format(ds.read_temp(rom))
            return_code = d.setTemperature(IDX,value)
            print("Request result: "+str(return_code))
        except Exception as e:
            print("Request failed")
            print(e)

Enregistrez le script du projet puis envoyez le sur l’ESP8266/ESP32 avec la touche F5. Si tout est correct, vous devez recevoir un code 200 à chaque requête HTTP.

 upicraft ds18b20 domoticz http request micropython

Les appareils virtuels Domoticz s’actualisent en quelques secondes.

Voilà, cet article conclu cette petite série de tutoriels sur le MicroPython. J’espère que vous avez aimez. Nous reprendrons les tutoriels un peu plus tard pour piloter le bras robotique présenté il y a quelques semaines. Si vous avez des besoins ou des problèmes particuliers, les commentaires sont là pour ça.

Print Friendly, PDF & Email

Inscrivez-vous à la newsletter hebdomadaire

Aucun spam et aucun autre usage ne sera fait de votre email. Vous pouvez vous désinscrire à tout moment.

Comparateur de prix

Bons plans

Les offres suivantes se terminent bientôt. Utilisez le coupon indiqué pour profiter du prix promo

Tags:

Domotique et objets connectés à faire soi-même