Créer un capteur de luminosité en utilisant un ESP8266 et une photoresistance pour 5€, envoyer directement les mesures dans une base de données Redis via la pile Wifi/TCP/IP et publier un message « push » pour notifier votre interface utilisateur (décrite dans un futur article) qu’il y a une nouvelle valeur à afficher en temps réel. Redis est une base clé-valeur en mémoire avec la persistence sur le disque, la haute-disponibilité et la distribution des données en cluster. Il est tellement facile à utiliser qu’un simple micro-controleur, tel qu’un Arduino ou un ESP8266, avec une pile TCP/IP peut s’y connecter et l’utiliser.

Vous pouvez trouver des liens vers les supports associés à la fin de cet article.

Pré-requis

Matériel

Je devais construire environ 200 de ces périphériques, au plus bas prix, dans un délais aussi court que possible. L’idée était de les construire et d’enseigner comment les utiliser avec Redis dans les meetups en France. Les participants pouvaient conserver les périphériques après la rencontre pour continuer à jouer avec à la maison.

Ils devaient être aussi peu cher que possible (j’ai atteint moins de 5€), aussi faciles et rapides que possible à assembler (30 secondes de moins par périphérique sont importantes quand on en fabrique 200), aussi polyvalents que possibles pour y connecter d’autres capteurs (thermistances, capteurs numériques, capteurs I2C, capteurs SPI, …).

Tout d’abord, il faut un peu de matériel. Malgré le fait que je sois un adepte d’Arduino, ils nécessiteraient une carte Wifi (chère) et de la soudure (long à construire). J’ai choisi d’utiliser des petites plaques de prototypage, un ESP8266 tout-en-un, une résistance et une photorésistance (LDR), qui peut facilement être remplacée par une thermistance pour le même prix.

Composants et outils

Parlons maintenant des fournitures : IoT1 Pleine taille

Mes prix comportent une remise car j’en ai commandé 100 de chaque.

Qté Composant Prix (€) Lien
1 Plaque de prototypage 170 points 0.86 Banggood
1 ESP8266 ESP-12E/F sur PCB 3.18 Banggood
1 Résistance 10K Ohms (metal/carbone) 0.03 Conrad
1 GL5528 LDR (Photorésistance) 0.02 Banggood
1 Cable micro-USB 1.10 Banggood
  Total 5.19  
  • Pince fine
  • Pince coupante

Construction

Voici les étapes. Les liens permettent d’ouvrir les photos détaillées intermédiaires.

Félicitations !!! C’est terminé pour le matériel !

Logiciel

Interface de développement, compilateur croisé et téléversement

Nous allons avoir besoin d’un logiciel pour écrire notre code, l’interface de développement (IDE), le logiciel pour compiler notre code dépendant à la fois du couple ordinateur/système d’exploitation et du micro-controlleur visé, le compilateur croisé, et du logiciel pour téléverser le code compilé dans le périphérique.

Il existe plusieurs interface et chaînes de compilation disponibles pour compiler du code C/C++ pour des micro-controlleurs. J’ai choisi d’utiliser l’IDE très simple mais suffisante fournie par le projet Arduino. Elle est générique mais peut automatiquement télécharger la chaîne de compilation nécessaire pour un micro-controlleur et compatible avec votre ordinateur, elle peut également télécharger et inclure de nombreuses bibliothèques depuis des dépôts sur internet.

Vous pouvez soit aller sur le site Arduino pour télécharger l’IDE1, soit cliquer sur les liens de téléchargement direct :

Extraire l’archive dans un répertoire, n’importe où dans votre système de fichiers. l’interface sera exécutée depuis ce répertoire et tous vos fichiers s’ytrouveront aussi. Le répertoire pourra être déplacé n’importe où, n’importe quand.

Bibliothèques, extraits de code et personnalisation de l’interface

L’interface peut être personnalisée pour devenir déplaçable et embarquer tout dans un seul répertoire (l’interface elle-même, la configuration, la chaîne de compilation pour les périphériques et votre code). Pour cela, nous devons uniquement créer un sous-répertoire portable qui contiendra tout notre code, toutes les bibliothèques et les fichiers de configuration (un peu comme un répertoire personnel).

J’en ai préparé un et ai créé un fichier de configuration avec les dépôts externes. Comme indiqué précédemment, la chaîne de compilation doit être installes. Comme les binaires dépendent de votre ordinateur et de votre système d’exploitation, je ne peux que préconfigurer le dépôt à partir duquel vous devrez les télécharger et les installer par vous-même. Une bibliothèque de connexion à Redis et un modèle de code sont également inclus dans le fichier de configuration.

Le fichier ajoutera aussi le code source complet de cet article de blog.

  • Télécharger l’archive de personnalisation

  • Extraire l’archive à la racine du répertoire d’installation de l’interface (ou dans le sous-répertoire Contents/java pour les utilisateurs de MacOS).

  • Démarrer l’interface

  • Aller dans le menu Tools/Board/Board manager (éventuellement Françisé) menu du gestionnaire de cartes

  • Chercher ESP8266 (il devrait y en avoir un trouvé depuis les dépôts préconfigurés). le Selectionner dans la liste et l’installer. Menu du gestionnaire de cartes

  • Refermer la boîte de dialogue et sélectionner NodeMCU 1.0 (ESP-12E Module) dans le menu Tools/Board Menu du gestionnaire de cartes

C’en est terminé avec l’installation et la configuration de l’interface.

Redis

Vous aurez besoin d’une base de données Redis, il peut s’agit de l’édition communautaire ou de l’édition entreprise. Cet article de blog ne traitera pas de l’implémentation du protocole des sentinelles, ni de celui du cluster, nécessaire respectivement pour la haute-disponibilité et pour la répartition des données dans l’édition communautaire. Ces deux protocoles ne sont pas nécessaires ni pour la haute-disponibilité, ni pour la répartition dans l’édition entreprise, ces fonctionnalités seront automatiquement activées dans l’édition entreprise.

Étant donné que l’édition entreprise est vue comme une simple instance Redis par les applications, indépendemment de l’activation de la répartition ou de la réplication, et afin de garder cet article d’une taille raisonnable, je décrirai l’installation et l’utilisation de l’édition communautaire, sans répartition et sans réplication.

Pour éviter d’avoir besoin des droits d’administration, pour garder l’installation de Redis déplaçable et pour maîtriser les versions utilisées, je n’utiliserai pas les paquetages redis-server disponible dans la plupart des distributions Linux, mais je vais télécharger, compiler, configurer et exécuter la version de Redis disponible sur le site officiel de Redis communautaire.

Télécharger le code source depuis le lien suivant Source Redis et l’extraire dans un répertoire. Ouvrir un terminal dans ce répertoire et saisir make. Une fois la compilation terminée, exécuter Redis à partir du même répertoire, en saisissant ./src/redis-server --protected-mode no (la protection limite les connexions entrantes à l’ordinateur local).

C’est terminé en ce qui concerne Redis. Epoustouflant, n’est-ce pas ? Seulement quatre commandes (téléchargement, extraction, compilation et exécution) et vous disposez d’une installation déplaçable démarrée.

Application sur le micro-controlleur

Il est désormais temps de commencer à coder !

Création d’une nouvelle application

Vous devriez avoir un squelette d’application dans votre interface lorsque vous la démarrez. Il y a deux functions obligatoires : void setup() et void loop(). Lorsque vous réinitialisez le périphérique (que ce soit matériellement avec le bouton, depuis le port USB, en l’alimentant ou par logiciel), il exécute le micro-programme de démarrage (bootloader). Si des données sont présentes sur le port série (ponté sur le connecteur USB), il les enregistre en mémoire flash puis exécute le contenu de la mémoire flash. S’il n’y a pas de données, il passe directement à l’exécution du programme en mémoire flash.

À chaque cycle d’alimentation ou de réinitialisation, le programme exécute la fonction setup une fois, puis itère sur la fonction loop sans fin.

Donc, il faut initialiser la console série (pour le deboggage) et ourir la connexion Wifi depuis la fonction setup. Ensuite, à chaque itération de la fonction loop, il faut vérifier si l’on doit lire et envoyer une valeur dans Redis. Si oui, il faut vérifier que nous disposons d’une connexion TCP ouverte vers le serveur Redis, éventuellement en ouvrir une et envoyer la valeur.

Lecture de la mesure

Premièrement, initialiser le retour sur la console série et lire la mesure du capteur.

Dans la fonction setup, initialiser la console série et attendre que ce soit fait (uniquement utile pour certains modèles d’Arduino, tels que le Yun, mais sans incidence pour les autres).

void setup() {
  // Serial console initialization for debugging
  Serial.begin(115200);
//  Serial.setDebugOutput(true); // Wifi debugging
  while (!Serial);
  Serial.println("Serial initialized.");
}

In the loop function, print the sensor’s value, from the Analog-Ditial-Converter, to the serial console.

void loop() {
    Serial.print("Sensor value (0-1024) : ");
    Serial.println(analogRead(0));
}

Tester le code avec l’icone Vérifier dans la barre d’outils, depuis le menu Sketch > Verify/Compile ou avec le raccourcis clavier Ctrl+R.

Si vous n’avez pas d’erreur, téléverser le binaire vers le périphérique en utilisant l’icone flèche, le menu Sketch > Upload ou le raccourcis clavier Ctrl+U. L’exécution commence dès le téléversement terminé.

Ouvrir la console série à l’aide de l’icone loupe (à droite de la barre d’outils), le menu Tools > Serial Monitor ou avec le raccourcis clavier Shift+Ctrl+M. Ajuster la vitesse de communication en fonction de celle définie dans le code (115200) et observer.

Vous pouvez constater la vitesse d’exécution de la fonction loop. Même s’il serait possible d’introduire un appel à la fonction void delay(long ms) dans la function loop, ce serait une mauvaise pratique, la fonction loop a besoin de s’exécuter aussi rapidement que possible, sans blocage, c’est la boucle principale de gestion des événements. Il est préférable d’initialiser une variable retenant l’heure de la dernière lecture à chaque affichage et de ne lire une nouvelle valeur qu’après un certain délai.

unsigned long lastSensorRead=0;
void loop() {
  if ((millis() - lastSensorRead)>5000) {
    lastSensorRead = millis();
    Serial.print("Sensor value (0-1024) : ");
    Serial.println(analogRead(0));
  }
}

Compiler, téléverser et observer… C’est mieux, seulement une valeur toutes les 5 secondes.

Connexion au réseau Wifi

Commençons par la fonction setup. il faut définir le nom du réseau Wifi et son mot de passe pour s’y connecter. Ajouter l’en-tête ESP, définir les informations de connexion au Wifi et ajouter les lignes suivantes dans la fonction setup. J’ai également inclus un clignotement de la LED pendant la phase de connexion pour avoir un retour d’état matériel, ainsi que des lignes de réinitialisation commentées en cas de problème inexplicable.

//  your network SSID (name)
#define WIFI_SSID "YourWifiNetwork"
#define WIFI_PASS "YourWifiNetworkPassword"

#include <ESP8266WiFi.h>

unsigned long lastSensorRead=0;

void setup() {
  // Serial console initialization for debugging
  Serial.begin(115200);
//  Serial.setDebugOutput(true); // Wifi debugging
  while (!Serial);
  Serial.println("Serial initialized.");

  // WIFI connection
//  ESP.eraseConfig();           // in case of need
//  ESP.reset();                 // in case of need
//  WiFi.softAPdisconnect(true); // in case of need
//  WiFi.disconnect(true);       // in case of need
  Serial.print("Connecting to ");
  Serial.print(WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  // Blink LED twice per second while waiting for connection
  pinMode(LED_BUILTIN, OUTPUT);
  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(250);
    digitalWrite(LED_BUILTIN,LOW);
    delay(250);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("WiFi (");
  Serial.print(WiFi.macAddress());
  Serial.print(") connected with IP ");
  Serial.println(WiFi.localIP());
  Serial.print("DNS0 : ");
  Serial.println(WiFi.dnsIP(0));
  Serial.print("DNS1 : ");
  Serial.println(WiFi.dnsIP(1));
}

Compiler, Téléverser et observer…

Connexion TCP à Redis

Il est maintenant temps de vérifier si la connexion à Redis est ouverte. Nous pouvons utiliser l’objet WiFiClient. Il peut ouvrir une connexion vers une adresse IP ou un nom DNS et un numéro de port. Il s’occupera de la résolution d’adresse DNS, mais aussi éventuellement conserver cette résolution en cache (ce qui peut poser problème si les DNS sont utilisés dans l’architecture pour la haute-disponibilité). Le support de l’encryption SSL est limité dans l’ESP, mais nous ne l’utiliserons pas dans cet article.

L’idée est de vérifier que nous disposons d’une connexion ouverte à chaque lecture de la mesure. Si nous ne l’avons pas, nous l’ouvrons.

Définir la connexion Redis, ajouter un objet WiFiClient et modifier la fonction loop :

//  your network SSID (name)
#define WIFI_SSID "Freebox-AEA6A1"
#define WIFI_PASS "adherebit-commend96-sonatio#!-calidior26"

// Redis details
#define REDISHOST "redisdb.server.com"
#define REDISPORT 6379

#include <ESP8266WiFi.h>

unsigned long lastSensorRead=0;
WiFiClient redis;
unsigned long lastValue=0;

void loop() {
  if ((millis() - lastSensorRead)>5000) {
    lastSensorRead = millis();
    lastValue = analogRead(0);
    Serial.print("Sensor value (0-1024) : ");
    Serial.println(lastValue);
    if (!redis.connected()) {
      Serial.print("Redis not connected, connecting...");
      if (!redis.connect(REDISHOST,REDISPORT)) {
        Serial.print  ("Redis connection failed...");
        Serial.println("Waiting for next read");
        return; 
      } else
        Serial.println("OK");
    }
}

Envoi de commandes à Redis

Il est temps d’envoyer réellement des commandes à Redis pour insérer les valeurs au début d’une list en utilisant LPUSH. La clé de la liste est construite par concaténation de la chaîne « v: » et de l’adresse MAC du périphérique.

Ajouter ce code immédiatement après le test de connexion.

    // 10 is the base
    ltoa(lastValue,szValue,10);
    redis.print(
      String("*3\r\n")
      +"$5\r\n"+"LPUSH\r\n"
      +"$19\r\n"+"v:"+WiFi.macAddress().c_str()+"\r\n"
      +"$"+strlen(szValue)+"\r\n"+szValue+"\r\n"
    );

J’ai utilisé une String C++ pour rendre le code plus compact.

Redis envoie une réponse à chaque commande, y compris lorsqu’il se produit une erreur. Ces réponses sont enregistrées dans des tampons du périphérique. Lorsque tous les tampons sont pleins, le périphérique ne peut plus émettre de nouvelle commande jusqu’à ce qu’un des tampons se libère suite à une expiration (chaque seconde). Je pourrais attendre la réponse de manière synchrone, mais ce serait une boucle bloquante. Même si ce serait acceptable car Redis répond habituellement très rapidement, ce serait également une mauvaise pratique. J’ai choisi de tester la présence d’une éventuelle réponse à chaque tour de boucle et de la consommer, juste avant la fin de la fonction loop.

  // If there is an answer from the Redis server available
  // Consume Redis server replies from the buffer and discard them
  while (redis.available() != 0)
    Serial.print((char)redis.read());

Compiler, téléverser et observer le résultat.

Résultats dans la console Arduino Pleine taille

Envoi de notification Push grâce à la commande PUBLISH

En tant qu’exercice, vous pouvez envoyer une autre commande à Redis en utilisant le protocole REdis Serialization Protocol pour notifier toutes les autres applications ayant souscrit (SUBSCRIBE) au canal que votre périphérique vient de mettre la liste à jour. L’idée est d’envoyer PUBLISH chan:<MACAddr> <lastValue>. Grâce à un tel message, les autres applications n’auront même pas à récupérer la mesure depuis la liste puisqu’elle sera transmise à l’intérieur même du message.

Vous serez en mesure d’observer les résultats si vous ouvez une connexion à Redis avec l’outil src/redis-cli -h <IP> -p <PORT> et si vous saisissez SUBSCRIBE chan:<MACAddr>

Résolution de problèmes

Si votre ESP lance une moche trace de crash sur la console série, il est possible de la comprendre en utilisant des Serial.print, mais vous pouvez également installer le greffon ESP Exception decoder dans l’interface.

Si vous voulez regarder ce qui se passe dans votre serveur Redis, vous pouvez le surveiller avec l’outil src/redis-cli -h <IP> -p <PORT> et la commande MONITOR. Commande Monitor Pleine taille

Améliorations

Vous pouvez connecter la LDR à une sortie numérique à la place du 3.3V. Il sera ainsi possible de l’alimenter uniquement au moment de lire les mesures pour économiser de l’énergie.

Une autre solution est d’utiliser une résistance interne de tirage pour retirer celle externe et de connecter la LDR directement entre AD0 et GND. Les valeurs seront inversées mais vous économiserez 0.02€. Ensuite, il sera possible de couper la résistance de tirage interne entre deux lectures pour économiser de l’énergie.

Vous pouvez utiliser les commandes de mise en veille du micro-controlleur pour l’endormir entre deux mesures, pour économiser de l’energie. C’est un peu plus complex, mais peut faire chuter le courant consmmé à quelques µA la plupart du temps !!! Pratique pour les périphériques sur batterie difficile d’accès pour la maintenance.

Si vous souhaitez avoir de la haute-disponibilité, vous pouvez soit implémenter le protocol des sentinelles Redis, soit une partie du protocole du cluster Redis, soit utiliser un proxy tel que celui fourni dans l’édition entreprise.

En ajoutant une encryption SSL entre le périphérique et le serveur, vous pourrez utiliser une authentification par mot de passe (command AUTH) pour améliorer le niveau de sécurité des accès à votre base de données.

Vous pouvez implémenter une bibliothèque pour encoder plus facilement les commandes Redis dans le protocol REdis Serialization Protocol et une seconde pour gérer la connexion avec Redis (expirations, pertes de connexion, pipelining).

Supports et liens

Lien Description
Full source Code source complet isans la notification
06/09/2017: Meetup Redis IOT à Tel Aviv, Israël chez RedisLabs Tel Aviv
10/10/2017: Meetup Redis IOT à Paris, France chez SOAT
17/10/2017: Meetup Redis IOT à Toulouse, France chez Étincelle coworking Toulouse
07/11/2017: Meetup Redis IOT à Lille, France chez Zenika Lille
14/11/2017: Meetup Redis IOT à Bordeaux, France chez Le Wagon Bordeaux
21/11/2017: Meetup Redis IOT à Lyon, France chez La cordée coworking
Redis France Groupe meetup sur Redis en France

Notes de bas de page

l’installeur MSI ou l’application du store. Nous voulons créer une installation portable sans requerir les droits d’administration, avec tous les fichiers dans un seul et unique dossier pouvant être déplacé.

  1. Utilisateurs de Windows : Choisir le fichier ZIP, pas