ESP8266-Arduino网络编程实例-Web页面控制步进电机 Web页面控制步进电机 本文将演示如何通过Web页面控制步进电机步数及方向。
客户端与服务器通过WebSocket协议通信。示例实现了如下功能:
Web页面显示一个表格,可以在其中输入希望电机移动的步数并选择方向:顺时针或逆时针。
显示电机状态:电机旋转或电机停止。 此外,只要电机旋转,就会有一个齿轮图标旋转。 齿轮根据所选方向顺时针或逆时针方向旋转。
1、硬件准备
ESP8266 NodeMCU开发板一块
数据线一条
UL2003N步进电机驱动模块
28BYJ-48 Stepper Motor步进电机
杜邦线若干
5V电源
硬件接线如下:
Motor Driver
ESP8266
IN1
GPIO 5
IN2
GPIO 4
IN3
GPIO 14
IN4
GPIO 12
2、软件准备
Arduino IDE或VSCode + PlatformIO
在前面的文章中,对如何搭建ESP8266开发环境做了详细的介绍,请参考:
ESP8266 NodeMCU的引脚介绍在前面的文章中做了详细的介绍,请参考:
3、代码实现 1)ESP8266服务器
本次实例使用到如下驱动库:
1)ESP8266服务器实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 #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 = ""; const char* ssid = "****"; const char* ssid_pwd = "****"; // 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(); }
2)Web页面(index.html)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <!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>
3)Web页面样式文件(style.css)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 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); } }
4)Web页面逻辑控制(script.js)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 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"); } } }
运行结果:
文章来源: https://iotsmart.blog.csdn.net/article/details/127481607
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!