Mesure de température DS18B20, code Arduino compatible ESP8266 et ESP32, publication sur Domoticz en HTTP • Domotique et objets connectés à faire soi-même

La sonde de température DS18B20 est un capteur de température numérique qui utilise le protocole de communication One Wire. OneWire est un bus de données numérique qui ne nécessite qu’un seul fil. Le bus OneWire permet d’adresser jusqu’à 100 appareils avec un seul fil. 

gqk8bjhrfu5fr5ssgds5-8948946

Code source

Vous avez été plusieurs à me contacter pour me demander en exemple mettant en oeuvre plusieurs capteurs DS18B20 et comment publier les mesures sur un serveur domotique. Dans ce projet, nous allons apprendre comment identifier les capteurs connectés au bus OneWire, lire les mesures puis les publier sur un serveur domotique à l’aide d’une requête HTTP.

Matériel nécessaire

La librairie OneWire est livrée avec plusieurs exemples qui sont compatibles avec l’Arduino, l’ESP8266 et l’ESP32. On pourra donc très facilement publier les mesures vers un serveur domotique en WiFi à l’aide d’une petite requête HTTP.

ghc0hdw3nm2irjbjzzwb-8543879

Repérage des broches. Source : Maxim Integrated

On trouve le DS18B20 conditionné sous deux formats. Sous la forme d’un boitier au standard TO-92 que l’on pourra intégrer à un PCB ou sur un plaque à trou de prototypage. La plage de mesure est très large comparée aux capteurs (DHT par exemple). Elle peut aller de -55°C à 125°C avec une précision de 0,5°C de -10°C à +85°C.

  • Tension d’alimentation de 3V à 5,5V
  • Plage de mesure de température de -55°C à 125°C (-67°F à +257°F)
  • Précision type de -10°C à +85°C : 0,5°C
  • Résolution de mesure programmable de 9 Bits à 12 Bit

Pour des applications nécessitant une résistance à l’eau, on le trouve pré-câblé dans un boitier étanche. Le bus 1-wire supporte une grande distance de transmission de données sans trop de perte. Il n’est pas rare de trouver des câbles pouvant aller jusqu’à 15m.

Plus d’offres

Vous aurez enfin besoin d’une résistance pour filtrer le signal numérique. Une résistance de 4K7 est conseillée.

Circuit avec plusieurs sondes Dallas DS18B20

Le branchement est très simple. Le bus de données One-Wire (1-Wire) est généralement situé sur un fil jaune. Le fil 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 doit placer une résistance (4K7 en général) entre le +5V et le bus de données.

Le bus OneWire est très robuste. On pourra l’alimenter avec une tension 5V ou 3V3, idéal pour les projets d’IoT à base d’ESP82,  ESP32 ou SAMD21. On peut aussi faire varier la valeur de résistance. 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.

Ici le bus OneWire est connecté à la broche D4 d’une LoLin d1 mini. Voici un tableau de correspondance pour diverses cartes de développement

Carte de développement Bus OneWire
Arduino 4
ESP8266 D4
ESP32 4

hfarouyjn4iyg29ooxlc-4261148

Scanner les adresses des sondes DS18B20, code Arduino compatible ESP8266 et ESP32

La première chose à faire est d’identifier les sondes. Chaque sonde dispose d’un identifiant unique sur 8 bits. Malheureusement, elle n’est jamais indiquée sur les emballages. A nous de le faire manuellement. Voici donc un petit scanner qui permet de récupérer les adresses des sondes DS18B20. Il scanne en boucle toutes les 5 secondes le bus OneWire à la recherche des sondes connectées. Il est possible d’ajouter “à chaud” les capteurs sans avoir à redémarrer le programme.

N’oubliez pas de modifier la variable PIN_ONEWIRE en indiquant la broche sur laquelle sont branchés les DS18B20 avant de téléverser le programme.

/*
 * One Wire scanner
 * Testé sur la carte ESP32 Wemos LoLin32 Lite | Checked on Wemos LoLin32 Lite development board
 * Code inspiré de l'exemple livré avec la librairie Arduino DallasTemperature 
 * Code inspired by DallasTemperature Arduino library from
 * http://milesburton.com/Dallas_Temperature_Control_Library
 */
#include 
// Bus OneWie connecté sur la broche 4 | OneWire bus connected on Pin 4
// Installer une résistance de 4.7K entre le +5V et le cable de données
// A 4.7K resistor is necessary between +5V and Data wire  
#define PIN_ONEWIRE D4
OneWire  ds(PIN_ONEWIRE);  

byte i;
byte type_s;
byte data[12];
byte addr[8];
  
void OneWireScanner(){
  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    return;
  }
  
  Serial.print("ROM = ");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print("0x");
    Serial.print(addr[i], HEX);
    if ( i != 7 ) {
      Serial.print(", ");
    }
  }
  
  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 
}

void setup() {
   Serial.begin(115200);
}

void loop() {
  // put your main code here, to run repeatedly:
  OneWireScanner();
  delay(5000);
}

Ouvrez l’IDE Arduino, créez un nouveau croquis et collez le code ci-dessus. Modifiez la broche sur lequel sera relié le capteur DS18B20. Ouvrez ensuite le gestionnaire de librairies, Croquis -> Inclure une bibliothèque -> Gérer les bibliothèques. Faites une recherche sur le mot clé OneWire puis installez la librairie OneWire

0kxvsegxehxrrc0bjaya-4217920

Téléversez le programme puis ouvrez le moniteur série. Voici le journal d’exécution avec deux sondes DS18B20. Décochez l’option “Défilement automatique“. Vous pourrez facilement copier l’adresse de chaque sonde pour l’inclure dans votre projet.

ROM =  0x28,  0xD4,  0xB0,  0x26,  0x0,  0x0,  0x80,  0xBC
  Chip = DS18B20
ROM =  0x28,  0xF4,  0xBC,  0x26,  0x0,  0x0,  0x80,  0x2B
  Chip = DS18B20
No more addresses.

Lire individuellement la température de plusieurs sondes DS18B20, code Arduino compatible ESP8266 et ESP32

Maintenant que chaque capteur de température est identifié, nous allons utiliser la librairie DallasTemperature qui ajoute quelques méthodes très utiles pour gérer les capteurs DS18B20. Retournez dans le gestionnaire bibliothèque pour installer la librairie DallasTemperature.

Créez un nouveau croquis et collez le code ci-dessous. Indiquez la broche sur laquelle est relié le bus de données One-Wire à l’aide de la constante ONE_WIRE_BUS. Vous pouvez modifier la précision de la mesure avec la constante TEMPERATURE_PRECISION. Par défaut, elle est renvoyée sur 10 bits. Enfin, remplacez les addresses de vos sondes pour insideThermometer et outsideThermometer (sonde extérieure).

#include 
#include 

// Data wire is plugged into port 4 on the Arduino or ESP32
#define ONE_WIRE_BUS 4
#define TEMPERATURE_PRECISION 10

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// Tableaux contenant l'adresse de chaque sonde OneWire | arrays to hold device addresses
DeviceAddress insideThermometer = { 0x28,  0xD4,  0xB0,  0x26,  0x0,  0x0,  0x80,  0xBC };
DeviceAddress outsideThermometer = { 0x28,  0xF4,  0xBC,  0x26,  0x0,  0x0,  0x80,  0x2B };

void setup() {
  Serial.begin(115200);
  
  // Start up the library
  sensors.begin();

  // locate devices on the bus
  Serial.print("Locating devices...");
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: "); 
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");

  // Vérifie sir les capteurs sont connectés | check and report if sensors are conneted 
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); 

  // set the resolution to 9 bit per device
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);

  // On vérifie que le capteur st correctement configuré | Check that ensor is correctly configured
  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC); 
  Serial.println();

  Serial.print("Device 1 Resolution: ");
  Serial.print(sensors.getResolution(outsideThermometer), DEC); 
  Serial.println();
}

void printTemperature(String label, DeviceAddress deviceAddress){
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(label);
  if (tempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    Serial.print(" Temp C: ");
    Serial.print(tempC);
    Serial.print(" Temp F: ");
    Serial.println(DallasTemperature::toFahrenheit(tempC));
  }  
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures();
  Serial.println("DONE");

  // print the device information
  printTemperature("Inside : ", insideThermometer);
  printTemperature("Outside : ", outsideThermometer);
  
  delay(5000);
}

Comment fonctionne le code ?

La librairie DallasTemperature attend un objet OneWire qui est relié à la broche de l’ESP32 (ou Arduino). Ici c’est la broche n°4.

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

La librairie DallasTemperature permet de définir une adresse pour chaque sonde (ce qu’on a fait précédemment). Ici, on va définir une sonde intérieure (Inside) et extérieure (Outside). C’est une variable de type DeviceAdress qui attend en paramètre un tableau de valeurs hexadécimales

DeviceAddress insideThermometer = { 0x28,  0xD4,  0xB0,  0x26,  0x0,  0x0,  0x80,  0xBC };
DeviceAddress outsideThermometer = { 0x28,  0xF4,  0xBC,  0x26,  0x0,  0x0,  0x80,  0x2B };

Dans la boucle setup, la méthode sensors.begin()  permet de démarrer la communication avec les sondes. Maintenant, on peut à tout moment aller lire la température sur une sonde en particulier, pour la température extérieure, cela donnera par exemple

sensors.requestTemperatures();                         // Demande la lecture de toutes les sondes | Request all temperatures from probes
float tempC = sensors.getTempC(outsideThermometer);    // Récupère la température de la sonde extérieure | Get temperature from outside probe
Serial.print(" Temp C: ");                             // Imprime la temperature (par défaut en °C | Print outside temp. By default in Celcius
Serial.print(tempC);

La libraire DallasTemperature met à disposition d’autres méthodes très utiles que voici. Tout est disponible dans le code source sur GitHub

// returns the number of devices found on the bus
uint8_t getDeviceCount(void);

// returns true if address is valid
bool validAddress(const uint8_t*);

// returns true if address is of the family of sensors the lib supports.
bool validFamily(const uint8_t* deviceAddress);

// finds an address at a given index on the bus
bool getAddress(uint8_t*, uint8_t);

// attempt to determine if the device at the given address is connected to the bus
bool isConnected(const uint8_t*);

// attempt to determine if the device at the given address is connected to the bus
// also allows for updating the read scratchpad
bool isConnected(const uint8_t*, uint8_t*);

// read device's scratchpad
bool readScratchPad(const uint8_t*, uint8_t*);

// write device's scratchpad
void writeScratchPad(const uint8_t*, const uint8_t*);

// read device's power requirements
bool readPowerSupply(const uint8_t*);

// get global resolution
uint8_t getResolution();

// set global resolution to 9, 10, 11, or 12 bits
void setResolution(uint8_t);

// returns the device resolution: 9, 10, 11, or 12 bits
uint8_t getResolution(const uint8_t*);

// set resolution of a device to 9, 10, 11, or 12 bits
bool setResolution(const uint8_t*, uint8_t, bool skipGlobalBitResolutionCalculation = false);

// sets/gets the waitForConversion flag
void setWaitForConversion(bool);
bool getWaitForConversion(void);

// sets/gets the checkForConversion flag
void setCheckForConversion(bool);
bool getCheckForConversion(void);

// sends command for all devices on the bus to perform a temperature conversion
void requestTemperatures(void);

// sends command for one device to perform a temperature conversion by address
bool requestTemperaturesByAddress(const uint8_t*);

// sends command for one device to perform a temperature conversion by index
bool requestTemperaturesByIndex(uint8_t);

// returns temperature raw value (12 bit integer of 1/128 degrees C)
int16_t getTemp(const uint8_t*);

// returns temperature in degrees C
float getTempC(const uint8_t*);

// returns temperature in degrees F
float getTempF(const uint8_t*);

// Get temperature for device index (slow)
float getTempCByIndex(uint8_t);

// Get temperature for device index (slow)
float getTempFByIndex(uint8_t);

// returns true if the bus requires parasite power
bool isParasitePowerMode(void);

// Is a conversion complete on the wire?
bool isConversionComplete(void);

int16_t millisToWaitForConversion(uint8_t);

Test du programme avec deux sondes DS18B20

Téléversez le programme et ouvrez le moniteur série. La lecture individuelle de chaque sonde est immédiate.

Inside :  Temp C: 18.00 Temp F: 64.40
Outside :  Temp C: 18.25 Temp F: 64.85
Requesting temperatures...DONE

Si vous débranchez l’une des sondes, un message d’erreur est affiché à coté de la sonde défectueuse. Il est très facile d’envoyer un email pour indiquer qu’une sonde est défectueuse. Pour tester les sondes, on dispose également de la méthode isConnected(adresse_sonde) de la librairie DallasTemperature. Le bus One-Wire fonctionne à chaud. Dès qu’on rebranche la sonde, elle est automatiquement détectée et la lecture de température est immédiate. C’est vraiment très pratique. On pourrait imaginer une petite interface web pour gérer les sondes sans avoir à recompiler le programme dès qu’on ajoute une nouvelle sonde. Par aller plus loin, vous pouvez lire cette série d’articles sur la création d’une interface WEB pour projets DIY à base d’ESP8266

Inside :  Temp C: 18.00 Temp F: 64.40
Outside : Error getting temperature

Publier les mesures sur un serveur Domoticz par requête HTTP

Il ne reste plus qu’à envoyer les données à un serveur domotique. Ici, nous allons prendre l’exemple de Domoticz qui dispose d’une interface (API) JSON. Le format de la requête HTTP est le suivant pour une mesure de type température (la documentation de l’API est détaillée ici). Pour plus de détails, lisez cet article précédent ou celui-ci pour faire la même chose avec Jeedom.

Commencez par aller 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.

qprusak9gmcquchqqsjk-4123020

Le code suivant est compatible avec l’Arduino, l’ESP8266 et l’ESP32. Plusieurs paramètres doivent être modifiés dans le code avant de le téléverser :

  • Indiquez la broche du bus One Wire sur le constante ONE_WIRE_BUS
  • Remplacez les adresses des sondes, insideThermometer et outsideThermometer
  • Le réseau WiFi sur lequel se connecter avec la constante wifi_ssid, ainsi que le mot de passe (password)
  • L’adresse IP du serveur Domoticz, variable host, le port de connexion (par défaut 8080)
  • Les Idx Domoticz pour chaque sonde IDX_insideTemp et IDX_outsideTemp
/*
 * Read multiple One-Wire DS18B20 probes and publish value on Domoticz with HTTP request
 * Lecture multiple de sonde OneWire DS18B20 et plublication des mesures sur un serveur Domoticz requete HTTP
 * Code adapté - Code adaptated 
 * 
 */
#include 
#include 
// Pour un ESP32 (le SDK Espressif doit être installé) | For ESP32 (Espressif SDK must be installed) 
#include 
#include 
// Pour une carte ESP8266 | For ESP8266 development board
//#include 
//#include 
#include 

// Data wire is plugged into port 4 on the Arduino or ESP32
#define ONE_WIRE_BUS 4
#define TEMPERATURE_PRECISION 10

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// Tableaux contenant l'adresse de chaque sonde OneWire | arrays to hold device addresses
DeviceAddress insideThermometer = { 0x28,  0xD4,  0xB0,  0x26,  0x0,  0x0,  0x80,  0xBC };
DeviceAddress outsideThermometer = { 0x28,  0xF4,  0xBC,  0x26,  0x0,  0x0,  0x80,  0x2B };

// Parametres WIFI - WiFi settings
#define wifi_ssid "xxxxxxxx"
#define wifi_password "xxxxxxxx"

// Paramètres HTTP Domoticz - HTTP Domoticz settings
const char* host = "xxx.xxx.xxx.xxx";
const int   port = 8080;
#define IDX_insideTemp    24
#define IDX_outsideTemp   25 
HTTPClient http;

void setup() {
  Serial.begin(115200);

  // Connexion au réseau WiFi, connexion aux sondes
  // Start WiFi connexion and probes
  setup_wifi();           
  sensors.begin();

  // locate devices on the bus
  Serial.print("Locating devices...");
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: "); 
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");

  // Vérifie sir les capteurs sont connectés | check and report if sensors are conneted 
  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0"); 
  if (!sensors.getAddress(outsideThermometer, 1)) Serial.println("Unable to find address for Device 1"); 

  // set the resolution to 9 bit per device
  sensors.setResolution(insideThermometer, TEMPERATURE_PRECISION);
  sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION);

  // On vérifie que le capteur st correctement configuré | Check that ensor is correctly configured
  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(insideThermometer), DEC); 
  Serial.println();

  Serial.print("Device 1 Resolution: ");
  Serial.print(sensors.getResolution(outsideThermometer), DEC); 
  Serial.println();
}

void printTemperature(String label, DeviceAddress deviceAddress){
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print(label);
  if (tempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    // Format JSON à respecter pour l'API Domoticz - Domoticz JSON API 
    // /json.htm?type=command&param=udevice&idx=IDX&nvalue=0&svalue=TEMP
    // https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Temperature
    if ( label == "Inside : " ) {
      String url = "/json.htm?type=command&param=udevice&idx=";
        url += String(IDX_insideTemp);
        url += "&nvalue=0&svalue=";    
        url += String(tempC); 
      sendToDomoticz(url);
    } else {
      String url = "/json.htm?type=command&param=udevice&idx=";
        url += String(IDX_outsideTemp);
        url += "&nvalue=0&svalue=";    
        url += String(tempC);  
      sendToDomoticz(url);
    }
  }  
}

void loop() {
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures();
  Serial.println("DONE");

  // print the device information
  printTemperature("Inside : ", insideThermometer);
  printTemperature("Outside : ", outsideThermometer);
  
  delay(5000);
}

//Connexion au réseau WiFi
void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(wifi_ssid);

  WiFi.begin(wifi_ssid, wifi_password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connexion OK ");
  Serial.print("=> Addresse IP : ");
  Serial.print(WiFi.localIP());
}


void sendToDomoticz(String url){
  Serial.print("Connecting to ");
  Serial.println(host);
  Serial.print("Requesting URL: ");
  Serial.println(url);
  http.begin(host,port,url);
  int httpCode = http.GET();
    if (httpCode) {
      if (httpCode == 200) {
        String payload = http.getString();
        Serial.println("Domoticz response "); 
        Serial.println(payload);
      }
    }
  Serial.println("closing connection");
  http.end();
}

Voilà, vous pouvez maintenant ajouter assez rapidement des sondes DS18B20 à vos projets d’objets connectés Arduino, ESP8266 ou ESP32. On trouve le DS18B20 sous la forme d’un boitier à 3 broches ou le plus souvent pré-câblé dans un boitier étanche en acier inoxydable. On pourra par exemple l’utiliser pour réguler un chauffage avec une sonde d’ambiance intérieure et une sonde extérieure. Le boitier inox est très bien adapté pour surveiller la température dans des milieux humides ou poussiéreux (frigo, congélateur, piscine, aquarium…), des milieux inaccessibles aux capteurs habituels (DHT22, DHT12 sur bus I2C…). Si vous n’avez aucune notion de programmation, le DS18B20 est également supporté par le firmware ESP Easy sur ESP8266 et maintenant sur ESP32.

q9ugn7mlgggv257z7ugx-5744535

Avez-vous aimé cet article ?