Movatterモバイル変換


[0]ホーム

URL:


Skip to content
HOMEESP32ESP8266ESP32-CAMRASPBERRY PIMICROPYTHONRPi PICOARDUINOREVIEWS

ESP32 Web Server: Control Stepper Motor (WebSocket)

In this guide you’ll learn how to create a web server with the ESP32 that displays a web page to control a stepper motor. The web page allows you to insert the number of steps and select clockwise or counterclockwise direction. Additionally, it also shows whether the motor is currently spinning or if it is stopped. The communication between the client and the server is achieved via WebSocket protocol. All clients are updated with the current motor state.

ESP32 Web Server Control Stepper Motor WebSocket Arduino IDE

This tutorial is the second part of this articleESP32 Web Server: Control Stepper Motor (HTML Form), but it can also be followed as a standalone tutorial.

To better understand how this project works, you can take a look at the following tutorials:

Table of Contents

  1. Prerequisites
  2. Project Overview
  3. Organizing your files:
    1. HTML File
    2. CSS File
    3. JavaScript File
    4. Arduino Sketch
  4. Upload Code and Files
  5. Demonstration

Prerequisites

Before proceeding with the tutorial, make sure you check the following prerequisites.

1) Parts Required

To follow this tutorial, you need the following parts:

You can use the preceding links or go directly toMakerAdvisor.com/tools to find all the parts for your projects at the best price!

2) Arduino IDE and ESP32 Boards Add-on

We’ll program the ESP32 using Arduino IDE. So, you must have the ESP32 add-on installed. Follow the next tutorial if you haven’t already:

If you want to use VS Code with the PlatformIO extension, follow the next tutorial instead to learn how to program the ESP32:

3) Filesystem Uploader Plugin

To upload the HTML, CSS, and JavaScript files needed to build this project to the ESP32 filesystem (LittleFS), we’ll use a plugin for Arduino IDE: LittleFSFilesystem uploader. Follow the next tutorial to install the filesystem uploader plugin if you haven’t already:

If you’re using VS Code with the PlatformIO extension, read the following tutorial to learn how to upload files to the filesystem:

4) Libraries

To build this project, you need to install the following libraries:

You can install these libraries in the Arduino Library Manager. Open the Library Manager by clicking the Library icon at the left sidebar.

Search forESPAsyncWebServer and install theESPAsyncWebServer by ESP32Async.

Installing ESPAsyncWebServer ESP32 Arduino IDE

Then, install the AsyncTCP library. Search forAsyncTCP and install theAsyncTCP by ESP32Async.

Installing AsyncTCP ESP32 Arduino IDE

5) Schematic Diagram

The following schematic diagram shows the connections between the stepper motor and the ESP32.

ESP32 with Stepper Motor 28BYJ-48 and ULN2003A Schematic Diagram Wiring

Note: you should power the ULN2003APG motor driver using an external 5V power supply.

Motor DriverESP32
IN1GPIO 19
IN2GPIO 18
IN3GPIO 5
IN4GPIO 17

Project Overview

The following video shows a quick demonstration of what you’ll achieve by the end of this tutorial.


The following image shows the web page you’ll build for this project.

Stepper Motor control ESP32 Web Server Demonstration
  • The web page shows a form where you can enter the number of steps you want the motor to move and select the direction: clockwise or counterclockwise.
  • It also shows the motor state:motor spinning ormotor stopped. Additionally, there’s a gear icon that spins as long as the motor is spinning. The gear spins clockwise or counterclockwise accordingly to the chosen direction.
ESP32 ESP8266 Stepper Motor Web Server Websocket How it Works
  • The server and the client communicate using WebSocket protocol.
  • When you click on theGO! button, it calls a Javascript function that sends a message via WebSocket protocol with all the information: steps and direction (3). The message is in the following format:
steps&direction

So, if you submit 2000 steps and clockwise direction, it will send the following message:

2000&CW
  • At the same time, it will change the motor state on the web page, and the gear will start spinning in the proper direction (2).
  • Then, the server receives the message (4) and spins the motor accordingly (5).
  • When the motor stops spinning (6), the ESP will send a message to the client(s), also via WebSocket protocol, informing that the motor has stopped (7).
  • The client(s) receive this message and update the motor state on the web page (8).

Organizing your Files

The files you want to upload to the ESP filesystem should be placed in a folder calleddata under the project folder. We’ll move three files to that folder:

  • index.html to build the web page;
  • style.css to style the web page;
  • script.js to handle websocket communication and start/stop the gear animation.
Organizing your Files arduino sketch index html style css script js

You should save the HTML, CSS, and JavaScript files inside a folder calleddata inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload those files to the ESP32 filesystem (LittleFS).

You can download all project files:

HTML File

Create a file calledindex.html with the following content:

<!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="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="script.js"></script></html>

View raw code

This HTML file is very similar to the one used in theprevious tutorial. You canclick here for a complete explanation of the HTML file.

We’ve added ids to the HTML elements we want to manipulate using JavaScript—the radio buttons and the input field:

  • clockwise radio button:id=”CW”
  • counterclowise radio button:id=”CCW”
  • steps input field:id=”steps”
<input type="radio" name="direction" value="CW" checked><label for="CW">Clockwise</label><input type="radio" name="direction" value="CCW"><label for="CW">Counterclockwise</label><br><br><br><label for="steps">Number of steps:</label><input type="number" name="steps">

We want to send the form results to the server (ESP32) via WebSocket protocol. So, we’ve added a button, that when clicked (onclick event) calls thesubmitForm() user-defined javascript function that sends the results to the server as you’ll see later in theJavaScript section.

<button>GO!</button>

Additionally, we also added a paragraph to display the motor state. We’ve added a<span> tag with themotor-state id so that we’re able to manipulate the text between the<span> tags using Javascript.

<p>Motor state: <span>Stopped</span></p>

Finally, there’s a paragraph displaying a gear with theid=”gear”. We need this id to make the gear move.

<p><i></i> </p>

Don’t forget that you need to reference the JavaScript file (scrip.js) in the HTML file as follows:

<script src="script.js"></script>

CSS File

Create a file calledstyle.css with the following content:

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); } }

View raw code

We already covered how the CSS for the HTML form works. You canclick here for a detailed explanation. Let’s take a look at the relevant parts for this tutorial.

We format the motor state text font-weight (bold) and color (red). To refer to a specific id in CSS, use# followed by the id (#motor-state).

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

The following line formats the gear icon color and size—remember that its id isgear, so we refer to it with#gear:

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

Then, we format two classesspin andspin-back that are not attributed to any HTML element yet. We’ll attribute thespin andspin-back classes to the gear using JavaScript when the motor starts moving.

These classes use theanimation property to rotate the gear. To learn more about how theanimation property works, we recommend taking a look atthis quick tutorial.

.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); } }

JavaScript File

Create a file calledscript.js with the following content:

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');    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");        }    }}

View raw code

Let’s see how the JavaScript for this project works.

Thegateway is the entry point to the WebSocket interface.window.location.hostname gets the current page address (the web server IP address)

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

Create a new global variable calledwebsocket.

var websocket;

Create another global variable calleddirection that will hold the motor’s current direction: clockwise, counterclowise or stopped.

var direction;

Add an event listener that will call theonload function when the web page loads.

window.addEventListener('load',  onload);

Theonload() function calls theinitWebSocket() function to initialize a WebSocket connection with the server.

function onload(event) {  initWebSocket();}

TheinitWebSocket() function initializes a WebSocket connection on the gateway defined earlier. We also assign several callback functions that will be triggered when the WebSocket connection is opened, closed or when a message is received.

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

When the connection is opened, print a message in the console for debugging purposes.

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

If for some reason the web socket connection is closed, call theinitWebSocket() function again after 2000 milliseconds (2 seconds).

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

Finally, we need to handle what happens when the form is submitted and when the client receives a new message (onMessage event).

When the form is submitted, thesubmitForm() function is called:

function submitForm(){

We start by getting which radio button is selected. We save the value of the selected radio button in thedirection variable.

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

Then, we change the motor state text tomotor spinning… and its color toblue. We refer to that HTML element by its idmotor-state.

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

Then, we check whether we’ve selected clockwise or counterclockwise direction to spin the gear in the right direction. To do that, we add the classspin orspin-back to the element with thegear id.

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

We get the number of steps inserted and save it in thesteps variable.

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

Then, we finally send a message via WebSocket protocol to the server (ESP32) with the number of steps and direction separated by a&.

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

The server (your ESP board) will send a message when it is time to change the motor state. When that happens, we save the message in thedirection variable.

We check the content of the message and change the motor state and gear animation accordingly.

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");    }  }}

Arduino Sketch

Before uploading, you can use the following link to:

Copy the following code to the Arduino IDE. Insert your network credentials and it will work straight away.

/*  Rui Santos & Sara Santos - Random Nerd Tutorials  Complete project details at https://RandomNerdTutorials.com/stepper-motor-esp32-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 <WiFi.h>#include <AsyncTCP.h>#include <ESPAsyncWebServer.h>#include "LittleFS.h"#include <Stepper.h>const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution#define IN1 19#define IN2 18#define IN3 5#define IN4 17Stepper myStepper(stepsPerRevolution, IN1, IN3, IN2, IN4);String message = "";// Replace with your network credentialsconst char* ssid = "REPLACE_WITH_YOUR_SSID";const char* password = "REPLACE_WITH_YOUR_PASSWORD";// Create AsyncWebServer object on port 80AsyncWebServer server(80);// Create a WebSocket objectAsyncWebSocket ws("/ws");//Variables to save values from HTML formString direction ="STOP";String steps;bool newRequest = false;// Initialize LittleFSvoid initLittleFS() {  if (!LittleFS.begin(true)) {    Serial.println("An error has occurred while mounting LittleFS");  }  else{    Serial.println("LittleFS mounted successfully");  }}// Initialize WiFivoid 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);    newRequest = true;  }}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();  initLittleFS();  myStepper.setSpeed(5);  // 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 (newRequest){    if (direction == "CW"){      myStepper.step(steps.toInt());      Serial.print("CW");    }    else{      myStepper.step(-steps.toInt());    }    newRequest = false;    notifyClients("stop");  }  ws.cleanupClients();}

View raw code

The Arduino sketch is very similar to the previous tutorial, but it handles the client-server communication using WebSocket protocol. Let’s see how it works or skip to thedemonstration section.

Include Libraries

First, include the required libraries. TheWiFi,AsyncTCP, andESPAsyncWebServer to create the web server, the LittleFS library to use the ESP32 filesystem, and theStepper library to control the stepper motor.

#include <Arduino.h>#include <WiFi.h>#include <AsyncTCP.h>#include <ESPAsyncWebServer.h>#include "LittleFS.h"#include <Stepper.h>

Stepper Motor Pins and Steps per Revolution

Define the steps per revolution of your stepper motor—in our case, it’s 2048:

const int stepsPerRevolution = 2048;  // change this to fit the number of steps per revolution

Define the motor input pins. In this example, we’re connecting to GPIOs 19, 18, 5, and 17, but you can use any other suitable GPIOs.

#define IN1 19#define IN2 18#define IN3 5#define IN4 17

Initialize an instance of the stepper library calledmyStepper. Pass as arguments the steps per revolution and the input pins. In the case of the 28BYJ-48 stepper motor, the order of the pins isIN1,IN3,IN2,IN4—it might be different for your motor.

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

Network Credentials

Insert your network credentials in the following lines.

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

AsyncWebServer and AsyncWebSocket

Create anAsyncWebServer object calledserver on port 80.

AsyncWebServer server(80);

TheESPAsyncWebServer library includes a WebSocket plugin that makes it easy to handle WebSocket connections. Create anAsyncWebSocket object calledws to handle the connections on the/ws path.

AsyncWebSocket ws("/ws");

Initializing Variables

The following variables will save the direction and number of steps received via WebSocket protocol. When the program first starts, the motor is stopped.

String direction ="stop";String steps;

ThenewRequest variable will be used to check whether a new request occurred. Then, in theloop(), we’ll spin the motor when a new request is received—when thenewRequest variable istrue.

bool newRequest = false;

initLittleFS()

TheinitLittleFS() function initializes the ESP32 Filesystem.

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

initWiFi()

TheinitWiFi() function initializes WiFi.

// Initialize WiFivoid 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());}

Handling WebSockets – Server

Previously, you’ve seen how to handle the WebSocket connection on the client side (browser). Now, let’s take a look on how to handle it on the server side.

Notify All Clients

ThenotifyClients() function notifies all clients with a message containing whatever you pass as a argument. In this case, we’ll want to notify all clients of the current motor state whenever there’s a change.

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

TheAsyncWebSocket class provides atextAll() method for sending the same message to all clients that are connected to the server at the same time.

Handle WebSocket Messages

ThehandleWebSocketMessage() function is a callback function that will run whenever we receive new data from the clients via WebSocket protocol.

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);    newRequest = true;  }}

We split the message to get the number of steps and direction.

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

Then, we notify all clients of the motor direction so that all clients change the motor state on the web interface.

notifyClients(direction);

Finally, set thenewRequest variable totrue, so that the motors starts spinning in theloop().

newRequest = true;

Configure the WebSocket server

Now we need to configure an event listener to handle the different asynchronous steps of the WebSocket protocol. This event handler can be implemented by defining theonEvent() as follows:

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;  }}

Thetype argument represents the event that occurs. It can take the following values:

  • WS_EVT_CONNECT when a client has logged in;
  • WS_EVT_DISCONNECT when a client has logged out;
  • WS_EVT_DATA when a data packet is received from the client;
  • WS_EVT_PONG in response to a ping request;
  • WS_EVT_ERROR when an error is received from the client.

There’s a section to notify any client of the current motor state when it first connects:

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;

Initialize WebSocket

Finally, theinitWebSocket() function initializes the WebSocket protocol.

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

setup()

In thesetup(), initialize the Serial Monitor.

Serial.begin(115200);

Call theinitWiFi() function to initialize WiFi.

initWiFi();

Call theinitLittleFS() function to initialize the filesystem.

initWebSocket();

And set the stepper motor speed in rpm.

myStepper.setSpeed(5);

Handle requests

Then, handle the web server. When you receive a request on the root (/) URL—this is when you access the ESP IP address— send the HTML text to build the web page:

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

When the HTML file loads on your browser, it will make a request for the CSS and JavaScript files. These are static files saved on the same directory (LittleFS). So, we can simply add the following line to serve files in a directory when requested by the root URL. It will serve the CSS and JavaScript files automatically.

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

Finally, start the server.

server.begin();

loop()

Let’s take a look at theloop() section.

If thenewRequest variable istrue, we’ll check what’s the spinning direction:CW orCCW. If it isCW, we move the motor the number of steps saved in thesteps variable using thestep() method on themyStepper object. To move the motor counterclockwise, we just need to pass the number of steps but with a minus () sign.

if (direction == "CW"){  // Spin the stepper clockwise direction  myStepper.step(steps.toInt());}else{  // Spin the stepper counterclockwise direction  myStepper.step(-steps.toInt());}

After spinning the motor, set thenewRequest variable tofalse, so that it can detect new requests again.

newRequest = false;

Additionally, notify all clients that the motor has stopped.

notifyClients("stop");

Upload Code and Files

After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.

Arduino IDE Open Sketch Folder to create data folder

Inside that folder, you should save the HTML, CSS, and JavaScript files.

Then, upload the code to your ESP32 board. Make sure you have the right board and COM port selected. Also, make sure you’ve added your network credentials.

Arduino 2.0 Upload Button

After uploading the code, you need to upload the files.

Press [Ctrl] + [Shift] + [P] on Windows or [] + [Shift] + [P] on MacOS to open the command palette. Search for the Upload LittleFS to Pico/ESP8266/ESP32 command and click on it.

If you don’t have this option is because you didn’t install the filesystem uploader plugin. Check this tutorial.

ESP32 Sketch Data Upload LittleFS Arduino IDE

Important: make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP32 EN/RST button, and it should print the ESP32 IP address.

Demonstration

Open a web browser or multiple web browser windows on your local network and you’ll access the web page to control the motor. Submit the form to control the motor.

ESP32 Stepper Motor Web Server Websocket Motor Spinning

The gear on the web page starts spinning in the right direction and the motor starts working.

28BYJ-48 connected to ULN2003 Motor Driver 01 module

When it stops, the gear on the web page and the motor state change accordingly.

ESP32 Stepper Motor Web Server Websocket Motor Stopped

Notice that if you have multiple clients connected, all clients update the motor state almost instantaneously.

Watch the video below for a live demonstration.


Wrapping Up

In this tutorial, you’ve learned how to control a stepper motor using a web server built with the ESP32. The web server provides a web page to control the stepper motor using a form whose results are sent to the ESP32 via WebSocket protocol.

This is part 3 of a series of tutorials about controlling a stepper motor using a web server. You can follow Part 1 and 2 at the following link:

If you want to learn more about HTML, CSS, JavaScript, and client-server communication protocols to build your ESP32 and ESP8266 web servers from scratch as we’ve done in this tutorial, make sure you take a look at our eBook:

We hope you find this tutorial useful.

Thanks for reading.



SMART HOME with Raspberry Pi ESP32 and ESP8266 Node-RED InfluxDB eBook
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects »Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

29 thoughts on “ESP32 Web Server: Control Stepper Motor (WebSocket)”

  1. Can you help me ?

    ESP32_Stepper_Motor_Websocket:141:6: error: ‘class AsyncWebSocket’ has no member named ‘cleanupClients’

    ws.cleanupClients();

    Reply
    • Hi.
      What’s the ESP32 boards version that you have? Did you install all the required libraries?
      The code compiles just fine for me. I’m using ESP32 boards version 1.0.6.
      Go to Tools > Board > Boards Manager search for “ESP32” and check the version you have installed.
      Regards,
      Sara

      Reply
      • Thank you for your response.
        I am working with Arduino ide version 1.8.12.
        I also use ESP32 boards version 1.0.6
        The esp32 is a wroom.
        The Esp8266 I used as a test is a 12F version.
        With both sketches I kept getting the same error message.
        Strangely enough, both sketches work fine when I use the ws.cleanupClients(); rule disable.

        Reply
  2. Hi there. Your tutorials are priceless. Thanks for all the good work you do.

    Reply
  3. Great tutorial! Can I to use my own local gif animation instead of a gear?

    Reply
  4. Enjoyed your presentation.
    I am using web sockets to control a stepper motor also. I am trying to implement an emergency stop button, however while the stepper motor move function is running. The web socket will not process any new request until the stepper motor stops. Did you the same issue?

    Reply
  5. Hello,

    I tried your project. It works but sometimes I have this message in the serial monitor :

    CWsteps10
    directionCW
    CWWebSocket client #5 disconnected
    Guru Meditation Error: Core 1 panic’ed (LoadProhibited). Exception was unhandled.
    Core 1 register dump:
    PC : 0x40154ac3 PS : 0x00060e30 A0 : 0x800d4956 A1 : 0x3ffb1ee0
    A2 : 0x3ffb1f2c A3 : 0x00000001 A4 : 0x00000003 A5 : 0x3ffb1e00
    A6 : 0x00000000 A7 : 0x3ffb0060 A8 : 0x00060023 A9 : 0x3ffb8058
    A10 : 0x00000000 A11 : 0x00000000 A12 : 0x8008dd84 A13 : 0x3ffd0a20
    A14 : 0x00000000 A15 : 0x3ffb0060 SAR : 0x0000000a EXCCAUSE: 0x0000001c
    EXCVADDR: 0x00000001 LBEG : 0x4008b74c LEND : 0x4008b768 LCOUNT : 0xffffffff

    ELF file SHA256: 0000000000000000

    Backtrace: 0x40154ac3:0x3ffb1ee0 0x400d4953:0x3ffb1f00 0x400d4a6f:0x3ffb1f20 0x400d4ab5:0x3ffb1f60 0x400d10d6:0x3ffb1f80 0x400de2b5:0x3ffb1fb0 0x4008db72:0x3ffb1fd0

    Rebooting…
    ets Jun 8 2016 00:22:57

    rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1216
    ho 0 tail 12 room 4
    load:0x40078000,len:10944
    load:0x40080400,len:6360
    entry 0x400806b4
    E (96) psram: PSRAM ID read error: 0xffffffff
    Connecting to WiFi …..192.168.1.92
    SPIFFS mounted successfully
    WebSocket client #1 connected from 192.168.1.55

    My ESP is an ESP32MCU-Node

    What is the problem ?

    Regards

    Reply
  6. Hi,
    excellent tutorials as always. Keep up the good work.

    Regarding stepper motors – my stepper motor has the following 6 wires: blue, pink, yellow, orange, red, red.

    Is there a way to figure which wires go to which connections?
    Thanks
    David

    Reply
  7. Hi,
    excellent tutorial as always. Keep up the good work.
    regarding the stepper motor control, can this be controlled from anywhere, I got the assignment to be able to control it from anywhere, thank you

    Reply
  8. Dear RANDOM NERD TUTORIALS!

    Can you please tell me what version of the Arduino IDE I need to have to make your program work?

    Is there a chance that it can work with newest Arduino IDE 2.3.2 too?

    Reply
  9. Thank you for the tutorial!
    I just have one question, my motor stops shortly after starting and only starts again when the set cycles are about to end. What could it be?

    Regards
    Marco

    Reply
  10. Hello Sara,
    I have a problem with ESPAsyncWebServer by Iacamera:

    I get an Error with ESPAsyncWebServer 3.1.2 and also with 3.1.0 .
    I tried Arduino IDE 1.8.19, 2.3.0, 2.3.2 and 2.3.3 on different PC’s.

    Are you using the same ESPAsyncWebServer?
    if not, how can I get another make?

    Reply
  11. Hi Sarah and Rui, thanks again for another great tutorial. I used some of this code to implement radio buttons. I believe there is an error in your raw text file, s/b
    var direction;
    https://raw.githubusercontent.com/RuiSantosdotme/Random-Nerd-Tutorials/master/Projects/ESP32/ESP32_Stepper_Motor_Websocket/data/script.js

    function submitForm(){
    const rbs = document.querySelectorAll(‘input[name=”direction”]’);
    direction;
    ….

    Reply
  12. Hi,
    Thanks for this great tutorial.
    Unfortunately I get this error message when compiling:
    …libraries/ESP_Async_WebServer/AsyncWebSocket.cpp.o:(.literal._ZN17AsyncWebLockGuardC5ERK12AsyncWebLockGuardC5ERK12AsyncWebLock[_ZN17AsyncWebLockGuardC5ERK12AsyncWebLock]+0x0): undefined reference to `pxCurrentTCB’

    Greetings from Germany
    Willi

    Reply
    • Hi.
      Try to downgrade the ESP32 boards version to 3.0.7.
      Then, let me know if that fixes the issue.
      Regards,
      Sara

      Reply
      • I tried to delete my posting because I made a mistake and that’s why it didn’t work.
        Unfortunately I can’t delete unreviewed posts.
        Can you please do that?
        I apologize for that.
        It works fine now.
        Thank you for your work.

        Reply
  13. Hi Sara,

    I succeeded to run this project, but i want to notify that pin 19, pin 18, pin 5 and pin 17 are not in the same place in my board. I have checked with the pinout ESP32 NodeMCU in the web.

    Thanks

    Jose Michel

    Reply

Leave a CommentCancel reply

Learn ESP32

ESP32 Introduction

ESP32 Arduino IDE

ESP32 Arduino IDE 2.0

VS Code and PlatformIO

ESP32 Pinout

ESP32 Inputs Outputs

ESP32 PWM

ESP32 Analog Inputs

ESP32 Interrupts Timers

ESP32 Deep Sleep

Protocols

ESP32 Web Server

ESP32 LoRa

ESP32 BLE

ESP32 BLE Client-Server

ESP32 Bluetooth

ESP32 MQTT

ESP32 ESP-NOW

ESP32 Wi-Fi

ESP32 WebSocket

ESP32 ESP-MESH

ESP32 Email

ESP32 Text Messages

ESP32 HTTP GET POST

HTTP GET Web APIs

HTTP POST Web APIs

Server-Sent Events

Web Servers

Output Web Server

PWM Slider Web Server

PWM Multiple Sliders Web Server

Async Web Server

Relay Web Server

Servo Web Server

DHT Web Server

BME280 Web Server

BME680 Web Server

DS18B20 Web Server

LoRa Web Server

Plot/Chart Web Server

Chart Multiple Series Web Server

SPIFFS Web Server

Thermostat Web Server

Momentary Switch Web Server

Physical Button Web Server

Input Fields Web Server

Images Web Server

RGB LED Web Server

Timer/Pulse Web Server

HTTP Auth Web Server

MPU-6050 Web Server

MicroSD Card Web Server

Stepper Motor Web Server

Stepper Motor WebSocket

Gauges Web Server

DIY Cloud

ESP32 Weather Station

Control GPIOs

View Sensor Readings

ESP32 MySQL

ESP32 PHP Email

ESP32 SIM800L

Cloud Node-RED Dashboard

Cloud MQTT Broker

ESP32 Cloud MQTT

ESP-NOW

ESP-NOW Introduction

ESP-NOW Two-Way

ESP-NOW One-to-Many

ESP-NOW Many-to-One

ESP-NOW + Wi-Fi Web Server

Firebase

Firebase Realtime Database

Firebase Web App

Firebase Authentication

Firebase BME280

Firebase Web App Sensor Readings

Firebase ESP32 Data Logging

Modules

ESP32 Relay Module

ESP32 DC Motors

ESP32 Servo

ESP32 Stepper Motor

ESP32 MicroSD Card

ESP32 MicroSD Card Data Logging

ESP32 PIR

ESP32 HC-SR04

ESP32 I2C Multiplexer

Sensors

ESP32 DHT11/DHT22

ESP32 BME280

ESP32 BME680

ESP32 DS18B20

ESP32 Multiple DS18B20

ESP32 BMP180

ESP32 BMP388

MQTT DHT11/DHT22

MQTT BME280

MQTT BME680

MQTT DS18B20

ESP32 MPU-6050

Displays

ESP32 OLED

ESP32 LCD

OLED Temperature

ESP32 Features

ESP32 Hall Sensor

ESP32 Touch Sensor

ESP32 I2C

ESP32 Flash Memory

ESP32 Dual Core

Useful Guides

ESP32 Troubleshooting

ESP32 Access Point

ESP32 Fixed IP Address

ESP32 MAC Address

ESP32 Hostname

ESP32 OTA

ESP32 OTA Arduino

ESP32 OTA VS Code

ESP32 Solar Panels

ESP32 Alexa

ESP32 Install SPIFFS

ESP32 Time and Date

ESP32 Epoch Time

ESP32 Google Sheets

ESP32 Email Altert

ESP32 ThingSpeak

Weather Station Shield

ESP32 IoT Shield

ESP32 Weather Station PCB

ESP32 Wi-Fi Manager

VS Code and PlatformIO

VS Code SPIFFS

VS Code Workspaces

Save Data Preferences Library

Reconnect to Wi-Fi

Useful Wi-Fi Functions

Other Projects

Telegram Control Outputs

Telegram Sensor Readings

Telegram Detect Motion

Telegram Group

ESP32 Status PCB

ESP32 BMP388 Datalogger

ESP32 Web Serial

ESP32 Door Monitor

ESP32 Door Telegram

ESP32 NTP Timezones

ESP32 Boards

ESP32 Camera

ESP32 LoRa

ESP32 OLED

ESP32 SIM800L

Learn More

Learn ESP32

Learn ESP8266

Learn ESP32-CAM

Learn MicroPython

Learn Arduino

Build Web Servers eBook

Smart Home eBook

Firebase Web App eBook

ESP32 Premium Course

Affiliate Disclosure:Random Nerd Tutorials is a participant in affiliate advertising programs designed to provide a means for us to earn fees by linking to Amazon, eBay, AliExpress, and other sites. We might be compensated for referring traffic and business to these companies.



Learn ESP32 with Arduino IDE eBook » Complete guide to program the ESP32 with Arduino IDE!



SMART HOME with Raspberry Pi, ESP32, and ESP8266 » learn how to build a complete home automation system.



Learn Raspberry Pi Pico/Pico W with MicroPython​ » The complete getting started guide to get the most out of the the Raspberry Pi Pico/Pico W (RP2040) microcontroller board using MicroPython programming language.



🔥 Learn LVGL: Build GUIs for ESP32 Projects​ » Learn how to build Graphical User Interfaces (GUIs) for ESP32 Projects using LVGL (Light Versatile Graphics Library) with the Arduino IDE.

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.


[8]ページ先頭

©2009-2025 Movatter.jp