Où sommes nous ?

Retour

Mesure de température en 1-wire avec un ESP8266

La théorie sur l'utilisation de sondes DS18B20 pour mesurer des températures a déjà été abordée dans la page sur l'internet des objets. Nous nous focaliserons ici sur l'utilisation avec un ESP8622.

En l'occurence, vu qu'il s'agit de faire des tests, nous le ferons avec un NodeMCU ; en utilisation réelle, un ESP-01 pourrait être largement suffisant : attention cependant qu'il faudra user du fer à souder directement sur la puce si on souhaite bénéficier du DeepSleep, la broche XPD_DCDC n'étant pas exposée.

Enfin, et pour être exhaustif, nous le ferons avec 2 sondes, une DS18B20 et une DS28EA00.

Connexion

Les connexions sont simplissimes :

Et pour pouvoir bénéficier du DeepSleep(), j'ai connecté RST (reset) avec D0.

Attention, ce que j'avais dit pour le 1-wire sur BananaPI reste d'actualité : gérer un bus par bit banging s'entend avec un bus court et peu de sondes, surtout qu'il n'est ici qu'en 3 volts. Et comme il n'y a pas de dispositif pour forcer le bus à l'état haut hors cette pauvre résistance de 4,7k, éviter autant que faire ce peu d'utiliser des sondes alimentées en mode "parasites".
Oublier ces règles posera aléatoirement des problèmes de connexion et diminuera la vie de l'ESP.

Est-ce que ca fonctionne ?

Pour vérifier que notre montage fonctionne, ce premier exemple se contentera de lister les sondes trouvées sur le bus et d'en afficher les mesures, d'une façon sale.

Les librairies

Ancienne version

L'exemple ci-dessous fonctionne avec la librairie OWBus. Les explications pour le code utilisant le classique DallasTemperature sont sur cette page.

Nous aurons besoin des librairies suivantes

#include <OneWire.h>                    // Gestion du bus 1-wire
#include <OWBus.h>
#include <OWBus/DS18B20.h>
#include <OWBus/DS28EA00.h>

Que nous initialiserons comme suit

#define ONE_WIRE_BUS D1           // Où est connecté le bus
OneWire oneWire(ONE_WIRE_BUS);
OWBus bus(&oneWire);

Il est évident que le D1 peut être replacé par le port que vous souhaitez.

Pour la gestions des sondes

Pour faciliter la gestion ultérieure de nos sondes, nous allons stocker leurs adresses par le code suivant : ça évitera de devoir le redemander à chaque fois.

int nbr_sondes;	// Combien avons nous trouvé de sondes ?
DS18B20 **sondes; // pour stocker les sondes

La fonction setup()

Première tâche : initialiser la console pour ne pas rester dans le noir

	Serial.begin(115200);    // débuging
delay(10);    // Le temps que ça se stabilise

Serial.print("Bus 1-wire utilisé :");
Serial.println(ONE_WIRE_BUS);

Commençons par voir combien de sondes sont découvertes

	Serial.print("Nombre de sondes :");
Serial.println(nbr_sondes = bus.getDeviceCount());

Et allouons la mémoire nécessaire pour stocker leurs adresses. On prend bien soin de vérifier que cette allocation fonctionne (on n'est pas chez m$), dans le cas contraire, exit() n'existant pas sur un microcontrôleur (eh oui, vous voulez qu'il fasse quoi en dehors de notre programme), on se contente de le placer dans un sommeil éternel ... jusqu'à son prochain reset.

	if(!(sondes = (DS18B20 **)malloc( sizeof(DS18B20 *) * nbr_sondes))){
Serial.println("Impossible d'allouer les sondes. STOP !");
ESP.deepSleep(0); // On s'arrete là
}
Serial.println("Le stockage des sondes est alloué");

Enfin, la boucle de lecture des différentes adresses

	int duree = millis();
OWBus::Address addr;
bus.search_reset();
int i = 0;
while( bus.search_next( addr ) ){
Serial.print( addr.toString().c_str() );
Serial.print(" : ");
if(!addr.isValid( &oneWire))
Serial.println("Invalid address");
else {
if(i > nbr_sondes){
Serial.println("Hum, plus le même nombre de sondes. Stop !");
ESP.deepSleep(0); // On s'arrete là
}
switch(addr.getFamillyCode()){
case DS18B20::FAMILLY_CODE:
sondes[i] = new DS18B20( bus, addr );
break;
case DS28EA00::FAMILLY_CODE:
sondes[i] = new DS28EA00( bus, addr );
break;
default:
Serial.print( addr.getFamilly() );
Serial.println(" n'est pas prise en compte");
continue; // on passe à la suivante
}
Serial.println( sondes[i]->getFamilly() );
sondes[i]->setResolution( i ? 12:9 );

i++;
}
yield();
}
nbr_sondes = i;

Serial.print("La lecture des sondes à durée ");
Serial.print( millis() - duree );
Serial.println(" milli-secondes")

À noter la fonction yield() : un seul programme tournant sur les ESP (pas de multi-tâches), sans yield() nous risquons de louper des rendez-vous importants (console, Wi-Fi) qui sont gérés en simili tâches de fond.

On notera que la 1er sonde aura une précision de 9bits contre 12 pour les suivantes. Cela influe sur la durée de conversion ... mais comme nous demandons des conversions parallèles (voir plus loin), c'est ici surtout pour voir la différence sur les valeurs reçues (évidement, j'ai vérifié que les sondes n'aient pas une trop grosse dispersion, entre 0,06 et 0,12°C dans mon cas).

et loop()

On commence par faire une demande d'acquisition de température : toutes les sondes vont se lancer dans leur conversion en parallèle ce qui nous fera gagner du temps.

	duree = millis();
bus.launchTemperatureAcquisition(); // broadcaste une demande d'acquisition de température
delay(750); // Laissons le temps à la conversion de se faire

Serial.print("La demande d'acquisition a durée ");
Serial.print( millis() - duree );
Serial.println(" milli-secondes");

Comme nous faisons une demande asynchrone (la demande est envoyée à toutes les sondes, nous n'attendons donc pas la réponse), le delay() est nécessaire pour leur laisser le temps d'effectuer les mesures avant que nous n'en lisions le résultat.

Attention : ces conversions demandent pas mal d'énergie, privilégier un fonctionnement séquentiel s'il y a beaucoup de sondes ce qui, je le répète, est fortement déconseillé avec ce genre de montage sans contrôleur.

Plus il y a la boucle de lecture dont le code parle de lui-même

    	duree = millis();	// Ainsi, nous tentons de mesurer aussi les latences dues à la conversion
for(int i=0; i<nbr_sondes; i++){
float temp;

Serial.print("Température pour ");
Serial.print( sondes[i]->getAddress().toString() );
Serial.print(" : ");
Serial.print( temp = sondes[i]->readLastTemperature() );
Serial.print("°C (");
Serial.print( sondes[i]->getResolution() );
Serial.print(" bits) en ");
Serial.print( millis() - duree );
Serial.println(" milli-secondes");

clientMQTT.publish( (MQTT_Topic + sondes[i]->getAddress().toString()).c_str(), String( temp ).c_str() );
yield();
duree = millis();
}

Petite subtilité quand même : pour lire la température, nous utilisons la méthode readLastTemperature() plutôt que getTemperature() qui aurait forcé une nouvelle acquisition.

et enfin une tempo : prochaine mesure dans 5 minutes.

    delay(300e3);    // Prochaine mesure dans 5 minutes
}

Résultat

Et bien, ça marche très bien !

us 1-wire utilisé :5
Nombre de sondes :2
Le stockage des sondes est alloué
2882b25e09000015 : 18B20
42886847000000bf : 28EA00
La lecture des sondes à durée 60 milli-secondes
La demande d'acquisition a durée 752 milli-secondes
Température pour 2882b25e09000015 : 23.37°C (12 bits) en 13 milli-secondes
Température pour 42886847000000bf : 23.50°C (12 bits) en 13 milli-secondes

A noter que si vous obtenez une valeur de 85°C, c'est que soit vous avez un problème d'alimentation, soit que vous avez oublié de demander la conversion et/ou le délais qui suit.

Ajout de la couche MQTT

Le réseau

On ajoute les librairies qui vont bien

#include <ESP8266WiFi.h>
#include <PubSubClient.h>    /* https://pubsubclient.knolleary.net/api.html */

et le code de connexion

void connexion_WiFi(){
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Connexion WiFi");
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while(WiFi.status() != WL_CONNECTED){
        delay(500);
        Serial.print(".");
    }

    Serial.print("ok : adresse ");
    Serial.println(WiFi.localIP());
    digitalWrite(LED_BUILTIN, HIGH);
}

On peut utiliser une adresse fixe pour gagner du temps (et donc de l'énergie) en ne faisant pas appel au DHCP.

MQTT

La démarche est la même avec dans un premier temps les librairies

#include <ESP8266WiFi.h>
#include <PubSubClient.h>    /* https://pubsubclient.knolleary.net/api.html */

et le code de connexion correspondant.

void Connexion_MQTT(){
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Connexion MQTT");
    while(!clientMQTT.connected()){
        if(clientMQTT.connect(MQTT_CLIENT)){
            Serial.println("connecté");
            break;
        } else {
            Serial.print("Echec, rc:");
            Serial.println(clientMQTT.state());
            delay(1000);    // Test dans 1 seconde
        }
    }
    digitalWrite(LED_BUILTIN, HIGH);
}

Dans la fonction setup(), on ne fait que définir le broker a utiliser (BROKER_HOST et BROKER_PORT sont définis dans une include dédiée contenant toutes les infos confidentielles de mon réseau)

    connexion_WiFi();
    clientMQTT.setServer(BROKER_HOST, BROKER_PORT);

Enfin, loop() devient

#define MQTT_CLIENT "TestTemp"
String MQTT_Topic("TestTemp/");
void loop(){
int duree;
if(!clientMQTT.connected()){
duree = millis();
Connexion_MQTT();
int fin = millis();

Serial.print("La reconnexion a durée ");
Serial.print( fin - duree );
Serial.println(" milli-secondes");
clientMQTT.publish( (MQTT_Topic + "reconnect").c_str(), String( fin - duree ).c_str() );
}

Serial.print("Alimentation : ");
Serial.println(ESP.getVcc());
clientMQTT.publish( (MQTT_Topic + "Alim").c_str(), String( ESP.getVcc() ).c_str() );

duree = millis();
bus.launchTemperatureAcquisition(); // broadcaste une demande d'acquisition de température
delay(750); // Laissons le temps à la conversion de se faire

Serial.print("La demande d'acquisition a durée ");
Serial.print( millis() - duree );
Serial.println(" milli-secondes");

duree = millis(); // Ainsi, nous tentons de mesurer aussi les latences dues à la conversion
for(int i=0; i<nbr_sondes; i++){
float temp;

Serial.print("Température pour ");
Serial.print( sondes[i]->getAddress().toString() );
Serial.print(" : ");
Serial.print( temp = sondes[i]->readLastTemperature() );
Serial.print("°C (");
Serial.print( sondes[i]->getResolution() );
Serial.print(" bits) en ");
Serial.print( millis() - duree );
Serial.println(" milli-secondes");

clientMQTT.publish( (MQTT_Topic + sondes[i]->getAddress().toString()).c_str(), String( temp ).c_str() );
yield();
duree = millis();
}

delay(300e3); // Prochaine mesure dans 5 minutes
}

Conclusion

Le code est somme toute relativement simple. Pour une utilisation complète, seul manque le deep sleep, mais ça sera pour une autre fois vu que le code est un peu complexifié.
La précision, 9 ou 12 bits a une influence négligeable vu que toutes les conversions ont lieu en parallèle.

Comme je le disais en introduction, hormis la question du deep sleep (inutilisé ici), on pourrait facilement créer des sondes de températures déportées à moindre coût en n'utilisant qu'un simple ESP-01.

J'ai mis le code complet sur mon GitHub. J'ai entre autre ajouté du code pour


Visitez :
La liste de nos voyages
Nos sorties Ski et rando
Copyright Laurent Faillie 2001-2024
N'oubliez pas d'entrer le mot de passe pour voir aussi les photos perso.
Contactez moi si vous souhaitez réutiliser ces photos et pour les obtenir avec une plus grande résolution.
Visites durant les 7 derniers jours Nombre de visites au total.

Vous pouvez laissez un commentaire sur cette page.