ESP8266. Développer et tester l'interface HTML avec Node.js et Pug (ancien Jade) • Domotique et objets connectés à faire soi-même

Code source

Le développement d’un Serveur Web pour ESP8266 peut s’avérer rapidement fastidieux. En effet, il faut télécharger les fichiers (HTML, code javascript) à chaque modification pour pouvoir effecter les mises au point. Heureusement, Node.js va nous permettre de développer et mettre au point sur un ordinateur tout le code de la partie Serveur Web avant de la télécharger sur l’ESP8266.

Node.js est tellement génial qu’on va pouvoir utiliser directement les templates Pug (article de présentation) et même récupérer des mesures réelles directement sur l’ESP8266 !

Dans ce tutoriel, nous allons voir comment réaliser rapidement une interface HTML qui permettra d’afficher les mesures d’un DHT22 et de piloter le GPIO de l’ESP8266. On pourra également récupérer les données (et les états) sur un ordinateur à l’aide d’un mini serveur Web développé en quelques lignes de code avec Nodejs. Le code comptes du projet est disponible sur GitHub ici

Matériel nécessaire pour tester le code

Pour tester le code de ce tutoriel, vous pouvez réaliser rapidement un petit montage à l’aide d’un ESP8266, d’une LED et d’une sonde de température DHT22.

Installer Node.js sur macOS, PC Windows ou Linux

Node.js est un projet Open Source populaire cross-plateforme (indépendant du système). A l’origine conçu pour développer des sites internet, Nodejs est maintenant utilisé pour réaliser des applications pour smartphones et tablettes (Meteorjs, ionicframework, phonegap).

Il est possible de “transformer” le site en application. C’est par exemple le cas de l’éditeur de texte Atom développé par GitHub. Et si vous utilisez Node-RED, c’est le moteur de ce projet pour IoT d’IBM. Nous allons en rester là sur Node.js pour cette fois, nous aurons l’occasion de l’utiliser dans de nombreux projets à l’avenir.

La première chose à faire est donc d’installer Node.js sur votre ordinateur. Avant de vous lancer, le mieux est de vérifier que Node.js n’est pas déjà installé sur votre machine. Si certainement le cas si vous utilisez Raspbian. Ouvrez le Terminal ou PowerShell sous Windows et exécutez cette commande

$ node -v; npm -v
v6.9.4
3.10.10

Si vous n’obtenez aucun résultat, allez sur le site officiel de Nodejs pour récupérer la version qui correspond à votre système. Il est préférable d’installer la version stable (LTS) afin d’éviter les effets de bords de la version en cours de développement.

Sur macOS, il est préférable d’installer Node.js à l’aide du gestionnaire de paquets Homebrew (page officielle du projet). Homebrew s’occupe de déclarer les chemins. Il est également plus facile de dés-installer (proprement) Node.js et npm (le gestionnaire de paquet associé) avec la commande brew uninstall node

Ouvrer un Terminal et executer cette commande

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Répondez aux questions pour terminer l’installation de Homebrew sur votre Mac. Une fois l’installation de Homebrew terminée, on peut installer Node.js en exécutant la commande

brew install node

Installer Node.js pour ARM, Raspberry Pi, Orange Pi…

Node.js est également disponible pour les plateformes ARM v6, v7 et v8. L’archive est directement récupérable en téléchargement mais le plus simple reste de passer par le Terminal. On commence par ajouter les sources de Nodejs au gestionnaire de dépôt du système. Complétez la commande en fonction de la version désirée.

Pour la version 7.x en cours de développement

curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -

Pour la version stable

curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

Maintenant, une simple commande apt-get pour installer Node.js sur votre Raspberry Pi par exemple.

sudo apt install nodejs

Tester si tout fonctionne

Nodejs dispose de son propre shell qu’on lance tout simplement avec la commande node dans le Terminal. Si tout est correctement installé, vous obtiendrez l’invite (>). Faites une petite addition. Pour sortir du shell, saisissez .exit

$ node
> 3+1
4
> .exit
$

Préparer un serveur de développement pour Web Serveur ESP8266

On démarre un serveur Web en quelques lignes avec Node.js. Ici, nous allons utiliser express.js qui est un framework simplifiant la gestion des routes (et accélère le rendu des pages, mais c’est accessoire ici). On utilise npm pour installer de nouveau package.

npm install express --save

Pour faire des requêtes HTTP et récupérer des données réelles sur l’ESP8266, nous aurons besoin du package request

npm install request

Utiliser directement les Templates Pug

Ce serait dommage de devoir générer les fichiers HTML après chaque modification. Pour pouvoir utiliser les fichiers Pug, il faut faire 3 choses :

  • Installer le package pug avec la commande npm install pug
  • Créer un dossier views dans le dossier data du projet. Ensuite déplacez tous les fichiers pug à l’intérieur. Il est possible de conserver l’arborescence (mais je n’ai pas testé)
  • Indiquer que le view engine sera pug.

Voici le code du serveur qui va nous permettre de tester en locale l’interface HTML de nos projets ESP8266. Il ne faut que 7 lignes de code pour faire fonctionner l’interface

var express = require('express');
var app = express();

// Créer directement les pages HTML à partir de templates pug 
app.set('views', './views');
app.set('view engine', 'pug');

app.get('/', function(req, res) {
  res.render('index')
});

app.listen(8080);

Enregistrez le fichier sous le nom server.js et lancez le serveur avec la commande node server.js  depuis le Terminal (sans oublier de vous placer dans le répertoire du projet).

Ouvrez un navigateur et saisissez localhost:8080 dans la barre d’adresse.

Et voilà, vous pouvez maintenant développer vos interfaces et le code javascript beaucoup plus rapidement sur votre propre ordinateur !

Mettre à disposition les ressources (images…)

Si votre projet contient des ressources, par exemple un dossier avec des images, vous devez les rendre accessible au navigateur internet.

Pour cela, il faut créer un dossier public dans lequel vous devrez déplacer les ressources (images entre autre). Ensuite on indique à express.js de rendre accessibles les resources publiques au client Web.

app.use(express.static('public'));
app.use('/static', express.static(__dirname + '/public'));

L’arborescence du projet modifiée.

Relancer automatiquement le serveur après chaque modification

Vous avez du remarquer qu’il est nécessaire de relancer le serveur après chaque modification (scripts js, templates pug…). On va donc automatiser le redémarrage du serveur Web après chaque modification.

Pour cela, il existe le plugin nodemon qui ne nécessite que très peu de configuration. Cela évite d’installer et configurer un gestionnaire de tâche tel que Grunt ou Gulp, surdimensionnés dans ce cas !

npm install -g nodemon

Maintenant, on lance le serveur à l’aide de la commande nodemon server.js .

Il y a beaucoup de paramètres disponibles que l’on peut mettre dans un petit fichier de configuration qu’il faudra nommer nodemon.json et placer au même niveau que le fichier server.js.

Dans la section watch, on indique les répertoires à surveiller. La clé ext permet d’indiquer les extensions des fichiers à surveiller.

{  
  "restartable": "rs",
  "ignore": [
    ".git",
    "node_modules/**/node_modules"
  ],
  "verbose": true,
  "execMap": {
    "js": "node --harmony"
  },
  "watch": [
    "views",
    ""
  ],
  "ext": "js json pug"
}

Maintenant, on démarre le serveur avec la commande nodemon server.js . On peut voir ici qu’il y a bien 2 fichiers surveillés. On remarque que le serveur a été automatiquement relancé après une modification du fichier index.pug. Il est aussi possible de relancer le serveur web à n’importe quel moment en saisissant rs.

$ nodemon server.js
[nodemon] 1.11.0
[nodemon] reading config /Volumes/Macintosh HD/Users/christophe/DHT22WebserverESP8266_SPIFFS_Tuto3/data/nodemon.json
[nodemon] to restart at any time, enter `rs`
[nodemon] ignoring: .git .nyc_output .sass-cache bower_components coverage node_modules .git node_modules/**/node_modules
[nodemon] watching: /Volumes/Macintosh HD/Users/christophe/DHT22WebserverESP8266_SPIFFS_Tuto3/data/views/**/*
[nodemon] watching extensions: js,json,pug
[nodemon] starting `node --harmony server.js`
[nodemon] child pid: 10892
[nodemon] watching 2 files
[nodemon] files triggering change check: views/index.pug
[nodemon] matched rule: **/Volumes/Macintosh HD/Users/christophe/DHT22WebserverESP8266_SPIFFS_Tuto3/data/views/**/*
[nodemon] changes after filters (before/after): 1/1
[nodemon] restarting due to changes...
[nodemon] views/index.pug

[nodemon] starting `node --harmony server.js`
[nodemon] child pid: 10902

Répondre aux requêtes de l’interface Web :

Il est beaucoup plus facile de développer et mettre au points les interfaces lorsqu’on dispose d’un jeu de données. On va donc répondre aux appels de l’interface et renvoyer des données. C’est comme ça que j’ai mis au point l’intégration des graphiques Google Charts.

Avec express.js, on récupère un appel sur une route à l’aide de la fonction app.use(‘/route’, function(req, res, next){}). Par exemple, ici on intercepte une demande de données sur la route /mesures.json.

app.use('/mesures.json', function (req, res, next) {
});

On va tenter de récupérer des mesures réelles sur l’ESP8266, et si l’ESP8266 n’a pas répondu dans les 2 secondes (timeout:2000),

request({url: baseUrl + '/mesures.json',timeout:2000}, function (error, response, body) {
});

Il va nous suffire de tester la réponse ou intercepter une erreur pour renvoyer les données de l’ESP8266 ou un jeu de test si l’ESP8266 n’a pas répondu à notre demande. La réponse de l’ESP8266 est contenu dans le body

if (!error && response.statusCode == 200) {
  console.log("Mesures receptionnees depuis ESP8666" + body) 
  res.send(body);
} else {
  console.log("ESP8666 muet, envoi du jeu de données test")
  res.send({"t":"21.70","h":"29.50","pa":"984.43"});
}

Ce qui donne une fois assemblé

app.use('/mesures.json', function (req, res, next) {
  request({url: baseUrl + '/mesures.json',timeout:2000}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log("Mesures receptionnees depuis ESP8666" + body) 
      res.send(body);
    } else {
      console.log("ESP8666 muet, envoi du jeu de données test")
      res.send({"t":"21.70","h":"29.50","pa":"984.43"});
    }
  })
});

Pour le GPIO, c’est un peu plus compliqué. Souvenez-vous, on peut décoder facilement une requête dans laquelle les données sont passées en paramètres dans l’url. Par exemple http://192.168.1.21/gpio&id=D7&etat=1. Coté Node.js, il va falloir construire la requête qui va bien. On va utiliser la fonction request mais cette fois-ci on ne va lui passer une série d’options qui va lui permettre de construire l’URL

var options = {
  url: baseUrl + "/gpio",
  method: 'GET',
  headers: {
    'User-Agent':       'Super Agent/0.0.1',
    'Content-Type':     'application/x-www-form-urlencoded'
  },
  qs: {'id': req.param.('id'), 'etat': req.param('etat')},
  timeout:2000
}

On récupère le valeur de chaque paramètre à l’aide de la fonction req.param(‘param’). Il ne reste plus qu’à interroger l’ESP8266 et renvoyer la réponse ou un jeu de test si ce dernier reste muet après 2 secondes.

request(options, function (error, response, body) {
  if (!error && response.statusCode == 200) {  
    console.log("Retour GPIO ESP8266 : " + body);
    res.send(body);
  } else {
    console.log("ESP8666 muet, envoi du jeu de données test")
    res.send({id:req.param('id'), etat: req.param('etat'), success:'1'});
  }  
};

Code complet du serveur

Voici le code complet du serveur que vous pouvez adapter à vos besoins. Il est également présent dans les dépôts GitHub du projet Web Server ESP8266.

/*
*   Serveur de développement rapide d'interfaces Web pour projets ESP8266
*   Fast development Web Server for ESP8266 projects
*   http://www.projetsdiy.fr - 2017
*/
var express = require('express');
var request = require('request');

// Sert les fichiers publics (css, img) - Serve public files
var app = express();
var baseUrl = "http://192.168.1.22";

// Créé directement les pages HTML à partir de templates pug 
app.set('views', './views');
app.set('view engine', 'pug');

app.use(express.static('public'));
app.use('/static', express.static(__dirname + '/public'));

app.get('/', function(req, res) {
  res.render('index')
});

app.use('/mesures.json', function (req, res, next) {
  request({url: baseUrl + '/mesures.json',timeout:2000}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log("Mesures receptionnées depuis ESP8666" + body) 
      res.send(body);
    } else {
      console.log("ESP8666 muet, envoi du jeu de données test")
      res.send({"t":"21.70","h":"29.50","pa":"984.43"});
    }
  })
});

app.use('/tabmesures.json', function (req, res, next) {
  request({url: baseUrl + '/tabmesures.json',timeout:2000}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log("tabmesures receptionnées depuis l'ESP8666" + body) 
      res.send(body);
    } else {
      console.log("ESP8666 muet, envoi du jeu de données test")
      res.send([{"mesure":"Température","valeur":"21.60","unite":"°C","glyph":"glyphicon-indent-left","precedente":"19.80"},{"mesure":"Humidité","valeur":"29.80","unite":"%","glyph":"glyphicon-tint","precedente":"30.40"},{"mesure":"Pression Atmosphérique","valeur":"984.51","unite":"mbar","glyph":"glyphicon-dashboard","precedente":"984.74"}]);
    }
  })
});

app.use('/graph_temp.json', function (req, res, next) {
  request({url: baseUrl + '/graph_temp.json',timeout:2000}, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log("Graph receptionnées depuis l'ESP8666" + body) // Show the HTML for the Google homepage.
      res.send(body);
    } else {
      console.log("ESP8666 muet, envoi du jeu de données test")
      res.send({"timestamp":[1485273937,1485273938,1485273939,1485273940,1485273941,1485273942,1485273943,1485273944,1485273945,1485273946,1485273947,1485273948,1485273949,1485273950],"t":[23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3,23.3],"h":[35.6,35.6,35.6,35.6,35.6,35.5,35.5,35.4,35.4,35.5,35.5,35.5,35.5,35.5],"pa":[987.7,987.7,987.7,987.8,987.7,987.7,987.7,987.7,987.7,987.8,987.7,987.7,987.7,987.7],"bart":[23.30,23.30,23.30,23.30,23.30,23.30,23.30],"barh":[35.60,35.60,35.50,35.40,35.50,35.50,35.50]});
    }
  })
});

app.use('/gpio', function (req, res, next) {
    var options = {
      url: baseUrl + "/gpio",
      method: 'GET',
      headers: {
          'User-Agent':       'Super Agent/0.0.1',
          'Content-Type':     'application/x-www-form-urlencoded'
        },
      qs: {'id': req.param('id'), 'etat': req.param('etat')},
      timeout:2000
    }

    //_url = baseUrl + "/gpio&id=" + req.param('id') + "&etat="+ req.param('etat');
    //console.log(_url);
    request(options, function (error, response, body) {
    if (!error && response.statusCode == 200) {  
      console.log("Retour GPIO ESP8266 : " + body);
      res.send(body);
    } else {
      console.log("ESP8666 muet, envoi du jeu de données test")
      res.send({id:req.param('id'), etat: req.param('etat'), success:1});
    }  
    });
})

app.listen(8080);

Maintenant, si un ESP8266 est connecté au réseau, vous êtes en mesure de récupérer des données pour mettre au point facilement votre projet. Les messages envoyés sur le Terminal à l’aide de la commande console.log() permettent de faciliter encore un peu plus la mise au point.

nodemon server.js
[nodemon] 1.11.0
[nodemon] reading config /Volumes/Macintosh HD/Users/christophe/DHT22WebserverESP8266_SPIFFS_Tuto3/data/nodemon.json
[nodemon] to restart at any time, enter `rs`
[nodemon] ignoring: .git .nyc_output .sass-cache bower_components coverage node_modules .git node_modules/**/node_modules
[nodemon] watching: /Volumes/Macintosh HD/Users/christophe/DHT22WebserverESP8266_SPIFFS_Tuto3/data/views/**/*
[nodemon] watching extensions: js,json,pug
[nodemon] starting `node --harmony server.js`
[nodemon] child pid: 11071
[nodemon] watching 1 files
Graph receptionnées depuis l'ESP8666{"timestamp":[1485883795],"t":[21.6],"h":[45.8],"pa":[977.9],"bart":[],"barh":[]}
Graph receptionnées depuis l'ESP8666{"timestamp":[1485883795],"t":[21.6],"h":[45.8],"pa":[977.9],"bart":[],"barh":[]}
tabmesures receptionnées depuis l'ESP8666[{"mesure":"Température","valeur":"21.70","unite":"°C","glyph":"glyphicon-indent-left","precedente":"21.60"},{"mesure":"Humidité","valeur":"45.60","unite":"%","glyph":"glyphicon-tint","precedente":"45.80"},{"mesure":"Pression Atmosphérique","valeur":"977.87","unite":"mbar","glyph":"glyphicon-dashboard","precedente":"977.89"}]
express deprecated req.param(name): Use req.params, req.body, or req.query instead server.js:68:22
express deprecated req.param(name): Use req.params, req.body, or req.query instead server.js:68:47
Retour GPIO ESP8266 : {"gpio":"D7","etat":"1","success":"1"}

Voilà, vous pouvez maintenant développer beaucoup plus rapidement vos interfaces HTML et code javascript pour vos projets ESP8266. C’est un tutoriel en mode accéléré. Nous reviendrons plus en détail sur les différentes technologies abordées ici dans de prochains tutoriels.

Mises à jour

8/08/2017 installation du package pug. Merci Frédéric.

Avez-vous aimé cet article ?

[Total: 0 Moyenne: 0]