A la découverte de l'ESP2688 - Partie II

Un LED c'est bien, mais 16 LEDs c'est mieux !

Lors des précédentes expériences, il a été possible de piloter une LED via un explorateur Internet en se connectant à un mini serveur web. Maintenant il serait sympathique de pouvoir piloter une série de LED de type NeoPixel (Led intelligente).

Les LED WS2812

Il fut un temps, lorsque l'on souhaitait faire de jolies effets de couleur, il était nécessaire de faire des mixages d'intensité de Leds rouge, vert, bleu via divers circuits. Pour ma part j’avais réalisé un
système à base du micro-contrôleur PIC et de transistor pour piloter plusieurs LED sur des canaux différents. Cela impliquait de nombreux câblages. Mais ce temps est maintenant révolu grâce au LED intelligentes. En effet les WS2812 sont des LED composés de trois LED correspondant aux couleurs primaire (Rouge, Vert et Bleu) et d'un microcontrôleur pour les pilotés et gérer la couleur, l'intensité.
Ces LED peuvent être ensuite associées en série et nous pouvons programmer chacune de ces LED séparément via un protocole série. Je vous invite à visiter quelques sites pour en savoir plus dessus :
 Pour le test décrit ci-dessous, nous nous sommes procuré un annau de 16 LED WS2812B sur Internet que voici :
 Cette anneau contient donc 16 LED toutes reliées à l’alimentation électrique (5 Vcc) (de manière parallèle) et en série pour le bus de données. Si nous jetons un coup d'oeil de l'autre côté du PCB nous découvrons la connexion principale :
Hormis l’alimentation de 5V qui doit être connectée sur les bornes 5V et GND, nous avons également une borne DI (Data Input) et DO (Data Output). Ces deux bornes correspondent aux connexions nécessaires pour le pilotage des LED. Le DI c'est l'entrée des données séries, et le DO c'est la sortie.
L'idée est que nous pouvons connecter un autre anneau en série à celui-ci si nous le souhaitons; pour ce faire nous relirons l'alimentation (en parallèle, cad le 5V au 5V de l’anneau suivant, idem pour la borne GND), par contre nous connecterons le DO du premier anneau au DI de l'anneau suivant (c'est une connexion en série).
Dans notre démo, nous utiliserons qu'un seul anneau, donc nous n'utiliserons pas le DO.

Pilotons tous ça avec un ESP8266-12E

Depuis le début de l'histoire, c'est maintenant l'ESP8266 qui va entrée en jeu. L'idée est simple :
  • Pouvoir piloter l'anneau de LED via un explorateur internet. Par exemple je souhaiterais pouvoir écrire une URL du style : http://<ip>/gpio/bleu pour que l'anneau s'illumine de bleu
  • Et le tous via une connexion Wifi bien sûr :-) !
 Un point important à prendre en compte est la gestion de l’alimentation. En effet les LED sa consomme pas mal de courant. Donc il ne sera pas possible d’alimenter l'anneau avec le port USB de votre ordinateur. Il est donc nécessaire de faire appel à une alimentation séparée. Le plus simple c'est d'allimenter l'anneau avec une alim de 5V indépendante et de laisser le PC alimenter l'ESP. Assurez vous que les masses (0V ou GND) des deux alimentations soit connectées (mais pas le +5V).

Il est possible également de relier le +5V de l’alimentation externe sur le Vin du module ESP2866. Mais attention à bien veiller de déconnecter l'USB, je ne suis pas certain de l'isolation des alimentations (à vérifier).

Nous utiliserons le port D4 du module pour envoyer les données à l'anneau de LED. Nous utilisons le port D4 (GPIO2) car c'est également le port utilisé par le contrôleur série UART1 de l'ESP. En effet chaque port du module peut être utilisé de différentes manières. Il faut savoir que la bibliothèque de code NeoPixelBus que nous utiliserons par la suite communiquera avec le Bus de LED via l'UART1 de l'ESP8266. Je vous invite à aller vérifier la documentation de cette bibliothèque.

Le logiciel

L'idée étant de faire simple, je vais réutiliser le principe qui a été déjà utilisé pour le pilotage d'une simple LED (voir par ici).

/*
 *  Ce petit programme démontre la réalisation d'un tout petit serveur
 *  HTTP, pour interagir avec le module ESP via le Wifi.
 *  Dans cette exemple nous utilisons un réseau Wifi existant, il faudra
 *  donc fournir les paramètres ssid & password en fonction de votre
 *  situation.
 * 
 *  Afin de pouvoir piloter l'anneau de led nous allons définir
 *  quelque URI de commande :
 * 
 *  Exemple : http://<IP>/gpio/bleu => Met une couleur bleu à toutes les LED
 * 
 *  Les informations concernant l'état du module sont envoyées sur la
 *  ligne série (115200 bauds)
 * 
 *  Le clignotement lent de la LED indique une tentative de connexion
 *  à votre réseau Wifi
 *  Le clignotement rapide indique que la connexion est établie (le
 *  clignotement s'arrête en suite)
 * 
 *  La page Web renvoyée à l'utilisateur est simple, mais elle utilise
 *  un CSS qui est stocké sur http://infoaleze.chez.com/IoT afin
 *  d'améliorer le visuel
 */

#include <ESP8266WiFi.h>

/*
 *  Cette librairie doit être télechargée et installée dans
 *  le répertoire des librairies d'Arduino.
 *  /!\ Attention /!\ à bien prendre la librairie pour ESP8266
 *  et notament la branch "UartDriven" :
 *  https://github.com/Makuna/NeoPixelBus/tree/UartDriven
 */
#include <NeoPixelBus.h>



const char* ssid = "TheSSID";
const char* password = "ThePassword";

const int NB_PIXEL = 16; // Nombre de LED dans l'anneau
const int colorSaturation  = 192; // Saturation de la couleur


// Création d'un server sur le port 80 (généralement utilisé par le WEB)
WiFiServer server(80);

// Initialisation du pilote NeoPixel
NeoPixelBus strip = NeoPixelBus(NB_PIXEL, 4, NEO_GRB);

// Création de quelques couleurs
RgbColor rgbRED   = RgbColor(colorSaturation, 0, 0);
RgbColor rgbGREEN = RgbColor(0, colorSaturation, 0);
RgbColor rgbBLUE  = RgbColor(0, 0, colorSaturation);
RgbColor rgbOFF   = RgbColor(0, 0, 0);



/* *********************************************************************** *
 *  Setup : initialisation des services, mode des entrées/sorties etc...   *
 * *********************************************************************** */
void setup() {

  int Led0 = LOW;
 
  /* Ouverture du port USB pour le débugage */
  Serial.begin(115200);
  delay(10);

  /* Dans cette exemple nous utiliserons
    - le port D0 soit GPIO16 pour la led intégrée bleu
    - le port D1 soit GPIO5  pour la led (en mode OUTPUT)   
 
  */
  pinMode(16, OUTPUT);
  pinMode(5, OUTPUT);
 
  digitalWrite(5, LOW);
  digitalWrite(16, HIGH);
 

  Serial.println();
  Serial.println();
  Serial.println("***************************************");
  Serial.println("** Petit programme de Test Wifi      **");
  Serial.println("** Permet de piloter un anneau de 16 **");
  Serial.println("** Leds (WS2812)                     **");
  Serial.println("** Ex: http://<IP>/gpio/bleu         **");
  Serial.println("**-----------------------------------**");
  Serial.println("** Yoann Darche  | 27/10/2015 | 1.00 **");
  Serial.println("***************************************");
  Serial.println();


  // Initialisation du Bus de Pixel
  Serial.println("[i] Initialisation du Bus de Pixel");
  strip.Begin();
  strip.Show();

   
  // initialisation de la connexion au réseau Wifi 
  Serial.print("[i] Tentative de connexion a ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);

  // On attend que la connexion soit opérationelle
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if(Led0 == LOW) { Led0 = HIGH; } else { Led0 = LOW; }
    digitalWrite(16, Led0);
  }
  Serial.println("");
  Serial.println("[+] WiFi connecte");
 
  // Démarre le serveur
  server.begin();
  Serial.println("[+] Serveur demarree");

  /* Les lignes suivantes réalisent une petite anim
   *  sur la LED intégrée afin d'avertir que le
   *  module est prêt et connecté
   */
  digitalWrite(16, LOW);
  delay(392);
  digitalWrite(16, HIGH);
  delay(392);
  for(int i=0; i<10; i++) {
    digitalWrite(16, LOW);
    delay(125);
    digitalWrite(16, HIGH);
    delay(125);
  }
 
  // Affiche dans la console de débugage l'adresse IP
  // utilisée
  Serial.print("L'adresse IP utilise est :");
  Serial.println(WiFi.localIP());
}

/* *********************************************************************** *
 *  Loop : Programme principal                                             *
 * *********************************************************************** */
void loop() {
  // Vérifie si un client est connécté au module
  WiFiClient client = server.available();
  if (!client) {
    return;
  }
 
  // Attend que le client (browser) envoie des données
  // dans notre cas la requête HTTP
  Serial.println("[i] Nouveau client");
  while(!client.available()){
    delay(1);
  }
 
  // Lecture de la première ligne envoyé par le client
  // dans notre cas GET <url>
  String req = client.readStringUntil('\r');
  Serial.println(req);
  // On vide le reste des données reçus (économie de mémoire)
  client.flush();
 
   // Décodage de la requête reçue
  int val;
  if (req.indexOf("/gpio/0") != -1) {
    val = 0;
    // Désactivation du GPIO5 soit D1
    digitalWrite(5, val);
  }
  else if (req.indexOf("/gpio/1") != -1) {
    val = 1;
    // Activation du GPIO5 soit D1
    digitalWrite(5, val);
  }
  else if (req.indexOf("/gpio/rouge") != -1) {
    setColor(rgbRED);
  }
  else if (req.indexOf("/gpio/vert") != -1) {
    setColor(rgbGREEN);
  }
  else if (req.indexOf("/gpio/bleu") != -1) {
    setColor(rgbBLUE);
  }
  else if (req.indexOf("/gpio/off") != -1) {
    setColor(rgbOFF);
  }

  else {
    Serial.println("[-] Requete invalide");
  }
 
  client.flush();

  // Entête de Réponse  
  String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n";
  // Ajout de la référence à une stylesheet pour faire des beaux boutons sans charger la mémoire
  s+= "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"http://infoaleze.chez.com/IoT/IoT.css\" media=\"all\"/></head><body>";
  // Ecriture de l'état du port
  s+="<h1>D1(GPIO5) est maintenant ";
  s += (val)?"On":"Off";
  s += "</h1>\n";
  // Ajout de bouton de commutation
  s += "<a href=\"../gpio/1\" class=\"bouton\">On</a><a href=\"../gpio/0\" class=\"bouton\">Off</a><br/>";

  // Ajout des contrôle pour l'anneua
  s+="<h1>Controle de l'anneau de LED</h1> ";
  s += "<a href=\"../gpio/off\" class=\"bouton\">OFF</a><a href=\"../gpio/bleu\" class=\"bouton\">Bleu</a><a href=\"../gpio/vert\" class=\"bouton\">Vert</a><a href=\"../gpio/rouge\" class=\"bouton\">Rouge</a><br/>";
 
  s += "</body></html>";
 
  // Send the response to the client
  client.print(s);
  delay(1);
  Serial.println("[i] Client deconecte");

  // Quand nous sortons de la fonction, la variable objet client sera automatiquement détruite
  // et donc la liaison avec le client coupée
}


/***************************************************************
 * Fonction qui met à jour toutes les LED.                     *
 ***************************************************************/
void setColor(RgbColor color) {
  Serial.println("[i] Envoie des donnees au LED");
  int i;
  for (i=0; i < NB_PIXEL; i++) {
    strip.SetPixelColor(i, color);
  }
  strip.Show();
}


Et la vidéo de démo :-)

4 commentaires:

  1. Salut,

    Bravo pour le tuto, mais j'aimerai juste soulever quelques points pour la sécurité des LED (c'est salement fragile ces petites bêtes).

    Au niveau du schéma du montage, il manque deux choses pour protéger les LED de l'anneau : un gros condensateur (1000 µF) aux bornes + et - de l'anneau (un pic de courant peut griller les leds, le condensateur va amortir), ainsi qu'une résistance de 300 à 500 ohms sur ligne de pilotage (idem, pour éviter de cramer la première LED).

    Autre ajoute conseillé à l'article : préciser qu'il faut d'abord alimenter les LED avant d'alimenter le microcontrolleur : un signal qui arrive sur la ligne de données alors que les LED ne sont pas alimentés est potentiellement destructeur.

    Et pour finir, de mémoire l'ESP8266 à des E/S en 3,3V. Hors les WS2812, si alimentés en 5V, s'attendent à un signal avec un niveau haut au plus bas à 3,7V. Donc dans ton cas ça fonctionne, mais ça tient plus du coup de bol :) Solution : ou bien alimenter les LED en 3,3V, ou bien utiliser un "level-switcher" entre l'ESP en 3,3V et les LED en 5V sur la ligne de data.

    Voilà, c'est tout :)

    RépondreSupprimer
    Réponses
    1. Merci, c'est vrai que j'ai été un peu vite sur la conception... Pour la capacité de lissage, je vais mettre ça assez rapidement en place, de même pour la résistance entre l'ESP et la première LED.
      Pour le Level shifter, en effet l'entrée est considérée comme à l'état haut à partir du moment où l'on applique une tension supérieur à 0.7 x Vdd (soit 0,7 * 5 = 3.5V), avec un hystérésis à 0.35*Vdd (soit 2.45V), c'est probablement grâce à ça que cela fonctionne. Bref je vais ajouter un petit transistor pour améliorer... Nouveau schéma à suivre...

      Supprimer
    2. Oups petite coquille, l’hystérésis est de 0.35V et pas de 0.35 x Vdd. J'ai trouvé un Level Shifter simple à base de 2 diodes et une résistance :
      Schéma > https://www.faludi.com/bwsn_book/images/5V-3.3V_level_shift_circuit_for_TX-RX.png (page web: http://jamesreubenknowles.com/level-shifting-stragety-experments-1741)

      Supprimer
  2. Pour les levels shifter, y'a aussi les montages à base de MOSFET qui sont simples à mettre en place : http://husstechlabs.com/support/tutorials/bi-directional-level-shifter/

    RépondreSupprimer