Arduino - Control Servo Motor via Web
This tutorial will show you how to use an Arduino to control a servo motor from a web browser on your smartphone or PC. We'll utilize WebSocket technology to enable smooth and dynamic control of the servo motor through a graphical web user interface.
Now, why should we use WebSocket? Here’s why:
Without WebSocket, every time you need to adjust the angle of the servo, you would have to refresh the webpage. This is not ideal.
However, with WebSocket, we establish a dedicated connection between the webpage and the Arduino. This setup allows us to transmit the angle value to the Arduino in the background, without needing to reload the page. As a result, the servo operates smoothly and responds in real-time. Pretty impressive, right?
Let’s get started!
Or you can buy the following sensor kits:
Disclosure: Some links in this section are Amazon affiliate links. If you make a purchase through these links, we may earn a commission at no extra cost to you.
Additionally, some links direct to products from our own brand, DIYables.
We have specific tutorials about servo motor and WebSocket. Each tutorial contains detailed information and step-by-step instructions about hardware pinout, working principle, wiring connection to Arduino, Arduino code... Learn more about them at the following links:
The Arduino code sets up both a web server and a WebSocket server. Here's the step-by-step process:
When you type the Arduino's IP address into a web browser, it sends a request for the webpage (User Interface) hosted on the Arduino.
The Arduino's web server responds by sending back the content of the webpage (HTML, CSS, JavaScript).
Your web browser then displays the webpage.
The JavaScript code embedded in the webpage initiates a WebSocket connection with the WebSocket server on the Arduino.
Once the WebSocket connection is active, if you adjust the handle on the webpage, the JavaScript code discreetly transmits the angle value to the Arduino via this WebSocket connection in the background.
The WebSocket server on the Arduino, upon receiving this angle value, adjusts the servo motor accordingly.
In essence, the WebSocket connection facilitates smooth, real-time control of the servo motor's angle.
This image is created using Fritzing. Click to enlarge image
For the sake of simplicity, the above wiring diagram is used for the testing or learning purposes, and for small-torque servo motor. In practice, we highly recommend using the external power supply for the servo motor. The below wiring diagram shows how to connect servo motor to an external power source.
The webpage's content (HTML, CSS, JavaScript) are stored separately on an index.h file. So, we will have two code files on Arduino IDE:
An .ino file that is Arduino code, which creates a web sever and WebSocket Server, and control servo motor
An .h file, which contains the webpage's content.
Do the wiring as above image.
Connect the Arduino board to your PC via a micro USB cable
Open Arduino IDE on your PC.
Select the right Arduino board (e.g. Arduino Uno R4 WiFi) and COM port.
Open the Library Manager by clicking on the Library Manager icon on the left navigation bar of Arduino IDE.
Search “mWebSockets”, then find the mWebSockets created by Dawid Kurek.
Click Install button to install mWebSockets library.
On Arduino IDE, create new sketch, Give it a name, for example, ArduinoGetStarted.com.ino
Copy the below code and open with Arduino IDE
#include <Servo.h>
#include <WiFiS3.h>
#include <WebSocketServer.h>
#include "index.h"
using namespace net;
#define SERVO_PIN 9
Servo servo;
const char *ssid = "YOUR_WIFI_SSID";
const char *password = "YOUR_WIFI_PASSWORD";
WebSocketServer webSocket(81);
WiFiServer server(80);
int status = WL_IDLE_STATUS;
void setup() {
Serial.begin(9600);
servo.attach(SERVO_PIN);
String fv = WiFi.firmwareVersion();
if (fv < WIFI_FIRMWARE_LATEST_VERSION)
Serial.println("Please upgrade the firmware");
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, password);
delay(4000);
}
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
server.begin();
webSocket.onConnection([](WebSocket &ws) {
const auto protocol = ws.getProtocol();
if (protocol) {
Serial.print(F("Client protocol: "));
Serial.println(protocol);
}
ws.onMessage([](WebSocket &ws, const WebSocket::DataType dataType,
const char *message, uint16_t length) {
switch (dataType) {
case WebSocket::DataType::TEXT:
{
String angle = String((char *)message);
int angle_value = angle.toInt();
servo.write(angle_value);
Serial.print(F("Rotate Servo Motor to "));
Serial.println(angle_value);
}
break;
case WebSocket::DataType::BINARY:
Serial.println(F("Received binary data"));
break;
}
});
ws.onClose([](WebSocket &, const WebSocket::CloseCode, const char *,
uint16_t) {
Serial.println(F("Disconnected"));
});
Serial.print(F("New WebSocket Connnection from client: "));
Serial.println(ws.getRemoteIP());
});
webSocket.begin();
}
void loop() {
webSocket.listen();
WiFiClient client = server.available();
if (client) {
while (client.connected()) {
if (client.available()) {
String HTTP_header = client.readStringUntil('\n');
if (HTTP_header.equals("\r"))
break;
Serial.print("<< ");
Serial.println(HTTP_header);
}
}
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
String html = String(HTML_CONTENT);
client.println(html);
client.flush();
delay(50);
client.stop();
}
}
Modify the WiFi information (SSID and password) in the code to match your own network credentials.
Create the index.h file On Arduino IDE by:
const char *HTML_CONTENT = R"=====(
<!DOCTYPE html>
<html>
<head>
<title>Arduino Controls Servo Motor via Web</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7">
<style>
body { text-align: center; }
canvas { background-color: #ffffff; }
</style>
<script>
var canvas_width = 401, canvas_height = 466;
var pivot_x = 200, pivot_y = 200;
var bracket_radius = 160, bracket_angle = 0;
var bracket_img = new Image();
var click_state = 0;
var last_angle = 0;
var mouse_xyra = {x:0, y:0, r:0.0, a:0.0};
var ws;
bracket_img.src = "https://esp32io.com/images/tutorial/servo-bracket.png";
function init()
{
var servo = document.getElementById("servo");
servo.width = canvas_width;
servo.height = canvas_height;
servo.style.backgroundImage = "url('https://esp32io.com/images/tutorial/servo-body.png')";
servo.style.backgroundPosition = "center";
servo.style.backgroundSize = "contain";
servo.addEventListener("touchstart", mouse_down);
servo.addEventListener("touchend", mouse_up);
servo.addEventListener("touchmove", mouse_move);
servo.addEventListener("mousedown", mouse_down);
servo.addEventListener("mouseup", mouse_up);
servo.addEventListener("mousemove", mouse_move);
var ctx = servo.getContext("2d");
ctx.translate(pivot_x, pivot_y);
rotate_bracket(0);
ws = new WebSocket("ws:
document.getElementById("ws_state").innerHTML = "CONNECTING";
ws.onopen = function(){ document.getElementById("ws_state").innerHTML = "CONNECTED" };
ws.onclose = function(){ document.getElementById("ws_state").innerHTML = "CLOSED"};
ws.onerror = function(){ alert("websocket error " + this.url) };
ws.onmessage = ws_onmessage;
}
function ws_onmessage(e_msg)
{
e_msg = e_msg || window.event;
alert("msg : " + e_msg.data);
}
function rotate_bracket(angle)
{
var servo = document.getElementById("servo");
var ctx = servo.getContext("2d");
ctx.clearRect(-pivot_x, -pivot_y, canvas_width, canvas_height);
ctx.rotate(angle / 180 * Math.PI);
ctx.drawImage(bracket_img, -pivot_x, -pivot_y);
ctx.rotate(-angle / 180 * Math.PI);
}
function check_range_xyra(event, mouse_xyra)
{
var x, y, r, a, rc_x, rc_y, radian;
var min_r, max_r, width;
if(event.touches)
{
var touches = event.touches;
x = (touches[0].pageX - touches[0].target.offsetLeft) - pivot_x;
y = pivot_y - (touches[0].pageY - touches[0].target.offsetTop);
min_r = 60;
max_r = pivot_x;
width = 40;
}
else
{
x = event.offsetX - pivot_x;
y = pivot_y - event.offsetY;
min_r = 60;
max_r = bracket_radius;
width = 20;
}
r = Math.sqrt(x * x + y * y);
a = Math.atan2(y, x);
mouse_xyra.x = x;
mouse_xyra.y = y;
mouse_xyra.r = r;
mouse_xyra.a = a;
radian = bracket_angle / 180 * Math.PI;
rc_x = x * Math.cos(radian) - y * Math.sin(radian);
rc_y = x * Math.sin(radian) + y * Math.cos(radian);
if((r < min_r) || (r > max_r))
return false;
if((rc_y < -width) || (rc_y > width))
return false;
return true;
}
function mouse_down()
{
if(event.touches && (event.touches.length > 1))
click_state = event.touches.length;
if(click_state > 1)
return;
if(check_range_xyra(event, mouse_xyra))
{
click_state = 1;
last_angle = mouse_xyra.a / Math.PI * 180.0;
}
}
function mouse_up()
{
click_state = 0;
}
function mouse_move()
{
var angle;
if(event.touches && (event.touches.length > 1))
click_state = event.touches.length;
if(click_state > 1)
return;
if(!click_state)
return;
if(!check_range_xyra(event, mouse_xyra))
{
click_state = 0;
return;
}
angle = mouse_xyra.a / Math.PI * 180.0;
if((Math.abs(angle) > 90) && (angle * last_angle < 0))
{
if(last_angle > 0)
last_angle = -180;
else
last_angle = 180;
}
bracket_angle += (last_angle - angle);
last_angle = angle;
if(bracket_angle > 90)
bracket_angle = 90;
if(bracket_angle < -90)
bracket_angle = -90;
rotate_bracket(bracket_angle);
if(ws.readyState == 1)
ws.send(Math.floor(90 - bracket_angle) + "\r\n");
debug = document.getElementById("debug");
debug.innerHTML = Math.floor(90 - bracket_angle);
event.preventDefault();
}
window.onload = init;
</script>
</head>
<body>
<h2>
Arduino - Servo Motor via Web<br>
<canvas id="servo"></canvas>
<p>
WebSocket : <span id="ws_state" style="color:blue">null</span><br>
Angle : <span id="debug" style="color:blue">90</span>
</p>
</h2>
<div class="sponsor">Sponsored by <a href="https://amazon.com/diyables">DIYables</a></div>
</body>
</html>
)=====";
You will see an error like below:
In file included from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/utility.h:3:0,
from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocket.h:5,
from c:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/WebSocketServer.h:5,
from C:\Users\YOU_ACCOUNT\Documents\Arduino\ArduinoGetStarted.com\ArduinoGetStarted.com.ino:2:
C:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/platform.h:54:12: fatal error: Ethernet.h: No such file or directory
# include <Ethernet.h>
^~~~~~~~~~~~
compilation terminated.
exit status 1
To fix this error:
Go to C:\Users\YOU_ACCOUNT\Documents\Arduino\libraries\mWebSockets\src/ directory.
Find the config.h file and open it by a text editor.
Look at the line 26, you will see it like below:
#define NETWORK_CONTROLLER ETHERNET_CONTROLLER_W5X00
#define NETWORK_CONTROLLER NETWORK_CONTROLLER_WIFI
Connecting to WiFi...
Connected to WiFi
Arduino Web Server's IP address IP address: 192.168.0.2
Take note of the IP address displayed, and enter this address into the address bar of a web browser on your smartphone or PC.
You will see the webpage it as below:
To save the memory of Arduino, the images of servo motor are NOT stored on Arduino. Instead, they are stored on the internet, so, your phone or PC need to have internet connection to load images for the web control page.
※ NOTE THAT:
If you modify the HTML content in the index.h and does not touch anything in ArduinoGetStarted.com.ino file, when you compile and upload code to Arduino, Arduino IDE will not update the HTML content.
To make Arduino IDE update the HTML content in this case, make a change in the ArduinoGetStarted.com.ino file (e.g. adding empty line, add a comment....)
The above Arduino code contains line-by-line explanation. Please read the comments in the code!
※ OUR MESSAGES
You can share the link of this tutorial anywhere. Howerver, please do not copy the content to share on other websites. We took a lot of time and effort to create the content of this tutorial, please respect our work!