Loading icon
Sketch ESP8266 – O1S

Ce petit module Wifi est très pratique lorsqu’on sait comment l’utiliser. Aujourd’hui on va voir comment y envoyer un Sketch et faire joujou avec un LED 🙂

Prérequis

Le projet

Ce qu’on va faire est assez simple : contrôler une LED via le Wifi grâce à un navigateur web. Je ne souhaite pas montrer comment faire avec l’application « blynk », déjà car beaucoup de tuto existent et je trouve que cela limite pas mal les choses. Personnellement je préfère tout contrôler et faire ce que j’ai envie 🙂

Grâce aux 2 PINS GPIO 0 et 2 on peut contrôler des choses lorsqu’on va envoyer ou retirer du courant. Par exemple : allumer une LED ou activer un relais.

Le branchement va ressembler à ceci :

  • TX et RX vont être utilisés juste pour téléverser le code sur l’ESP, une fois fini c’est plus nécessaire
  • Le PIN RST de l’ESP n’est pas branché
  • Le PIN GPIO2 de l’ESP n’est pas branché (car nous allons utiliser simplement le GPIO0)

Ne pas oublier, pour téléverser votre code dans l’ESP8266, il faut le mettre en mode écriture. Donc assurez vous de valider correctement les prérequis 🙂

Conseil : Avant d’aller plus loin, on s’assure que l’ESP est bien branché et que le téléversement fonctionne. Un Sketch vierge avec uniquement les 2 boucles est amplement suffisant 🙂 Et on vérifie que l’IDE arduino est bien configuré pour l’ESP (tous les liens sont en début d’article).

Place au code

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

// Le pin que l'on va utiliser (sur un ESP 01S) il y a 2 pins : 0 et 2
int pinGPIO0 = 0;

// Permet de lancer le serveur sur le port 80
ESP8266WebServer server(80);

/*
 * Configuration de votre accès WIFI :
 * SSID + Mot de passe
 */
char* ssid = "Captain-arduino"; // à remplacer par vos infos
char* password = "42"; // à remplacer par vos infos

/**
 * J’initialise une variable pour la suite :)
 */
String result = "";

void setup()
{
  /**
   * On tente de se connecter au wifi
   */
  WiFi.begin(ssid, password);
  Serial.begin(9600);
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  // Ici on est connecté au wifi
  // Et on affiche le tout dans le moniteur série : ainsi que l'adresse IP locale de votre ESP
  // Une adresse IP locale c'est par exemple ceci : 192.168.1.42 (pour vous ça peut être 192.168.1.10, 192.168.1.9, 192.168.1.78, etc.)
  Serial.println("");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  pinMode(pinGPIO0, OUTPUT); // le pinMode output car on va envoyer une donnée
  digitalWrite(pinGPIO0, LOW); // par défaut on éteint la led

/**
 * Configuration des routes
 */
  server.on("/", HTTP_GET, viewIndex); // return un text/plain : du simple texte
  server.on("/web", HTTP_GET, viewWeb); // return un entête html et une page entière html 
  server.on("/led", HTTP_GET, viewLed); // return du json 
  server.on("/led", HTTP_POST, updateLed); // return un text/plain : du texte là aussi (le mieux serait de retourner du json)
  
  server.onNotFound(viewNotFound); // Il faut penser aux urls qui n'existent pas (les 404)

  // Lancement du serveur
  server.begin();
}

void loop()
{
  /**
     ceci permet d'écouter les requêtes HTTP afin de charger ce qu'on a configuré dans le setup
  */
  server.handleClient();
}

// Le classique Hello World :)
void viewIndex() {
  server.send(200, "text/plain", "Hello world!");
}

/*
 * La page web : c'est affreux on est bien d'accord 
 * On l'obtient lorsqu'on ira sur cette route (avec une methode GET) : [votre_IP]/web par exemple : http://192.168.1.42/web
 */
void viewWeb() {
  server.send(200, "text/html", "<!DOCTYPE html><html lang='fr'><head><meta charset='UTF-8'><title>Captain Arduino</title><style>body{background: #000;color:#fff;text-align: center;}button{border: 0; padding: 5px 20px; text-transform: uppercase; cursor: pointer;}.show-led:hover, .show-led.current{background: green;color: #fff;}.hide-led:hover, .hide-led.current{background: red;color: #fff;}</style></head><body><h1>L'ESP8266 - 01s avec Captain Arduino</h1><div class='wrapper'><div class=\"content\"><button class='show-led' onclick=\"configLed(1, `show-led`)\"></button><button class='hide-led' onclick=\"configLed(0, `hide-led`)\"></button><div class='error'></div></div></div><script>var options={method: 'GET', headers:{'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'},}; fetch('http://192.168.1.42/led', options).then((resp)=> resp.json()) .then(data=>{if (data.currentLed){document.getElementsByClassName('show-led')[0].classList.add('current');document.getElementsByClassName('hide-led')[0].classList.remove('current');document.getElementsByClassName('show-led')[0].innerHTML='La led est allumée';document.getElementsByClassName('hide-led')[0].innerHTML='éteindre la led';}else{document.getElementsByClassName('hide-led')[0].classList.add('current');document.getElementsByClassName('show-led')[0].classList.remove('current');document.getElementsByClassName('hide-led')[0].innerHTML='La led est éteinte';document.getElementsByClassName('show-led')[0].innerHTML='allumer la led';}});function configLed(led, classNameLed){var data='show=' + led;var options={method: 'POST', headers:{'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, body: data,}; [].forEach.call(document.getElementsByTagName('button'), function(el){el.classList.remove('current');});fetch('http://192.168.1.42/led', options) .then(res=>{if (res.ok){document.getElementsByClassName(classNameLed)[0].classList.add('current');if(led){document.getElementsByClassName('show-led')[0].classList.add('current');document.getElementsByClassName('hide-led')[0].classList.remove('current');document.getElementsByClassName('show-led')[0].innerHTML='La led est allumée';document.getElementsByClassName('hide-led')[0].innerHTML='éteindre la led';}else{document.getElementsByClassName('hide-led')[0].classList.add('current');document.getElementsByClassName('show-led')[0].classList.remove('current');document.getElementsByClassName('hide-led')[0].innerHTML='La led est éteinte';document.getElementsByClassName('show-led')[0].innerHTML='allumer la led';}}else{document.getElementsByClassName('error').innerHTML='Une erreur survenue. Merci de réessayer';}}) .catch(error=>{document.getElementsByClassName('error').innerHTML='Une erreur survenue ('+error+'). Merci de réessayer';});}</script></body></html>");
}

/**
 * Fonction permettant de savoir si la led est allumé ou éteinte
 * Dans le HTML de la fonction viewWeb() j'ai mis en place un appel fetch (javascript) afin d'appeler cette route (avec une methode GET) : [votre_IP]/led par exemple : http://192.168.1.42/led
 * Cette fonction retourne un format json pour dire 0 ou 1 si la led est éteinte ou allumé
 */
void viewLed() {
  int currentLed = 0;
  /**
   * On récupère l'info du PIN : HIGH ou LOW donc 1 ou 0
   * Si on est HIGH => cela signifie que la LED est allumé, donc la variable currentLed passe à 1
   * Sinon on fait rien car la variable currentLed est déjà à 0
   */
  if (digitalRead(pinGPIO0)) {
    currentLed = 1;
  }

/**
 * Configuration de la variable JSON pour le return
 */
  String messageResponse = "\"currentLed\":\n";
  messageResponse += currentLed;

/**
 * Access-Control-Allow-Origin est très IMPORTANT ! 
 * Cela renvoie une réponse indiquant si les ressources peuvent être partagées avec une origine donnée
 * Dans notre cas, je lui dit : tu partages avec tout le monde
 */
  server.sendHeader("Access-Control-Allow-Origin", "*");
  server.send(200, "application/json", "{" + messageResponse + "}");
}

/**
 * Mise à jour de la led
 */
void updateLed() {
  // hasArg
  if (server.arg("show") == "1") {
    digitalWrite(pinGPIO0, HIGH);
    result = "show test";
  } else {
    digitalWrite(pinGPIO0, LOW);
    result = "hide test";
  }
  server.sendHeader("Access-Control-Allow-Origin", "*");
  server.send(200, "text/plain", result);
}

/**
 * Gestion des 404 : un simple texte
 */
void viewNotFound() {
  server.send(404, "text/plain", "404: Not found");
}

Il y a beaucoup de choses 🙂

Les routes ?

server.on("/", HTTP_GET, viewIndex);
server.on("/web", HTTP_GET, viewWeb);
server.on("/led", HTTP_GET, viewLed);
server.on("/led", HTTP_POST, updateLed);
  • Le premier paramètre correspond au chemin d’accès (le path)
  • Le deuxième paramètre correspond à la méthode d’accès :
    • HTTP_GET : pour du GET en gros : accéder à l’url directement (via un navigateur par exemple)
    • HTTP_POST : pour du POST : ici c’est par exemple via un formulaire, on souhaite envoyer une donnée (ou plusieurs)
  • Le dernier paramètre, c’est la fonction que l’on va utiliser lorsqu’on utilise tel ou tel route

Pourquoi avoir 2 routes identique mais avec une méthode différente ?

  • Cela permet simplement de mieux organiser son code et de pouvoir faire appel à 2 fonctions différentes en fonction du contexte.

Dans notre cas :

  • HTTP_GET /led va nous dire si la LED est allumée ou éteinte
  • HTTP_POST /led va nous permettre de modifier l’état de notre LED : l’allumer ou l’éteindre

SERVER.SEND

server.send(200, "text/plain", "Hello world!");
server.send(200, "application/json", "{" + messageResponse + "}");
server.send(200, "text/plain", result);
server.send(200, "text/html","code_HTML");
server.send(404, "text/plain", "404: Not found");
  • Le premier paramètre correspond au code HTTP
  • Le deuxième paramètre c’est « le content-type » de la page : pour simplifier c’est tout simplement sous quelle forme on souhaite renvoyer les données ? du texte ? du Json ? du HTML ?
  • Le dernier paramètre c’est le code renvoyé : une chaine de caractères ? une variable ? etc.

Dans le code je renvoie 3 content-type différent uniquement pour vous montrer comment faire. En utilisation réelle, personnellement j’utilise que le JSON.

Server.sendHeader

server.sendHeader("Access-Control-Allow-Origin", "*");

Chose étrange ? Erreur ? non non c’est bien utile ! Le paramètre : « Access-Control-Allow-Origin » permet d’autoriser l’accès à d’autres sites.

Ok mais ça veut dire quoi ?

Pour faire simple, sans cette ligne, si on souhaite utiliser notre ESP8266 dans un site Web, on serait bloqué. Car le site A n’aurait pas le droit de récupérer le contenu de notre ESP8266 et le navigateur renverrait une erreur. Pour plus d’informations : https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Access-Control-Allow-Origin

Le rendu HTML ?

server.send(200, "text/html", "<!DOCTYPE html><html lang='fr'><head><meta charset='UTF-8'><title>Captain Arduino</title><style>body{background: #000;color:#fff;text-align: center;}button{border: 0; padding: 5px 20px; text-transform: uppercase; cursor: pointer;}.show-led:hover, .show-led.current{background: green;color: #fff;}.hide-led:hover, .hide-led.current{background: red;color: #fff;}</style></head><body><h1>L'ESP8266 - 01s avec Captain Arduino</h1><div class='wrapper'><div class=\"content\"><button class='show-led' onclick=\"configLed(1, `show-led`)\"></button><button class='hide-led' onclick=\"configLed(0, `hide-led`)\"></button><div class='error'></div></div></div><script>var options={method: 'GET', headers:{'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'},}; fetch('http://192.168.1.42/led', options).then((resp)=> resp.json()) .then(data=>{if (data.currentLed){document.getElementsByClassName('show-led')[0].classList.add('current');document.getElementsByClassName('hide-led')[0].classList.remove('current');document.getElementsByClassName('show-led')[0].innerHTML='La led est allumée';document.getElementsByClassName('hide-led')[0].innerHTML='éteindre la led';}else{document.getElementsByClassName('hide-led')[0].classList.add('current');document.getElementsByClassName('show-led')[0].classList.remove('current');document.getElementsByClassName('hide-led')[0].innerHTML='La led est éteinte';document.getElementsByClassName('show-led')[0].innerHTML='allumer la led';}});function configLed(led, classNameLed){var data='show=' + led;var options={method: 'POST', headers:{'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'}, body: data,}; [].forEach.call(document.getElementsByTagName('button'), function(el){el.classList.remove('current');});fetch('http://192.168.1.42/led', options) .then(res=>{if (res.ok){document.getElementsByClassName(classNameLed)[0].classList.add('current');if(led){document.getElementsByClassName('show-led')[0].classList.add('current');document.getElementsByClassName('hide-led')[0].classList.remove('current');document.getElementsByClassName('show-led')[0].innerHTML='La led est allumée';document.getElementsByClassName('hide-led')[0].innerHTML='éteindre la led';}else{document.getElementsByClassName('hide-led')[0].classList.add('current');document.getElementsByClassName('show-led')[0].classList.remove('current');document.getElementsByClassName('hide-led')[0].innerHTML='La led est éteinte';document.getElementsByClassName('show-led')[0].innerHTML='allumer la led';}}else{document.getElementsByClassName('error').innerHTML='Une erreur survenue. Merci de réessayer';}}) .catch(error=>{document.getElementsByClassName('error').innerHTML='Une erreur survenue ('+error+'). Merci de réessayer';});}</script></body></html>");

On est bien d’accord, cette ligne est affreuse ! Pas de panique, en fin d’article je donnerais la page .html toute belle comme si on l’utiliserait pour un site.

Grossomodo, cette ligne affreuse est tout simplement un code HTML que j’ai mis en place et qui ressemble à ceci :

Le code : 80% d’HTML, 14% de JavaScript et 1% de CSS.

Je ne vais pas rentrer dans de grandes explications sur les languages HTML, JavaScript et CSS mais pour faire très simple ce sont des languages pour réaliser des sites Internet. J’utilise du JavaScript natif afin que tout soit utilisable directement et facilement (du coup pas de librairie supplémentaire comme par exemple jQuery).

En situation réelle, cela donne ceci :

  • au début je clique sur un bouton et ensuite j’actualise le navigateur afin de montrer qu’au chargement de la page on se retrouve bien sur le bon état de notre LED : allumé ou non
  • ensuite je clique sur un bouton et puis l’autre et on voit bien la LED s’allumer ou s’éteindre

Cest rapide 🙂 c’est normal car là l’ESP n’est pas en veille. Lorsqu’il se met en veille, il faut attendre 3-4 secondes et ensuite il est de-nouveau réactif.

Téléchargement du Sketch ESP8266 Téléchargement de la page web ESP8266 (format html)

Si cet article vous a plu, n’hésitez pas à laisser un commentaire, noter et partager l’article.

Pour utiliser du HTML avec l’ESP il y a plusieurs méthodes que l’on peut trouver facilement sur Internet. Mais nous on verra dans un autre article autre chose de bien plus fun et maintenable.

L’idée : utiliser l’ESP comme une API (Application Programming Interface). L’ESP aura plusieurs routes et chaque route renverra du JSON.

L’avantage ?

  • Tout le code de la page web ne sera plus sur l’ESP
  • Vous pourrez faire votre propre application domotique en fonction de vos besoins
  • Vous ajoutez un capteur de température à votre système domotique ? Tout pourra être centralisé au même endroit
  • La page web n’est plus sur l’ESP donc elle peut être sur votre ordinateur (bon là c’est moyen car si le PC est éteint ça ne fonctionne plus), sur une raspberry, ou encore sur un NASS
  • Le design ne vous plait plus ? Vous pouvez le changer facilement sans toucher à votre ESP

On peut par exemple obtenir ceci :

Ne manquez pas le prochain article … 🙂

0
J'aimerais avoir votre avis, merci de commenter.x