DIY Projects

ESP32 MicroPython project with several DS18B20 probes, publish the measurements to Domoticz using the HTTP JSON API

Best deal at:

After several articles to present the principles of MicroPython on ESP8266 and ESP32 development board, it’s time to get down to business. I propose to take the Arduino reading code of several Dallas DS18B20 temperature measurement probes in MicroPython (previous article). Here, we will use the uPiCraft code editor already presented previously in this tutorial and this one (management of the WiFi connection). uPiCraft contains some add-ons including a module to scan the One-Wire bus and read the temperature very simply. We will also discover how to connect to a Domoticz server and publish the metrics. If you do not want to use uPiCraft, the DS18x20 module source code is available further down the article. If you do not have the MicroPython firmware installed on an ESP8266 or ESP32 board, follow this tutorial with the script or this one with uPiCraft.

Necessary material

The code works was on ESP8266 as well as on ESP32. The One-Wire bus requires only one wire to communicate with the micro-controller. The code can retrieve the temperature of 2 Dallas DS18B20 probes but you can connect up to 100. It should be enough to cover an entire house!

Circuit with multiple Dallas DS18B20 probes

The connection is very simple. The One-Wire bus is usually located on a yellow wire. Red is used for power, black for GND as usual. For the bus to work, it must be “de-wormed”. For this, a resistor (4K7 in general) is placed between the + 5V and the data bus. I also tried other resistors (5K7) and a power supply + 3V3 successfully. Obviously, everything will depend on the cable length. The longer the cabling, the more rigorous it will be to supply the power and the de-interference of the signal.

For this tutorial, the DS18B20 is plugged into the D4 pin of the ESP8266. All the codes in this tutorial can be used on an Arduino Uno or ESP32.

Copy the DS18X20 library for MicroPython on the ESP32 / ESP8266

The uPiCraft IDE comes with some additional libraries to the native MicroPython modules. The DS18X20 library is stored with the other libraries in the side navigation bar in the uPi_Lib section.

Log in to your MicroPython development board. If you missed the previous episodes, go to the Tools menu and select Serial to choose the COM port to which the board is connected. Then click on the connect button.

To copy the library to the board, simply drag and drop on the connected device. The other method is to open the library and then choose the Download option in the Tools menu.

If you are not using the uPiCraft IDE, create a new file named DS18X20.pu and paste the source code. It is also available on GitHub. This driver allows you to create an object attached to the One-Wire probe pin. Several methods are available:

# 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):

    def read_scratch(self, rom):
        if self.ow.crc8(self.buf):
            raise Exception('CRC error')
        return self.buf

    def write_scratch(self, rom, 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)
                t = buf[0] >> 1
            return t - 0.25 + (buf[7] - buf[6]) / buf[7]
            t = buf[1] << 8 | buf[0]
            if t & 0x8000: # sign bit set
                t = -((t ^ 0xffff) + 1)
            return t / 16

Identify Dallas DS18B20 probes in MicroPython

The first thing to do is to individually identify the probes connected on the One-Wire bus. It is still more practical to know if we measure the temperature in the room or outside.

Create a new script and paste the following code. Change the pin of the One-Wire bus. Save it as (for example) and press F5 to upload and execute the script. The script runs in a loop by scanning the bus every second. To stop the script, place the cursor in the console (bottom of the screen) and press CTRL + C. Connect the probes successively to locate them. The last hex code will be used to identify the probes separately. There is very little chance that two probes have the same code. If so, use another part of the 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)

Read the temperature of DS18B20 probes in MicroPython

Now that each probe has been identified, let’s look at how to recover the temperature. The scan method retrieves the identifiers of the probes attached to the OneWire bus. Each identifier will be needed to read the temperature individually. Here, an infinite loop (while True 🙂 makes it possible to record the temperature of each probe every second. To interrupt the loop, press the stop icon.

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=' ')
    for rom in roms:
        print(ds.read_temp(rom), end=' ')

Prepare virtual appliances on Domoticz

Go to the Domoticz server to create two virtual devices of the temperature type and get the Idx of each probe. Follow this tutorial to learn how to do it.

Log in to Domoticz and publish metrics using an HTTP request

That’s it, everything is ready. We know how to identify Dallas DS18B20 probes, read the temperature individually. All that remains is to send the measurements to a home automation server. Here we will publish it on a Domoticz server using the JSON API. The complete technical documentation is here.

I modified Ralph’s source code published on the MicroPython forum and added other physical quantities. To make HTTP requests, we have the MicroPython socket (module) library (online documentation). 5 lines of code are enough to connect and send an HTTP request in MicroPython.

import socket
s = socket.socket()

Create a new script and paste the following source code. Save it as Copy it on the development board. To use this module, we must initialize a Domoticz object by passing it the IP address of the server, the port and the identification key (optional). Then, to send measurements, it will suffice to call the method corresponding to the – measured quantity. For the DS18B20, we will use the setTemperature method. Each method waits

Warning. The name of the library must be different from the class, otherwise the module can not be called. You will have an error object is not callable. For this, you can play capitalize for example. 

The method assembles the query in the format expected by the Domoticz JSON API. Then, the sendRequest method is called to publish the measurements to the server. The sendRequest method is waiting for the server to return. If the server is not available, the return 0 method. Otherwise, it returns the HTML code of the server. If the Domoticz server has accepted the request, code 200 is returned.

# Domoticz JSON API documentation
# Code adaptated from orignial Ralph script 
# Code adapté du script original de Ralph publié sur le forum MicroPython
# - (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):
            s = socket.socket()
            s.send(b"GET /json.htm?"+path+" HTTP/1.1\r\nHost:\r\nAuthorization: Basic "+self.basic+"\r\n\r\n")
            status = str(s.readline(), 'utf8')
            code = status.split(" ")[1]
            return code

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

If you followed the previous tutorial on managing the WiFi connection in MicroPython, copy the library to your board, otherwise go and get it here.

Now create a new script and paste the code below. Change the following settings

To identify each probe, we will test the end of the identifier. The identifier is stored in a hexadecimal array.

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

Finally, before executing the HTTP request, it is necessary to convert the float measure to a string. For this, we have the format function of MicroPython.

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

Full project code

# 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

# 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   = ""
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()
    for rom in roms:
        IDX = 0
        if rom[7] == insideTemp:
          IDX = Idx_inside
          IDX = Idx_outside  
            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")

Domoticz virtual devices update in seconds.

Here, this article concludes this small series of tutorials on MicroPython. I hope you like it. We will resume the tutorials a little later to drive the robotic arm presented a few weeks ago. If you have special needs or problems, the comments are there for that.

Click to rate this post!
[Total: 0 Average: 0]
Exit mobile version