Serveur Web ESP8266 NodeMCU : moteur pas à pas (WebSocket)

Serveur Web ESP8266 NodeMCU : moteur pas à pas (WebSocket)

Dans ce guide, vous apprendrez à créer un serveur Web avec la carte ESP8266 NodeMCU qui affiche une page Web pour contrôler un moteur pas à pas. La page Web vous permet d’insérer le nombre d’étapes et de sélectionner le sens horaire ou antihoraire. De plus, il indique également si le moteur tourne actuellement ou s’il est arrêté. La communication entre le client et le serveur est réalisée via le protocole WebSocket. Tous les clients sont mis à jour avec l’état actuel du moteur.

ESP8266 NodeMCU Contrôle du serveur Web Moteur pas à pas WebSocket Arduino IDE

Pour mieux comprendre le fonctionnement de ce projet, vous pouvez jeter un œil aux tutoriels suivants :

Table des matières

  1. Conditions préalables
  2. Aperçu du projet
  3. Organisation de vos fichiers :
    1. Fichier HTML
    2. Fichier CSS
    3. Fichier JavaScript
    4. Esquisse Arduino
  4. Télécharger le code et les fichiers
  5. Manifestation

Conditions préalables

Avant de poursuivre le didacticiel, assurez-vous de vérifier les prérequis suivants.

1) Pièces requises

Pour suivre ce tutoriel, vous avez besoin des pièces suivantes :

Vous pouvez utiliser les liens précédents ou accéder directement à MakerAdvisor.com/tools pour trouver toutes les pièces pour vos projets au meilleur prix !

Serveur Web ESP8266 NodeMCU moteur pas a pas WebSocket

2) Complément pour les cartes Arduino IDE et ESP8266

Nous allons programmer l’ESP8266 à l’aide de l’IDE Arduino. Donc, vous devez avoir installé le module complémentaire ESP8266. Suivez le tutoriel suivant si vous ne l’avez pas déjà fait :

Si vous souhaitez utiliser VS Code avec l’extension PlatformIO, suivez plutôt le tutoriel suivant pour apprendre à programmer l’ESP8266 :

3) Plugin de téléchargement de système de fichiers

Pour télécharger les fichiers HTML, CSS et JavaScript nécessaires à la construction de ce projet sur le système de fichiers ESP8266 (LittleFS), nous utiliserons un plug-in pour Arduino IDE : LittleFS Filesystem Uploader. Suivez le tutoriel suivant pour installer le plugin filesystem uploader si vous ne l’avez pas déjà fait :

Si vous utilisez VS Code avec l’extension PlatformIO, lisez le didacticiel suivant pour savoir comment télécharger des fichiers sur le système de fichiers :

4) Bibliothèques

Il existe différentes façons de contrôler les moteurs pas à pas avec un microcontrôleur. Pour contrôler le moteur pas à pas avec l’ESP8266, nous utiliserons le AccélStepper bibliothèque. Cette bibliothèque vous permet de déplacer facilement le moteur d’un nombre défini de pas, de régler sa vitesse, son accélération et bien plus encore. La bibliothèque possède une excellente documentation expliquant comment utiliser ses méthodes. Tu peux le vérifier ici.

Suivez les étapes suivantes pour installer la bibliothèque dans votre IDE Arduino.

  1. Accédez à Esquisse > Inclure la bibliothèque > Gérer les bibliothèques…
  2. Recherchez « accelstepper ».
  3. Installez la bibliothèque AccelStepper de Mike McCauley. Nous utilisons la version 1.61.0.
Installer la bibliothèque AccelStepper Arduino IDE

Pour construire le serveur Web, vous devez installer les bibliothèques suivantes :

Les bibliothèques ESPAsyncWebServer et ESPAsynTCP ne peuvent pas être installées via le gestionnaire de bibliothèque Arduino. Vous devez cliquer sur les liens précédents pour télécharger les fichiers de la bibliothèque. Ensuite, dans votre IDE Arduino, allez dans Sketch> Inclure la bibliothèque> Ajouter une bibliothèque .zip et sélectionnez les bibliothèques que vous venez de télécharger.

Installation de bibliothèques (VS Code + PlatformIO)

Si vous programmez l’ESP8266 à l’aide de PlatformIO, vous devez ajouter les lignes suivantes au fichier platformio.ini pour inclure les bibliothèques (changez également la vitesse du moniteur série sur 115200 et définissez le système de fichiers sur LittleFS) :

monitor_speed = 115200
board_build.filesystem = littlefs
lib_deps = ESP Async WebServer
    waspinator/AccelStepper @ ^1.61

5) Diagramme schématique

Le diagramme schématique suivant montre les connexions entre le moteur pas à pas et l’ESP8266.

ESP8266 avec moteur pas à pas 28BYJ-48 et schéma de câblage ULN2003A

Remarque : Vous devez alimenter le pilote de moteur ULN2003 à l’aide d’une alimentation externe de 5 V.

Pilote de moteur ESP8266
EN 1 GPIO 5
EN 2 GPIO 4
IN3 GPIO 14
IN4 GPIO 12

Aperçu du projet

L’image suivante montre la page Web que vous allez créer pour ce projet.

Vue d'ensemble du projet WebSocket du moteur pas à pas de contrôle ESP8266 NodeMCU
  • La page Web affiche un formulaire dans lequel vous pouvez entrer le nombre de pas que vous souhaitez que le moteur déplace et sélectionner la direction : dans le sens des aiguilles d’une montre ou dans le sens inverse des aiguilles d’une montre.
  • Il indique également l’état du moteur : moteur qui tourne ou moteur arrêté. De plus, il y a une icône d’engrenage qui tourne tant que le moteur tourne. L’engrenage tourne dans le sens des aiguilles d’une montre ou dans le sens inverse des aiguilles d’une montre en fonction de la direction choisie.
ESP32 ESP8266 Moteur pas à pas Serveur Web Websocket Comment ça marche
  • Le serveur et le client communiquent via le protocole WebSocket.
  • Lorsque vous cliquez sur GO! bouton, il appelle une fonction Javascript qui envoie un message via le protocole WebSocket avec toutes les informations : étapes et direction (3). Le message est au format suivant :
steps&direction

Ainsi, si vous soumettez 2000 pas et dans le sens des aiguilles d’une montre, le message suivant sera envoyé :

2000&CW
  • En même temps, cela changera l’état du moteur sur la page Web et l’engrenage commencera à tourner dans la bonne direction (2).
  • Ensuite, le serveur reçoit le message (4) et fait tourner le moteur en conséquence (5).
  • Lorsque le moteur s’arrête de tourner (6), l’ESP enverra un message au(x) client(s), également via le protocole WebSocket, informant que le moteur s’est arrêté (7).
  • Le ou les clients reçoivent cette demande et mettent à jour l’état du moteur sur la page Web (8).

Organiser vos fichiers

Les fichiers que vous souhaitez télécharger sur le système de fichiers ESP doivent être placés dans un dossier appelé data sous le dossier du projet. Nous allons déplacer trois fichiers dans ce dossier :

  • index.html pour créer la page Web ;
  • style.css pour styliser la page Web ;
  • script.js pour gérer la communication websocket et démarrer/arrêter l’animation de l’engrenage.
ESP8266 organiser vos fichiers arduino sketch index html style css script js

Vous devez enregistrer les fichiers HTML, CSS et JavaScript dans un dossier appelé data dans le dossier d’esquisse Arduino, comme indiqué dans le diagramme précédent. Nous allons télécharger ces fichiers sur le système de fichiers ESP8266 (LittleFS).

Vous pouvez télécharger tous les fichiers du projet :

Fichier HTML

Créez un fichier appelé index.html avec le contenu suivant :

<!DOCTYPE html>
<html>
<head>
  <title>Stepper Motor</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" href="https://www.raspberryme.com/stepper-motor-esp8266-websocket/style.css">
  <link rel="icon" type="image/png" href="favicon.png">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
</head>
<body>
  <div class="topnav">
    <h1>Stepper Motor Control <i class="fas fa-cogs"></i></h1>
  </div>
  <div class="content">
        <form>
          <input type="radio" id="CW" name="direction" value="CW" checked>
          <label for="CW">Clockwise</label>
          <input type="radio" id="CCW" name="direction" value="CCW">
          <label for="CW">Counterclockwise</label><br><br><br>
          <label for="steps">Number of steps:</label>
          <input type="number" id="steps" name="steps">
        </form>
        <button onclick="submitForm()">GO!</button>
        <p>Motor state: <span id="motor-state">Stopped</span></p>
        <p><i id="gear" class="fas fa-cog"></i> </p>

  </div>
</body>
<script src="https://www.raspberryme.com/stepper-motor-esp8266-websocket/script.js"></script>
</html>

Afficher le code brut

Ce fichier HTML est très similaire à celui utilisé dans ce tutoriel. Vous pouvez cliquer ici pour une explication complète du fichier HTML.

Nous avons ajouté des identifiants aux éléments HTML que nous voulons manipuler à l’aide de JavaScript : les boutons radio et le champ de saisie :

  • bouton radio dans le sens des aiguilles d’une montre : id=”CW”
  • bouton radio dans le sens contraire : id=”CCW”
  • champ de saisie des étapes : id= »étapes »
<input type="radio" id="CW" name="direction" value="CW" checked>
<label for="CW">Clockwise</label>
<input type="radio" id="CCW" name="direction" value="CCW">
<label for="CW">Counterclockwise</label><br><br><br>
<label for="steps">Number of steps:</label>
<input type="number" id="steps" name="steps">

Nous voulons envoyer les résultats du formulaire au serveur (ESP8266) via le protocole WebSocket. Nous avons donc ajouté un bouton qui, lorsqu’il est cliqué (événement onclick), appelle la fonction javascript définie par l’utilisateur submitForm() qui envoie les résultats au serveur, comme vous le verrez plus tard dans le Rubrique JavaScript.

<button onclick="submitForm()">GO!</button>

De plus, nous avons également ajouté un paragraphe pour afficher l’état du moteur. Nous avons ajouté une balise avec l’identifiant de l’état du moteur afin de pouvoir manipuler le texte entre les balises à l’aide de Javascript.

<p>Motor state: <span id="motor-state">Stopped</span></p>

Enfin, il y a un paragraphe affichant une roue dentée avec l’id= »gear ». Nous avons besoin de cet identifiant pour faire bouger l’équipement.

<p><i id="gear" class="fas fa-cog"></i> </p>

N’oubliez pas que vous devez référencer le fichier JavaScript (scrip.js) dans le fichier HTML comme suit :

<script src="https://www.raspberryme.com/stepper-motor-esp8266-websocket/script.js"></script>

Fichier CSS

Créez un fichier nommé style.css avec le contenu suivant :

html {
  font-family: Arial, Helvetica, sans-serif;
}

h1 {
  font-size: 1.8rem;
  color: white;
}

p{
  font-size: 20px;
  text-align: center;
}

.topnav {
  overflow: hidden;
  background-color: #0A1128;
  text-align: center;
}

body {
  margin: 0;
}

.content {
  padding: 20px;
  max-width: max-content;
  margin: 0 auto;
}

input[type=number], select {
  width: 100%;
  padding: 12px 20px;
  margin: 8px 0;
  display: inline-block;
  border: 1px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}

form{
  border-radius: 5px;
  background-color: #f2f2f2;
  padding: 20px;
}

button {
  background-color: #034078;
  border: none;
  padding: 14px 20px;
  text-align: center;
  font-size: 20px;
  border-radius: 4px;
  transition-duration: 0.4s;
  width: 100%;
  color: white;
  cursor: pointer;
}

button:hover {
    background-color: #1282A2;
}

input[type="radio"] {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  border: 2px solid #999;
  transition: 0.2s all linear;
  margin-right: 5px;
  position: relative;
  top: 4px;
  }

input[type="radio"]:checked{
  border: 6px solid #1282A2;
}

#motor-state{
  font-weight: bold;
  color: red;
}

#gear{
  font-size:100px;
  color:#2d3031cb;
}

.spin {
  -webkit-animation:spin 4s linear infinite;
  -moz-animation:spin 4s linear infinite;
  animation:spin 4s linear infinite;
}

.spin-back {
  -webkit-animation:spin-back 4s linear infinite;
  -moz-animation:spin-back 4s linear infinite;
  animation:spin-back 4s linear infinite;
}

@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

@-moz-keyframes spin-back { 100% { -moz-transform: rotate(-360deg); } }
@-webkit-keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); } }
@keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); transform:rotate(-360deg); } }

Afficher le code brut

Nous avons déjà couvert le fonctionnement du CSS pour le formulaire HTML. Vous pouvez cliquer ici pour une explication détaillée. Jetons un coup d’œil aux parties pertinentes de ce didacticiel.

Nous formatons le texte de l’état du moteur font-weight (gras) et couleur (rouge). Pour faire référence à un identifiant spécifique dans CSS, utilisez # suivi de l’identifiant (#motor-state).

#motor-state{
  font-weight: bold;
  color: red;
}

Les lignes suivantes formatent la couleur et la taille de l’icône d’engrenage – rappelez-vous que son identifiant est gear, nous y faisons donc référence avec #gear :

#gear{
  font-size:100px;
  color:#2d3031cb;
}

Ensuite, nous formatons deux classes spin et spin-back qui ne sont encore attribuées à aucun élément HTML. Nous attribuerons les classes spin et spin-back à l’engrenage à l’aide de JavaScript lorsque le moteur commencera à bouger.

Ces classes utilisent la propriété animation pour faire pivoter l’engrenage. Pour en savoir plus sur le fonctionnement de la propriété d’animation, nous vous recommandons de consulter ce tutoriel rapide.

.spin {
  -webkit-animation:spin 4s linear infinite;
  -moz-animation:spin 4s linear infinite;
  animation:spin 4s linear infinite;
}

.spin-back {
  -webkit-animation:spin-back 4s linear infinite;
  -moz-animation:spin-back 4s linear infinite;
  animation:spin-back 4s linear infinite;
}

@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }

@-moz-keyframes spin-back { 100% { -moz-transform: rotate(-360deg); } }
@-webkit-keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); } }
@keyframes spin-back { 100% { -webkit-transform: rotate(-360deg); transform:rotate(-360deg); } }

Fichier JavaScript

Créez un fichier appelé script.js avec le contenu suivant :

var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onload);
var direction;

function onload(event) {
    initWebSocket();
}

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

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

function onClose(event) {
    console.log('Connection closed');
    document.getElementById("motor-state").innerHTML = "motor stopped"
    setTimeout(initWebSocket, 2000);
}

function submitForm(){
    const rbs = document.querySelectorAll('input[name="direction"]');
    direction;
    for (const rb of rbs) {
        if (rb.checked) {
            direction = rb.value;
            break;
        }
    }

    document.getElementById("motor-state").innerHTML = "motor spinning...";
    document.getElementById("motor-state").style.color = "blue";
    if (direction=="CW"){
        document.getElementById("gear").classList.add("spin");
    }
    else{
        document.getElementById("gear").classList.add("spin-back");
    }
    
    var steps = document.getElementById("steps").value;
    websocket.send(steps+"&"+direction);
}

function onMessage(event) {
    console.log(event.data);
    direction = event.data;
    if (direction=="stop"){ 
      document.getElementById("motor-state").innerHTML = "motor stopped"
      document.getElementById("motor-state").style.color = "red";
      document.getElementById("gear").classList.remove("spin", "spin-back");
    }
    else if(direction=="CW" || direction=="CCW"){
        document.getElementById("motor-state").innerHTML = "motor spinning...";
        document.getElementById("motor-state").style.color = "blue";
        if (direction=="CW"){
            document.getElementById("gear").classList.add("spin");
        }
        else{
            document.getElementById("gear").classList.add("spin-back");
        }
    }
}

Afficher le code brut

Voyons comment fonctionne le JavaScript pour ce projet.

La passerelle est le point d’entrée de l’interface WebSocket. window.location.hostname obtient l’adresse de la page actuelle (l’adresse IP du serveur Web)

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

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

var websocket;

Créez une autre variable globale appelée direction qui contiendra la direction actuelle du moteur : dans le sens des aiguilles d’une montre, dans le sens inverse ou à l’arrêt.

var direction;

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.

function onload(event) {
  initWebSocket();
}

La fonction initWebSocket() initialise une connexion WebSocket sur la passerelle définie précédemment. Nous attribuons également plusieurs fonctions de rappel qui seront déclenchées 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, imprimez un message dans la console à des fins de débogage.

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

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

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

Enfin, nous devons gérer ce qui se passe lorsque le formulaire est soumis et lorsque le client reçoit un nouveau message (événement onMessage).

Lorsque le formulaire est soumis, la fonction submitForm() est appelée :

function submitForm(){

Nous commençons par déterminer quel bouton radio est sélectionné. Nous enregistrons la valeur du bouton radio sélectionné dans la variable de direction.

const rbs = document.querySelectorAll('input[name="direction"]');
var direction;
for (const rb of rbs) {
  if (rb.checked) {
    direction = rb.value;
    break;
  }
}

Ensuite, nous changeons le texte de l’état du moteur en rotation du moteur… et sa couleur en bleu. Nous nous référons à cet élément HTML par son id motor-state.

document.getElementById("motor-state").innerHTML = "motor spinning...";
document.getElementById("motor-state").style.color = "blue";

Ensuite, nous vérifions si nous avons sélectionné le sens horaire ou antihoraire pour faire tourner l’engrenage dans la bonne direction. Pour ce faire, nous ajoutons la classe spin ou spin-back à l’élément avec l’identifiant de l’engrenage.

if (direction=="CW"){
  document.getElementById("gear").classList.add("spin");
}
else{
  document.getElementById("gear").classList.add("spin-back");
}

Nous obtenons le nombre d’étapes insérées et l’enregistrons dans la variable étapes.

var steps = document.getElementById("steps").value;

Ensuite, nous envoyons enfin un message via le protocole WebSocket au serveur (ESP8266) avec le nombre d’étapes et la direction séparées par un &.

websocket.send(steps+"&"+direction);

Le serveur (votre carte ESP) enverra un message lorsqu’il sera temps de changer l’état du moteur. Lorsque cela se produit, nous enregistrons le message dans la variable de direction.

Nous vérifions le contenu du message et modifions l’état du moteur et l’animation des engrenages en conséquence.

function onMessage(event) {
  console.log(event.data);
  direction = event.data;
  if (direction=="stop"){ 
    document.getElementById("motor-state").innerHTML = "motor stopped"
    document.getElementById("motor-state").style.color = "red";
    document.getElementById("gear").classList.remove("spin", "spin-back");
  }
  else if(direction=="CW" || direction=="CCW"){
    document.getElementById("motor-state").innerHTML = "motor spinning...";
    document.getElementById("motor-state").style.color = "blue";
    if (direction=="CW"){
      document.getElementById("gear").classList.add("spin");
    }
    else{
      document.getElementById("gear").classList.add("spin-back");
    }
  }
}

Esquisse Arduino

Avant de télécharger, vous pouvez utiliser le lien suivant pour :

Copiez le code suivant dans l’IDE Arduino. Insérez vos informations d’identification réseau et cela fonctionnera immédiatement.

/*
  Rui Santos
  Complete project details at https://Raspberryme.com/stepper-motor-esp8266-websocket/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <AccelStepper.h>

#define IN1 5
#define IN2 4
#define IN3 14
#define IN4 12
AccelStepper stepper(AccelStepper::HALF4WIRE, IN1, IN3, IN2, IN4);

String message = "";

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

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create a WebSocket object
AsyncWebSocket ws("/ws");

//Variables to save values from HTML form
String direction ="STOP";
String steps;

bool notifyStop = false;

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFS mounted successfully");
  }
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

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

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;
    message = (char*)data;
    steps = message.substring(0, message.indexOf("&"));
    direction = message.substring(message.indexOf("&")+1, message.length());
    Serial.print("steps");
    Serial.println(steps);
    Serial.print("direction");
    Serial.println(direction);
    notifyClients(direction);
    notifyStop = true;
    if (direction == "CW"){
      Serial.print("CW");
      stepper.move(steps.toInt());
    }
    else{
      Serial.print("CCW");
      stepper.move(-steps.toInt());
    }
  }
}

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());
      //Notify client of motor current state when it first connects
      notifyClients(direction);
      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);
}

void setup() {
  // Serial port for debugging purposes

  Serial.begin(115200);
  initWiFi();
  initWebSocket();
  initFS();
  stepper.setMaxSpeed(1000);
  stepper.setAcceleration(100);

  // Web Server Root URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html");
  });
  
  server.serveStatic("/", LittleFS, "/");

  server.begin();
}

void loop() {
  if (stepper.distanceToGo() == 0 && notifyStop == true){  
    direction = "stop";
    notifyClients(direction);
    notifyStop = false;
  }
  ws.cleanupClients();
  stepper.run();
}

Afficher le code brut

L’esquisse Arduino est très similaire à ce didacticiel, mais elle gère la communication client-serveur à l’aide du protocole WebSocket. Voyons comment cela fonctionne ou passez à la section de démonstration.

Inclure les bibliothèques

Tout d’abord, incluez les bibliothèques requises. ESP8266WiFi, ESPAsyncTCP et ESPAsyncWebServer pour créer le serveur Web, la bibliothèque LittleFS pour utiliser le système de fichiers ESP8266 et la bibliothèque AccelStepper pour contrôler le moteur pas à pas.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncTCP.h>
#include "LittleFS.h"
#include <AccelStepper.h>

Broches du moteur pas à pas

Définissez les broches d’entrée du moteur. Dans cet exemple, nous nous connectons aux GPIO 5, 4, 14 et 12, mais vous pouvez utiliser tout autre GPIO approprié.

#define IN1 5
#define IN2 4
#define IN3 14
#define IN4 12

Initialisez une instance de la bibliothèque AccelStepper appelée stepper. Passez comme arguments : AccelStepper::HALF4WIRE pour indiquer que nous contrôlons le moteur pas à pas avec quatre fils et les broches d’entrée. Dans le cas du moteur pas à pas 28BYJ-48, l’ordre des broches est IN1, IN3, IN2, IN4—il peut être différent pour votre moteur.

Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);

Informations d’identification réseau

Insérez vos informations d’identification réseau dans les lignes suivantes.

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

AsyncWebServer et AsyncWebSocket

Créez un objet AsyncWebServer appelé serveur 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");

Initialisation des variables

Les variables suivantes enregistreront la direction et le nombre d’étapes reçues via le protocole WebSocket. Lorsque le programme démarre pour la première fois, le moteur est arrêté.

String direction ="stop";
String steps;

La variable notifyStop sera utilisée pour vérifier si le moteur a atteint sa position souhaitée et informer tous les clients que le moteur est arrêté.

bool notifyStop = false;

initFS()

La fonction initFS() initialise le système de fichiers ESP8266 (littleFS).

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
    Serial.println("LittleFS mounted successfully");
  }
}

initWiFi()

La fonction initWiFi() initialise le WiFi.

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

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 du moteur chaque fois qu’il y a un changement.

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

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;
    message = (char*)data;
    steps = message.substring(0, message.indexOf("&"));
    direction = message.substring(message.indexOf("&")+1, message.length());
    Serial.print("steps");
    Serial.println(steps);
    Serial.print("direction");
    Serial.println(direction);
    notifyClients(direction);
    notifyStop = true;
    if (direction == "CW"){
      Serial.print("CW");
      stepper.move(steps.toInt());
    }
    else{
      Serial.print("CCW");
      stepper.move(-steps.toInt());
    }
  }
}

Nous divisons le message pour obtenir le nombre d’étapes et la direction.

message = (char*)data;
steps = message.substring(0, message.indexOf("&"));
direction = message.substring(message.indexOf("&")+1, message.length());

Ensuite, nous informons tous les clients de la direction du moteur afin que tous les clients modifient l’état du moteur sur l’interface Web.

notifyClients(direction);

Nous définissons la variable notifyStop sur true afin de pouvoir notifier que le moteur s’est arrêté plus tard.

notifyStop = true;

Ensuite, définissez le nombre de pas que vous voulez que le moteur se déplace en fonction de la direction (nombre de pas négatif pour le sens antihoraire et nombre de pas positif pour le sens horaire). Pour cela, nous utilisons la fonction move(). La fonction move () ne déplace pas réellement le moteur, elle définit simplement le nouveau nombre de pas que nous voulons déplacer le moteur. Le moteur ira alors à la position souhaitée pas à pas en utilisant la fonction run() dans la boucle().

if (direction == "CW"){
  Serial.print("CW");
  stepper.move(steps.toInt());
}
else{
  Serial.print("CCW");
  stepper.move(-steps.toInt());
}

Remarque : la fonction move() définit la position cible par rapport à la position actuelle. La fonction moveTo() définit la position cible absolue (chaque position est définie par un nombre prédéterminé d’étapes). Pour plus d’informations consultez la documentation de la bibliothèque.

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());
      //Notify client of motor current state when it first connects
      notifyClients(direction);
      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.

Il y a une section pour informer n’importe quel client de l’état actuel du moteur lors de sa première connexion :

case WS_EVT_CONNECT:
  Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
  //Notify client of motor current state when it first connects
  notifyClients(direction);
  break;

Initialiser WebSocket

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

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

installation()

Dans setup(), initialisez le Serial Monitor.

Serial.begin(115200);

Appelez la fonction initWiFi() pour initialiser le WiFi.

initWiFi();

Initialiser la communication websocket.

initWebSocket();

Appelez la fonction initFS() pour initialiser le système de fichiers.

initFS();

Et réglez la vitesse et l’accélération maximales du moteur pas à pas.

stepper.setMaxSpeed(1000);
stepper.setAcceleration(100);

Traiter les demandes

Ensuite, gérez le serveur Web. Lorsque vous recevez une requête sur l’URL root (/), c’est-à-dire lorsque vous accédez à l’adresse IP ESP, envoyez le texte HTML pour créer la page Web :

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(LittleFS, "/index.html", "text/html");
});

Lorsque le fichier HTML se charge sur votre navigateur, il fera une demande pour les fichiers CSS et JavaScript. Ce sont des fichiers statiques enregistrés dans le même répertoire (LittleFS). Ainsi, nous pouvons simplement ajouter la ligne suivante pour servir les fichiers dans un répertoire à la demande de l’URL root. Il servira automatiquement les fichiers CSS et JavaScript.

server.serveStatic("/", LittleFS, "/");

Enfin, démarrez le serveur.

server.begin();

boucle()

Jetons un coup d’œil à la section loop().

Dans la boucle () est l’endroit où le moteur se déplace d’un pas à la fois en utilisant la fonction run (). Cette fonction fait bouger le moteur jusqu’à ce qu’il atteigne la position désirée (définie par la fonction move())).

stepper.run();

De plus, il existe une instruction if pour vérifier si nous devons informer les clients que le moteur s’est arrêté. La fonction distanceToGo() renvoie le nombre de pas restants jusqu’à ce que nous atteignions la position souhaitée. Ainsi, lorsqu’il renvoie 0, cela signifie que le moteur s’est arrêté. Nous vérifions également si la variable notifyStop est vraie (cela signifie que le moteur tournait auparavant).

if (stepper.distanceToGo() == 0 && notifyStop == true){  
  direction = "stop";
  notifyClients(direction);
  notifyStop = false;
}

Lorsque ces conditions sont remplies, nous informons les clients que le moteur s’est arrêté.

direction = "stop";
notifyClients(direction);

Enfin, définissez la variable notifyStop sur false. Ce ne sera à nouveau vrai que si la carte reçoit une demande de déplacement du moteur.

Télécharger le code et les fichiers

Après avoir inséré vos informations d’identification réseau, enregistrez le code. Accédez à Sketch > Show Sketch Folder et créez un dossier appelé data.

croquis afficher le dossier de croquis arduino IDE

Dans ce dossier, vous devez enregistrer les fichiers HTML, CSS et JavaScript.

Ensuite, téléchargez le code sur votre carte ESP8266. Assurez-vous d’avoir sélectionné la bonne carte et le bon port COM. Assurez-vous également d’avoir ajouté vos informations d’identification réseau.

Bouton de téléchargement Arduino 2.0

Après avoir téléchargé le code, vous devez télécharger les fichiers. Accédez à Outils> Téléchargement de données ESP8266 LittleFS et attendez que les fichiers soient téléchargés.

Outils ESP8266 Téléchargement de données LittleFS IDE Arduino

Lorsque tout est téléchargé avec succès, ouvrez le moniteur série à un débit en bauds de 115200. Appuyez sur le bouton ESP8266 RST, et il devrait imprimer l’adresse IP ESP8266.

Manifestation

Ouvrez un navigateur Web ou plusieurs fenêtres de navigateur Web sur votre réseau local et vous accéderez à la page Web pour contrôler le moteur. Soumettez le formulaire pour contrôler le moteur.

ESP8266 NodeMCU Moteur pas à pas Serveur Web Websocket Moteur tournant

L’engrenage sur la page Web commence à tourner dans la bonne direction et le moteur physique commence à fonctionner.

28BYJ-48 connecté au module ULN2003 Motor Driver 01

Lorsqu’il s’arrête, la vitesse sur la page Web et l’état du moteur changent en conséquence.

Moteur pas à pas ESP8266 NodeMCU Serveur Web Websocket Moteur arrêté

Notez que si vous avez plusieurs clients connectés, tous les clients mettent à jour l’état du moteur presque instantanément.

Regardez la vidéo ci-dessous pour une démonstration en direct.

Conclusion

Dans ce tutoriel, vous avez appris à contrôler un moteur pas à pas à l’aide d’un serveur Web construit avec l’ESP8266. Le serveur Web fournit une page Web pour contrôler le moteur pas à pas à l’aide d’un formulaire dont les résultats sont envoyés à l’ESP8266 via le protocole WebSocket.

Si vous souhaitez en savoir plus sur les protocoles de communication HTML, CSS, JavaScript et client-serveur pour créer vos serveurs Web ESP32 et ESP8266 à partir de rien, comme nous l’avons fait dans ce didacticiel, assurez-vous de consulter notre eBook :

Consultez les ressources suivantes pour en savoir plus sur la carte ESP8266 :

Nous espérons que vous trouverez ce tutoriel utile.

Merci d’avoir lu.

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

YouTube video