Serveur WebSocket ESP8266 NodeMCU : sorties de contrôle (IDE Arduino)

Serveur WebSocket ESP8266 NodeMCU : sorties de contrôle (IDE Arduino)

Dans ce didacticiel, vous apprendrez à créer un serveur Web avec l’ESP8266 à l’aide du protocole de communication WebSocket. À titre d’exemple, nous allons vous montrer comment créer une page Web pour contrôler les sorties ESP8266 à distance. L’état de la sortie est affiché sur la page Web et se met à jour automatiquement dans tous les clients.

Sorties de contrôle du serveur ESP8266 NodeMCU WebSocket IDE Arduino

L’ESP8266 sera programmé à l’aide de l’IDE Arduino et de l’ESPAsyncWebServer. Nous avons également un guide WebSocket similaire pour l’ESP32.

Si vous avez suivi certains de nos précédents projets de serveur Web comme celui-ci, vous avez peut-être remarqué que si vous avez plusieurs onglets (dans le même ou sur différents appareils) ouverts en même temps, l’état ne se met pas à jour dans tous onglets automatiquement, sauf si vous actualisez la page Web. Pour résoudre ce problème, nous pouvons utiliser le protocole WebSocket – tous les clients peuvent être avertis lorsqu’un changement se produit et mettre à jour la page Web en conséquence.

Ce tutoriel était basé sur un projet créé et documenté par un de nos lecteurs (Stéphane Calderoni). Vous pouvez lire son excellent tutoriel ici.

Présentation de WebSocket

Un WebSocket est une connexion persistante entre un client et un serveur qui permet une communication bidirectionnelle entre les deux parties à l’aide d’une connexion TCP. Cela signifie que vous pouvez envoyer des données du client au serveur et du serveur au client à tout moment.

ESP32 ESP8266 Serveur WebSocket Comment ça marche

Le client établit une connexion WebSocket avec le serveur via un processus appelé poignée de main WebSocket. La poignée de main commence par une requête/réponse HTTP, permettant aux serveurs de gérer les connexions HTTP ainsi que les connexions WebSocket sur le même port. Une fois la connexion établie, le client et le serveur peuvent envoyer des données WebSocket en mode full duplex.

Grâce au protocole WebSockets, le serveur (carte ESP8266) peut envoyer des informations au client ou à tous les clients sans qu’il soit sollicité. Cela nous permet également d’envoyer des informations au navigateur Web lorsqu’un changement se produit.

Ce changement peut être quelque chose qui s’est produit sur la page Web (vous avez cliqué sur un bouton) ou quelque chose qui s’est produit du côté ESP8266, comme appuyer sur un bouton physique sur un circuit.

Aperçu du projet

Voici la page Web que nous allons créer pour ce projet.

Vue d'ensemble du projet ESP32 WebSocket Server Toggle Outputs
  • Le serveur Web ESP8266 affiche une page Web avec un bouton pour basculer l’état du GPIO 2;
  • Pour plus de simplicité, nous contrôlons GPIO 2 – la LED intégrée. Vous pouvez utiliser cet exemple pour contrôler n’importe quel autre GPIO ;
  • L’interface affiche l’état GPIO actuel. Chaque fois qu’un changement se produit sur l’état du GPIO, l’interface est mise à jour instantanément ;
  • L’état GPIO est mis à jour automatiquement dans tous les clients. Cela signifie que si vous avez plusieurs onglets de navigateur Web ouverts sur le même appareil ou sur différents appareils, ils sont tous mis à jour en même temps.

Comment ça fonctionne?

L’image suivante décrit ce qui se passe lorsque vous cliquez sur le bouton « Basculer ».

Mise à jour du serveur Web ESP8266 NodeMCU WebSocket Tous les clients Comment ça marche

Voici ce qui se passe lorsque vous cliquez sur le bouton « Basculer »:

  1. Cliquez sur le bouton « Basculer » ;
  2. Le client (votre navigateur) envoie des données via le protocole WebSocket avec le message « toggle » ;
  3. L’ESP8266 (serveur) reçoit ce message, il sait donc qu’il doit basculer l’état de la LED. Si la LED était auparavant éteinte, allumez-la ;
  4. Ensuite, il envoie des données avec le nouvel état de la LED à tous les clients également via le protocole WebSocket ;
  5. Les clients reçoivent le message et mettent à jour l’état des voyants sur la page Web en conséquence. Cela nous permet de mettre à jour tous les clients presque instantanément lorsqu’un changement se produit.

Préparation de l’IDE Arduino

Nous programmerons le ESP8266 carte à l’aide de l’IDE Arduino, assurez-vous donc de l’avoir installé dans votre IDE Arduino.

Installation de bibliothèques – Serveur Web asynchrone

Pour construire le serveur Web, nous utiliserons le ESPAsyncWebServer bibliothèque. Cette bibliothèque a besoin de ESPAsyncTCP bibliothèque pour fonctionner correctement. Cliquez sur les liens ci-dessous pour télécharger les bibliothèques.

Ces bibliothèques ne peuvent pas être installées via le gestionnaire de bibliothèque Arduino, vous devez donc copier les fichiers de bibliothèque dans le dossier Bibliothèques d’installation Arduino. Alternativement, dans votre IDE Arduino, vous pouvez aller à Sketch> Inclure la bibliothèque> Ajouter une bibliothèque .zip et sélectionner les bibliothèques que vous venez de télécharger.

Code pour le serveur ESP8266 NodeMCU WebSocket

Copiez le code suivant dans votre IDE Arduino.

/*********
  Rui Santos
  Complete project details at https://Raspberryme.com/esp8266-nodemcu-websocket-server-arduino/
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

bool ledState = 0;
const int ledPin = 2;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
  html {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  h2{
    font-size: 1.5rem;
    font-weight: bold;
    color: #143642;
  }
  .topnav {
    overflow: hidden;
    background-color: #143642;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
    max-width: 600px;
    margin: 0 auto;
  }
  .card {
    background-color: #F8F7F9;;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding-top:10px;
    padding-bottom:20px;
  }
  .button {
    padding: 15px 50px;
    font-size: 24px;
    text-align: center;
    outline: none;
    color: #fff;
    background-color: #0f8b8d;
    border: none;
    border-radius: 5px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
   }
   /*.button:hover {background-color: #0f8b8d}*/
   .button:active {
     background-color: #0f8b8d;
     box-shadow: 2 2px #CDCDCD;
     transform: translateY(2px);
   }
   .state {
     font-size: 1.5rem;
     color:#8c8c8c;
     font-weight: bold;
   }
  </style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Output - GPIO 2</h2>
      <p class="state">state: <span id="state">%STATE%</span></p>
      <p><button id="button" class="button">Toggle</button></p>
    </div>
  </div>
<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  window.addEventListener('load', onLoad);
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }
  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var state;
    if (event.data == "1"){
      state = "ON";
    }
    else{
      state = "OFF";
    }
    document.getElementById('state').innerHTML = state;
  }
  function onLoad(event) {
    initWebSocket();
    initButton();
  }
  function initButton() {
    document.getElementById('button').addEventListener('click', toggle);
  }
  function toggle(){
    websocket.send('toggle');
  }
</script>
</body>
</html>
)rawliteral";

void notifyClients() {
  ws.textAll(String(ledState));
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
      notifyClients();
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
             void *arg, uint8_t *data, size_t len) {
    switch (type) {
      case WS_EVT_CONNECT:
        Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
        break;
      case WS_EVT_DISCONNECT:
        Serial.printf("WebSocket client #%u disconnected\n", client->id());
        break;
      case WS_EVT_DATA:
        handleWebSocketMessage(arg, data, len);
        break;
      case WS_EVT_PONG:
      case WS_EVT_ERROR:
        break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if (ledState){
      return "ON";
    }
    else{
      return "OFF";
    }
  }
  return String();
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Start server
  server.begin();
}

void loop() {
  ws.cleanupClients();
  digitalWrite(ledPin, ledState);
}

Afficher le code brut

Insérez vos informations d’identification réseau dans les variables suivantes et le code fonctionnera immédiatement.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Comment fonctionne le code

Continuez à lire pour savoir comment le code fonctionne ou passez à la Manifestation section.

Importation de bibliothèques

Importez les bibliothèques nécessaires pour créer le serveur Web.

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>

Informations d’identification réseau

Insérez vos identifiants réseau dans les variables suivantes :

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Sortie GPIO

Créez une variable appelée ledState pour contenir l’état du GPIO et une variable appelée ledPin qui fait référence au GPIO que vous souhaitez contrôler. Dans ce cas, nous contrôlerons la LED intégrée (qui est connectée au GPIO 2).

bool ledState = 0;
const int ledPin = 2;

AsyncWebServer et AsyncWebSocket

Créez un objet AsyncWebServer sur le port 80.

AsyncWebServer server(80);

La bibliothèque ESPAsyncWebServer inclut un plugin WebSocket qui facilite la gestion des connexions WebSocket. Créez un objet AsyncWebSocket appelé ws pour gérer les connexions sur le chemin /ws.

AsyncWebSocket ws("/ws");

Construire la page Web

La variable index_html contient le code HTML, CSS et JavaScript nécessaires pour créer et styliser la page Web et gérer les interactions client-serveur à l’aide du protocole WebSocket.

Remarque : nous plaçons tout ce qui est nécessaire pour construire la page Web sur la variable index_html que nous utilisons sur l’esquisse Arduino. Notez qu’il peut être plus pratique d’avoir des fichiers HTML, CSS et JavaScript séparés que vous téléchargez ensuite sur le système de fichiers ESP8266 et que vous les référencez dans le code.

Lecture recommandée : Serveur Web ESP8266 utilisant SPIFFS (SPI Flash File System)

Voici le contenu de la variable index_html :

<!DOCTYPE HTML>
<html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
  html {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  h2{
    font-size: 1.5rem;
    font-weight: bold;
    color: #143642;
  }
  .topnav {
    overflow: hidden;
    background-color: #143642;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
    max-width: 600px;
    margin: 0 auto;
  }
  .card {
    background-color: #F8F7F9;;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding-top:10px;
    padding-bottom:20px;
  }
  .button {
    padding: 15px 50px;
    font-size: 24px;
    text-align: center;
    outline: none;
    color: #fff;
    background-color: #0f8b8d;
    border: none;
    border-radius: 5px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
   }
   .button:active {
     background-color: #0f8b8d;
     box-shadow: 2 2px #CDCDCD;
     transform: translateY(2px);
   }
   .state {
     font-size: 1.5rem;
     color:#8c8c8c;
     font-weight: bold;
   }
  </style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Output - GPIO 2</h2>
      <p class="state">state: <span id="state">%STATE%</span></p>
      <p><button id="button" class="button">Toggle</button></p>
    </div>
  </div>
<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }

  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var state;
    if (event.data == "1"){
      state = "ON";
    }
    else{
      state = "OFF";
    }
    document.getElementById('state').innerHTML = state;
  }
  window.addEventListener('load', onLoad);
  function onLoad(event) {
    initWebSocket();
    initButton();
  }

  function initButton() {
    document.getElementById('button').addEventListener('click', toggle);
  }
  function toggle(){
    websocket.send('toggle');
  }
</script>
</body>
</html>

CSS

Entre les balises

, nous incluons les styles pour styliser la page Web à l’aide de CSS. N’hésitez pas à le modifier pour que la page Web s’affiche comme vous le souhaitez. Nous n’expliquerons pas le fonctionnement du CSS de cette page Web car il n’est pas pertinent pour ce didacticiel WebSocket.

<style>
  html {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  h2 {
    font-size: 1.5rem;
    font-weight: bold;
    color: #143642;
  }
  .topnav {
    overflow: hidden;
    background-color: #143642;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
    max-width: 600px;
    margin: 0 auto;
  }
  .card {
    background-color: #F8F7F9;;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding-top:10px;
    padding-bottom:20px;
  }
  .button {
    padding: 15px 50px;
    font-size: 24px;
    text-align: center;
    outline: none;
    color: #fff;
    background-color: #0f8b8d;
    border: none;
    border-radius: 5px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
   }
   .button:active {
     background-color: #0f8b8d;
     box-shadow: 2 2px #CDCDCD;
     transform: translateY(2px);
   }
   .state {
     font-size: 1.5rem;
     color:#8c8c8c;
     font-weight: bold;
   }
 </style>

HTML

Entre les balises , nous ajoutons le contenu de la page Web visible par l’utilisateur.

<div class="topnav">
  <h1>ESP WebSocket Server</h1>
</div>
<div class="content">
  <div class="card">
    <h2>Output - GPIO 2</h2>
    <p class="state">state: <span id="state">%STATE%</span></p>
    <p><button id="button" class="button">Toggle</button></p>
  </div>
</div>

Il y a un titre 1 avec le texte « ESP WebSocket Server ». N’hésitez pas à modifier ce texte.

<h1>ESP WebSocket Server</h1>

Ensuite, il y a un titre 2 avec le texte « Sortie – GPIO 2 ».

<h2>Output - GPIO 2</h2>

Après cela, nous avons un paragraphe qui affiche l’état GPIO actuel.

<p class="state">state: <span id="state">%STATE%</span></p>

Le %STATE% est un espace réservé pour l’état GPIO. Elle sera remplacée par la valeur actuelle par l’ESP8266 au moment de l’envoi de la page Web. Les espaces réservés sur le texte HTML doivent être entre les signes %. Cela signifie que ce texte %STATE% est comme une variable qui sera ensuite remplacée par la valeur réelle.

Après avoir envoyé la page Web au client, l’état doit changer dynamiquement chaque fois qu’il y a un changement dans l’état GPIO. Nous recevrons ces informations via le protocole WebSocket. Ensuite, JavaScript gère ce qu’il faut faire avec les informations reçues pour mettre à jour l’état en conséquence. Pour pouvoir gérer ce texte à l’aide de JavaScript, le texte doit avoir un identifiant que nous pouvons référencer. Dans ce cas, l’identifiant est l’état ( ).

Enfin, il y a un paragraphe avec le bouton pour basculer l’état GPIO.

<p><button id="button" class="button">Toggle</button></p>

Notez que nous avons donné un identifiant au bouton ( id= »bouton »).

JavaScript – Gestion des WebSockets

Le JavaScript se place entre les balises . Il est chargé d’initialiser une connexion WebSocket avec le serveur dès que l’interface Web est entièrement chargée dans le navigateur et de gérer l’échange de données via WebSockets.

<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }

  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var state;
    if (event.data == "1"){
      state = "ON";
    }
    else{
      state = "OFF";
    }
    document.getElementById('state').innerHTML = state;
  }

  window.addEventListener('load', onLoad);

  function onLoad(event) {
    initWebSocket();
    initButton();
  }

  function initButton() {
    document.getElementById('button').addEventListener('click', toggle);
  }

  function toggle(){
    websocket.send('toggle');
  }
</script>

Voyons comment cela fonctionne.

La passerelle est le point d’entrée de l’interface WebSocket.

var gateway = `ws://${window.location.hostname}/ws`;

window.location.hostname obtient l’adresse de la page actuelle (l’adresse IP du serveur Web).

Créez une nouvelle variable globale appelée websocket.

var websocket;

Ajoutez un écouteur d’événement qui appellera la fonction onload lors du chargement de la page Web.

window.addEventListener('load', onload);

La fonction onload() appelle la fonction initWebSocket() pour initialiser une connexion WebSocket avec le serveur et la fonction initButton() pour ajouter des écouteurs d’événement aux boutons.

function onload(event) {
  initWebSocket();
  initButton();
}

La fonction initWebSocket() initialise une connexion WebSocket sur la passerelle définie précédemment. Nous attribuons également plusieurs fonctions de rappel lorsque la connexion WebSocket est ouverte, fermée ou lorsqu’un message est reçu.

function initWebSocket() {
  console.log('Trying to open a WebSocket connection…');
  websocket = new WebSocket(gateway);
  websocket.onopen    = onOpen;
  websocket.onclose   = onClose;
  websocket.onmessage = onMessage;
}

Lorsque la connexion est ouverte, nous imprimons simplement un message dans la console et envoyons un message disant « salut ». L’ESP8266 reçoit ce message, nous savons donc que la connexion a été initialisée.

function onOpen(event) {
  console.log('Connection opened');
  websocket.send('hi');
}

Si pour une raison quelconque la connexion web socket est fermée, nous appelons à nouveau la fonction initWebSocket() après 2000 millisecondes (2 secondes).

function onClose(event) {
  console.log('Connection closed');
  setTimeout(initWebSocket, 2000);
} 

La méthode setTimeout() appelle une fonction ou évalue une expression après un nombre spécifié de millisecondes.

Enfin, nous devons gérer ce qui se passe lorsque nous recevons un nouveau message. Le serveur (votre carte ESP) enverra soit un message « 1 » soit un message « 0 ». En fonction du message reçu, on veut afficher un message « ON » ou « OFF » sur le paragraphe qui affiche l’état. Vous souvenez-vous de cette balise avec id= »state » ? Nous obtiendrons cet élément et définirons sa valeur sur ON ou OFF.

function onMessage(event) {
  var state;
  if (event.data == "1"){
    state = "ON";
  }
  else{
    state = "OFF";
  }
  document.getElementById('state').innerHTML = state;
}

La fonction initButton() récupère le bouton par son identifiant (bouton) et ajoute un écouteur d’événement de type ‘click’.

function initButton() {
  document.getElementById('button').addEventListener('click', toggle);
}

Cela signifie que lorsque vous cliquez sur le bouton, la fonction bascule est appelée.

La fonction bascule envoie un message à l’aide de la connexion WebSocket avec le texte « bascule ».

function toggle(){
  websocket.send('toggle');
}

Ensuite, l’ESP8266 doit gérer ce qui se passe lorsqu’il reçoit ce message – basculer l’état GPIO actuel.

Gestion des WebSockets – Serveur

Auparavant, vous avez vu comment gérer la connexion WebSocket côté client (navigateur). Voyons maintenant comment le gérer côté serveur.

Aviser tous les clients

La fonction notifyClients() notifie tous les clients avec un message contenant tout ce que vous transmettez comme argument. Dans ce cas, nous voudrons informer tous les clients de l’état actuel de la LED chaque fois qu’il y a un changement.

void notifyClients() {
  ws.textAll(String(ledState));
}

La classe AsyncWebSocket fournit une méthode textAll() pour envoyer le même message à tous les clients connectés au serveur en même temps.

Gérer les messages WebSocket

La fonction handleWebSocketMessage() est une fonction de rappel qui s’exécute chaque fois que nous recevons de nouvelles données des clients via le protocole WebSocket.

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
      notifyClients();
    }
  }
}

Si nous recevons le message « toggle », nous basculons la valeur de la variable ledState. De plus, nous notifions tous les clients en appelant la fonction notifyClients(). De cette façon, tous les clients sont informés du changement et mettent à jour l’interface en conséquence.

if (strcmp((char*)data, "toggle") == 0) {
  ledState = !ledState;
  notifyClients();
}

Configurer le serveur WebSocket

Nous devons maintenant configurer un écouteur d’événement pour gérer les différentes étapes asynchrones du protocole WebSocket. Ce gestionnaire d’événements peut être implémenté en définissant onEvent() comme suit :

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
 void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
      handleWebSocketMessage(arg, data, len);
      break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
      break;
  }
}

L’argument type représente l’événement qui se produit. Il peut prendre les valeurs suivantes :

  • WS_EVT_CONNECT lorsqu’un client s’est connecté ;
  • WS_EVT_DISCONNECT lorsqu’un client s’est déconnecté ;
  • WS_EVT_DATA lorsqu’un paquet de données est reçu du client ;
  • WS_EVT_PONG en réponse à une requête ping ;
  • WS_EVT_ERROR lorsqu’une erreur est reçue du client.

Initialiser WebSocket

Enfin, la fonction initWebSocket() initialise le protocole WebSocket.

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

processeur()

La fonction processor() est chargée de rechercher des espaces réservés sur le texte HTML et de les remplacer par ce que nous voulons avant d’envoyer la page Web au navigateur. Dans notre cas, nous remplacerons l’espace réservé %STATE% par ON si le ledState est 1. Sinon, remplacez-le par OFF.

String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if (ledState){
      return "ON";
    }
    else{
      return "OFF";
    }
  }
}

installation()

Dans setup(), initialisez le moniteur série à des fins de débogage.

Serial.begin(115200);

Configurez le ledPin en tant que SORTIE et réglez-le sur BAS lorsque le programme démarre pour la première fois.

pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

Initialisez le Wi-Fi et imprimez l’adresse IP ESP8266 sur le moniteur série.

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

// Print ESP Local IP Address
Serial.println(WiFi.localIP());

Initialisez le protocole WebSocket en appelant la fonction initWebSocket() créée précédemment.

initWebSocket();

Traiter les demandes

Servez le texte enregistré sur la variable index_html lorsque vous recevez une demande sur la root / l’URL – vous devez transmettre la fonction de processeur en tant qu’argument pour remplacer les espaces réservés par l’état GPIO actuel.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html, processor);
});

Enfin, démarrez le serveur.

server.begin();

boucle()

La LED sera contrôlée physiquement sur la boucle().

void loop() {
  ws.cleanupClients();
  digitalWrite(ledPin, ledState);
}

Notez que nous appelons tous la méthode cleanupClients(). Voici pourquoi (explication de la page GitHub de la bibliothèque ESPAsyncWebServer):

Les navigateurs ne ferment parfois pas correctement la connexion WebSocket, même lorsque la fonction close() est appelée en JavaScript. Cela finira par épuiser les ressources du serveur Web et entraînera le blocage du serveur. L’appel périodique de la fonction cleanupClients() depuis la boucle principale() limite le nombre de clients en fermant le client le plus ancien lorsque le nombre maximum de clients a été dépassé. Cela peut être appelé à chaque cycle, cependant, si vous souhaitez utiliser moins d’énergie, il suffit d’appeler aussi rarement qu’une fois par seconde.

Manifestation

Après avoir inséré vos informations d’identification réseau sur les variables ssid et mot de passe, vous pouvez télécharger le code sur votre carte. N’oubliez pas de vérifier si vous avez sélectionné la bonne carte et le bon port COM.

Après avoir téléchargé le code, ouvrez le moniteur série à un débit en bauds de 115200 et appuyez sur le bouton EN/RST intégré. L’adresse IP ESP doit être imprimée.

Ouvrez un navigateur sur votre réseau local et insérez l’adresse IP ESP8266. Vous devriez avoir accès à la page Web pour contrôler la sortie.

Vue d'ensemble du projet ESP32 WebSocket Server Toggle Outputs

Cliquez sur le bouton pour basculer la LED. Vous pouvez ouvrir plusieurs onglets de navigateur Web en même temps ou accéder au serveur Web à partir de différents appareils et l’état de la LED sera automatiquement mis à jour dans tous les clients chaque fois qu’il y a un changement.

Conclusion

Dans ce tutoriel, vous avez appris à configurer un serveur WebSocket avec l’ESP8266. Le protocole WebSocket permet une communication en duplex intégral entre le client et le serveur. Après l’initialisation, le serveur et le client peuvent échanger des données à tout moment.

Ceci est très utile car le serveur peut envoyer des données au client chaque fois que quelque chose se passe. Par exemple, vous pouvez ajouter un bouton physique à cette configuration qui, lorsqu’il est enfoncé, avertit tous les clients de mettre à jour l’interface Web.

Dans cet exemple, nous vous avons montré comment contrôler un GPIO de l’ESP8266. Vous pouvez utiliser cette méthode pour contrôler plus de GPIO. Vous pouvez également utiliser le protocole WebSocket pour envoyer des lectures de capteurs ou des notifications à tout moment.

Nous espérons que vous avez trouvé ce tutoriel utile. Nous avons l’intention de créer plus de tutoriels et d’exemples en utilisant le protocole WebSocket. Alors restez à l’écoute.

En savoir plus sur l’ESP8266 avec nos ressources :

Apprenez l’histoire de Raspberry Pi à travers cette vidéo :

YouTube video

  • AZDelivery D1 Mini NodeMCU Lua avec ESP8266-12F Module WLAN CH340G Compatible avec Arduino incluant Un E-Book!
    ✅ La carte AZ-Delivery D1 mini est une carte WiFi Mini-NodeMcu Lua basée sur un ESP-8266-12F et un connecteur micro USB. ✅ Cette carte WLAN contient 11 broches d'entrée/sortie numériques, toutes les broches ont une interruption / pwm / I2C / 1 fil. ✅ Grâce à sa conception ultra-compacte, le Mini NodeMcu AZ-Delivery D1 avec le module WLAN ESP8266-12F peut être facilement stocké à distance. ✅ La carte de développement sans fil D1 Mini AZ-Delivery est 100% compatible avec IDE et peut être programmée dans Arduino-IDE. ✅ Ce produit inclut un E-Book qui fournit des informations utiles sur la façon de commencer votre projet. Il permet une installation rapide et fait gagner du temps sur le processus de configuration. On y trouve une série d'exemples d'applications, des guides d'installation complets et des bibliothèques.
  • AZDelivery NodeMCU Lolin V3 Module ESP8266 (ESP-12F), Carte de développement Development Board Wi-FI avec CH340, NodeMCU V3 Wireless Compatible avec Arduino incluant Un Ebook!
    ✅ Puissant microcontrôleur ESP8266 (ESP-12E) avec WLAN 802.11 b/g/n et antenne intégrée de 20dBm ✅ Prototypage pratique grâce à une programmation aisée via un script Lua et une conception compatible avec le breadboard (espacement des broches de 27.5 mm entre les en-têtes) ✅ Dimensions (L xlx H): 58 mm x 31 mm x 13 mm ✅ Livre électronique avec instructions d'installation et exemples de script ! ✅ Ce produit inclut un E-Book qui fournit des informations utiles sur la façon de commencer votre projet. Il permet une installation rapide et fait gagner du temps sur le processus de configuration. On y trouve une série d'exemples d'applications, des guides d'installation complets et des bibliothèques.
  • HiLetgo 2pcs NodeMCU LUA ESP8266 CP2102 ESP-12E Internet Wifi Carte de développement Open Source Serial module sans fil Fonctionne parfaitement avec Arduino IDE/Micropython
    ESP8266 CP2102 NodeMCU LUA ESP-12E Module sans fil série WIFI Micro-USB intégré, avec commutateurs de flash et de réinitialisation, facile à programmer Port d'E / S complet et prise en charge sans fil 802.11, téléchargement direct pas besoin de réinitialiser Compatible Arduino, fonctionne parfaitement avec les derniers Arduino IDE / Mongoose IoT / Micropython Accès au téléchargement des données sur le site Web: http://www.nodemcu.com