Menu Fermer

Contrôle de température avec un Raspberry Pi par un LM35 déposé sur un NODEMCU esp8266

Le titre suffit … presque! 

Nous allons tout d’abord décrire la préparation sur le NODEMCU et commencerons évidemment par le schéma Fritzing! Dans la réalité, j’ai commencé par vérifier l’environnement IDE de l’Arduino avant de déposer un LM37 sur une planche à pain. Le LM37 avait déjà été utilisé dans les articles Partie 1/2 et Partie 2/2 d’un communication USB entre un Arduino et le Raspberry Pi. Ici nous ferons la même démarche pour la préparation des sketches, avec le câble USB sur un PC, avant d’utiliser l’interface Wifi avec un petit, vraiment petit serveur Web sur le NODEMCU et une application Java sur le Raspberry Pi.

Schéma Fritzing

Le fil jaune est attaché à la broche analogique A0 du NODEMCU. Le LM35 ici, qui correspond au mien, est arrondi derrière. Si on le regarde de face, le fil rouge positif positif est à gauche et la terre, le fil noir, est à droite.

Une premier sketch

L’idée est de développer un premier sketch pour vérifier le LM35.

Il faudra commencer par configurer l’IDE de l’Arduino qui doit être utilisé pour télécharger ce sketch. Le première chose est de définir l’URL de référence pour télécharger le gestionnaire des cartes supplémentaire.
https://github.com/esp8266/Arduino/releases/download/2.3.0/package_esp8266com_index.json doit être introduit correctement sous Préférences (menu Fichier / Préférences).

Ensuite il sera possible sous Outils, Type de cartes et Gestionnaire de carte, de spécifier le paquet à installer:

Nous pourrons alors sélectionner la bonne carte.

Si nous avons un autre type d’ESP8266, il faudra installer le bon.

Voici à présent ce premier sketch suivi de sa description:

//Pin A0 pour let capteur LM35
int outputpin = A0;
float calibre = .9;

int nbmes = 4; //Nb de mesures
int myTemps[4];
int myTempsSort[4];

void setup() {
  Serial.println("Sketch started");
  Serial.begin(9600);
  delay(2000);

  for (int i=0; i < nbmes; i++) {
    int analogValue = analogRead(outputpin);
    float millivolts = (analogValue/1024.0) * 3300 * calibre;
    myTemps[i] = millivolts;

    delay(1000);
  }
  show(myTemps, nbmes);
  sort(myTemps, nbmes);
  show(myTemps, nbmes);
}

void loop() { //boucle principale et unique
  for (int i = 0; i < (nbmes-1); i++) {
    myTemps[i] = myTemps[i + 1];
  }

  int analogValue = analogRead(outputpin);
  float millivolts = (analogValue/1024.0) * 3300 * calibre;
  myTemps[nbmes - 1] = millivolts;

  show(myTemps, nbmes);

  for (int i = 0; i < nbmes; i++) {
    myTempsSort[i] = myTemps[i + 1];
  } 
  sort(myTempsSort, nbmes);
  show(myTempsSort, nbmes);

  float average = avg(myTempsSort, nbmes)/10;
  Serial.print("T Celsius: ");
  Serial.println(average);
  delay(3000);
}

void sort(int v[], int size) {
  for (int i=0; i < (size-1); i++) {
    for (int j = 0; j < (size-(i+1)); j++) {
      if (v[j] > v[j+1]) {
        int t = v[j];
        v[j] = v[j+1];
        v[j+1] = t;
      }
    }
  }
}

float avg(int v[], int size) {
  int nbval = 0;
  int calc = 0;
  for (int i=1; i < (size-1); i++) {
    calc += v[i];
    nbval += 1;
  } 
  return calc / nbval;
}

void show(int v[], int size) {
  for (int i=0; i < size; i++) {
    Serial.print(myTemps[i]);
    Serial.print(" ");
  }
 Serial.println("");
}

Le fichier lm35modemcu.ino est téléchargeable ici. Il doit être installé dans un sous-répertoire lm35modemcu que nous pourrons alors charger dans l’IDE de l’Arduino.

Le A0 est clair, c’est celui indiqué dans le schéma Fritzing. Le calibre est particulier à cause de la tension de 3.3V qui pourrait varier. Il faudrait donc faire une calibration avec un thermomètre précis. Le 9600 devra correspondre à la vitesse définie dans la fenêtre du moniteur série de l’IDE lors de l’exécution pour voir les textes des print s’afficher correctement.

Au départ nous lisons 4 fois la température pour remplir notre tableau myTemps[]. Dans tout le sketch nous utilisons des entiers pour simplifier les calculs en float. Ce n’est que lors du calcul de moyenne, que nous passons en float. Tous les myTemps[] sont des dixièmes de degrés, 243 pour 24.3 degrés.  Le myTempsSort[] est essentiel: nous trions les 4 dernière valeur en gardant les anciennes dans le tableau myTemps[] : les valeurs de gauche restant les anciennes.

L’idée sera dans la boucle loop, de ne lire qu’une seule valeur et de la déposer, après décalage, dans la dernière position. Après un sort() qui va nous trier les valeurs,  nous retirerons les deux extrêmes et faire la moyenne du reste avec avg(). C’est un bon compromis!

Les show() pour vérifier notre code et les print() seront évidemment retirés dans la version finale Web.

Le moniteur série de l’IDE pourrait apparaître ainsi:

En appuyant avec deux doigts le LM35 nous pouvons faire varier rapidement la température et vérifier ce qui vient dans les deux listes avant et après le tri.

Le code de ce premier sketch sera utilisé pour le suivant.

Le second sketch Web

C’est la version de notre sketch précédent pour recevoir la température par le Web avec notre NODEMCU connecté en WiFi:

Voici à présent ce premier sketch suivi de sa description:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

const char* ssid = ".....";
const char* password = ".....";

//Pin A0 pour let capteur LM35
int outputpin = A0;
float calibre = 0.9;

int loopVal = 0;
int nbmes = 4; //Nb de mesures
int myTemps[4];
float myTemp = 0.0;

ESP8266WebServer server(80);

void handleRoot() {
  String temps = String(loopVal) + ": ";
  for (int i=0; i < nbmes; i++) {
    temps += String(myTemps[i]) + " ";
  }
  server.send(200, "text/plain", temps);
}

void handleNotFound(){
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void setup(void){
  Serial.begin(115200);
  Serial.println("Setup started");

  WiFi.begin(ssid, password);

  //Config static IP
  IPAddress ip(192, 168, 1, 70);
  IPAddress gateway(192, 168, 1, 1);
  IPAddress subnet(255, 255, 255, 0); 
  WiFi.config(ip, gateway, subnet);

  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/temperature", []() {
    server.send(200, "text/plain", String(myTemp));
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");

  delay(2000);

  for (int i=0; i < nbmes; i++) {
    int analogValue = analogRead(outputpin);
    float millivolts = (analogValue/1024.0) * 3300 * calibre;
    myTemps[i] = millivolts;

    delay(1000);
  } 
}

void sort(int v[], int size) {
  for (int i=0; i < (size-1); i++) {
    for (int j = 0; j < (size-(i+1)); j++) {
      if (v[j] > v[j+1]) {
        int t = v[j]; 
        v[j] = v[j+1];
        v[j+1] = t;
      }
    }
  }
}

float avg(int v[], int size) {
  int nbval = 0;
  float calc = 0.0;
  for (int i=1; i < (size-1); i++) {
    calc += v[i];
    nbval += 1;
  }

  return calc / nbval;
}

void show(int v[], int size) {
  for (int i=0; i < size; i++) {
    Serial.print(myTemps[i]);
    Serial.print(" ");
  }
  Serial.println("");
}

void loop(void){
  server.handleClient();
  delay(50);

  loopVal++;
  if (loopVal == 40) {
    loopVal = 0;

    for (int i = 0; i < (nbmes-1); i++) {
      myTemps[i] = myTemps[i + 1];
    }

    int analogValue = analogRead(outputpin);
    float millivolts = (analogValue/1024.0) * 3300 * calibre;
    myTemps[nbmes - 1] = millivolts;

    show(myTemps, nbmes);
  
    int myTempsCopy[4];
    for (int i = 0; i < nbmes; i++) {
      myTempsCopy[i] = myTemps[i + 1];
    }

    sort(myTempsCopy, nbmes);
    show(myTempsCopy, nbmes);
    myTemp = avg(myTempsCopy, nbmes)/10;
    Serial.println(myTemp);
  }
}

Le fichier webmodemcu.ino est téléchargeable ici. Il doit être installé dans un sous-répertoire webmodemcu que nous pourrons alors charger dans l’IDE de l’Arduino.

J’avais déjà écrit un article, en janvier 2017, ESP8266 NodeMCU: Un projet plateforme, qui pourrait aussi être consulté.

Les premiers include correspondent à des bibliothèques disponible dans l’IDE après l’installation du gestionnaire de la carte ESP8266.

Il faudra commencer par adapter le ssid  et le password pour qu’ils correspondent à notre routeur WiFi. Les procédures pour attribuer une adresse IP fixe sont les mêmes que pour un Raspberry Pi et expliquer dans le livre. Ici nous avons choisi l’adresse 192.168.1.70.

Le code pour la mesure de la température est pratiquement un copier/coller du sketch lm35modemcu précédent intégré au code du serveur Web  sur le port 80 (ESP8266WebServer server(80);) . C’est très similaire à un serveur écrit en Java (chapitre 22 du livre).

Il est clair que la première vérification se fera à partir de l’IDE et en utilisant les URL décrits ci-dessous.

Ensuite nous connecterons notre NODEMCU esp8266 avec une alimentation traditionnelle USB et déplacerons notre « bijou » dans une pièce dont nous désirons connaître la température! 

Vérification depuis un navigateur Web

Nous avons programmé deux gestionnaires, un pour la racine, handleRoot(), et un pour la température, server.on(« /temperature », []():

Le gestionnaire de la racine (http://192.168.1.70) va nous vérifier que la boucle fonctionne (variable loopVal, ici 11) et nous montrer les 4 dernières valeurs retournées au dixième de degrés et à un intervalle de 2 secondes.

Le gestionnaire temperature (http://192.168.1.70/temperature) nous retournera la valeur moyenne, ici 22.6 degrés:

Réception de la température depuis une classe Java

Nous nous référerons à la classe LectureUrl du chapitre 22 pour la préparation du projet. Voici un premier exemple de classe Java, ReadNodeMcuTemperature , qu’il faudra évidemment adapter (pas mal de travail):

import java.net.*;
import java.io.*;

public class ReadNodeMcuTemperature {
  public static void main(String[] args) throws Exception {
    URL notreUrl = new URL("http://192.168.1.70/temperature");
    BufferedReader in = 
        new BufferedReader(
            new InputStreamReader(notreUrl.openStream()));

    String inputLine = in.readLine(); 
    System.out.println(inputLine);
    in.close();
  }
}

Mais ça marche. La température sera retournée dans notre console d’Eclipse, par exemple 22.35. Nous pourrons ensuite transférer le fichier  ReadNodeMcuTemperature.class pour exécution sur le Raspberry Pi.

L’article Communication socket – ESP8266 – Raspberry Pi – Python – Java pourrait aussi intéresser les lecteurs.

Exercices

C’est le même principe que le livre: nous présentons et développons un composant ou une application, et nous passons à des extensions ou des améliorations. La meilleure des manières d’apprentissage et de se familiariser avec le logiciel.

Exercice 1

Modifions lm35modemcu.ino et webmodemcu.ino pour intégrer dans la fonction avg() à la fois la fonction sort() et l’utilisation d’une copie identique à myTempsSort[].

Exercice 2

Ecrire une « vraie » classe Java ReadNodeMcuTemperature pour lire la température à intervalle régulier, la stocker dans une base de données SQLite (chapitre 23) avec la date et l’heure et envoyer  chaque jour un Email (chapitre 24) de statistique de température.

Exercice 3

En utilisant l’article Communiquer entre un Arduino et un Raspberry Pi via USB (Python, Java) (2/2) imaginer des cas de figures pour faire varier certains paramètres sur le NODEMCU, par exemple le nombre de mesures, comme 6 au lieu de 4, ou les intervalles de mesures (comme 10 au lieu de 3 secondes). Il faudra donc envoyer du Raspberry Pi ces nouvelles valeurs à réceptionner dans le(s) sketch(s) à adapter.

Date de la dernière modification: 12 février 2019