ESP32 Servo: Điều khiển qua Internet bằng Web Server

Trong bài hướng dẫn này, mình sẽ hướng dẫn bạn làm việc với ESP32 Servo nhé! Cụ thể, chúng ta sẽ tạo một Web Server có thể điều khiển Servo bằng thanh trượt (slider). 

Để dễ hơn cho bạn thì đầu tiên IoTZone sẽ giới thiệu nhanh về cách điều khiển Servo qua ESP 32, rồi chúng ta mới đi xây dựng Web Server.

Chuẩn bị phần cứng

  • Mạch ESP32 (mình dùng loại ESP32 DOIT DEVKIT V1 Board)
  • Động cơ Servo (mình dùng loại Micro Servo S0009 hoăc Servo S0003)
  • Dây cắm Jumper

Kết nối

Servo có 3 dây cắm khác nhau: dây nguồn (power), dây nối đất (ground) và dây tín hiệu (signal). Mỗi dây có mỗi màu như hình:

Làm quen với ESP32 Servo
Làm quen với ESP32 Servo

Đôi khi, dây tín hiệu có thể là màu trắng hoặc màu vàng, màu cam. Nếu bạn dùng Servo S0009 như của mình, bạn có thể nối Servo trực tiếp vào mạch ESP32 nhé!

Còn trong trường hợp sử dụng Servo loại khác, đôi khi bạn sẽ cần thêm nguồn cấp điện bên ngoài, tùy vào loại Servo bạn dùng.

Thứ tự kết nối dây Servo đến mạch ESP32 như sau:

  • Dây GND của Servo -> Cổng GND của ESP32
  • Dây nguồn của Servo -> Cổng VIN của ESP32
  • Dây tín hiệu của Servo -> GPIO 13 (hoặc bất kỳ cổng xuất PWM nào khác)
Kết nối Servo với mạch ESP32 qua GPIO 13
Kết nối Servo với mạch ESP32 qua GPIO 13

Lưu ý: Bạn có thể sử dụng bất kỳ cổng GPIO nào trên mạch ESP 32 trong trường hợp này, vì GPIO đa phần đều hỗ trợ xuất tín hiệu PWM. Tuy nhiên, IoTZone không khuyến khích bạn sử dụng cổng GPIO 9, 10 và 11; vì các cổng này thông thường đều đã được kết nối tới đèn Flash SPI tích hợp sẵn trên mạch.

Hướng dẫn cách điều khiển Servo

Các loại Servo thông thường sẽ hỗ trợ quay từ 0º đến 180º, và chúng được điều khiển bằng tín hiệu PWM. Nhờ vào đó, chúng có thể quay tới một góc chính xác.

Servo có thể quay góc chính xác từ 0º đến 180º
Servo có thể quay góc chính xác từ 0º đến 180º

Để điều khiển Servo, chúng ta chỉ cần lập trình ESP32 gửi xung tín hiệu 50Hz với độ rộng phù hợp. Bây giờ đã có khá nhiều thư viện trong ESP32 hỗ trợ việc này. Cùng xem qua hướng dẫn điều khiển Servo bên dưới nhé!

Nạp chương trình bằng Arduino IDE

Chuẩn bị

Nếu Arduino IDE của bạn chưa hỗ trợ mạch ESP32, bạn hãy cài tiện ích ESP32 trong phần mềm theo hướng dẫn sau nhé: Cách lập trình ESP32 bằng Arduino IDE (Windows, Linux, Mac OS X)

Cài thư viện ESP32_Arduino_Servo_LIbrary

Như mình đã giới thiệu phía trên, đây là thư viện giúp chúng ta dễ dàng điều khiển Servo hơn. Bạn hãy cài thư viện theo hướng dẫn bên dưới:

  • Tải thư viện qua đường link sau: ESP32_Arduino_Servo_Library
  • Giải nén thư mục vừa tải về
  • Đổi tên thư mục thành ESP32_Arduino_Servo_Library
  • Di chuyển thư mục vào folder thư viện của Arduino IDE
  • Tắt và mở lại Arduino IDE

Link chương trình full

Trong thư viện đã có sẵn đoạn chương trình điều khiển Servo, bạn hãy nhấn vào File >> Examples >> ServoESP32 >> Simple Servo nhé:

#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0;    // variable to store the servo position

void setup() {
  myservo.attach(13);  // attaches the servo on pin 13 to the servo object
}

void loop() {
  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
  for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }
}

1

Chương trình trên giúp cho Servo quay sang góc 0 độ rồi quay tới góc 180 độ, lặp lại liên tục.

Vậy là đã xong, giờ IoTZone sẽ hướng dẫn bạn cách xây dựng Web Server nhé!

Giới thiệu về Web Server ESP32

Để điều khiển ESP32 Servo, chúng ta sẽ làm một Web Server như sau:

  • Có 1 thanh trượt từ 0 đến 180 để điều chỉnh góc quay Servo
  • Giá trị thanh trượt này sẽ tự động cập nhật liên tục mà không cần người dùng F5 lại trang Web
  • Việc F5 trang Web không làm thay đổi giá trị góc trên thanh trượt đang có
Cách Xây dựng Web Server ESP32

Cách tạo Web Server điều khiển ESP32 Servo

Tạo trang HTML

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
    body {
      text-align: center;
      font-family: "Trebuchet MS", Arial;
      margin-left:auto;
      margin-right:auto;
    }
    .slider {
      width: 300px;
    }
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
  <h1>ESP32 with Servo</h1>
  <p>Position: <span id="servoPos"></span></p>
  <input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>
  <script>
    var slider = document.getElementById("servoSlider");
    var servoP = document.getElementById("servoPos");
    servoP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      servoP.innerHTML = this.value;
    }
    $.ajaxSetup({timeout:1000});
    function servo(pos) {
      $.get("/?value=" + pos + "&");
      {Connection: close};
    }
  </script>
</body>
</html>
1

Tạo thanh trượt

Để tạo thanh trượt trong Web HTML, chúng ta sử dụng thẻ <input>, đây là thẻ cho phép người dùng thay đổi giá trị dữ liệu.

Mình sẽ sử dụng thuộc tính type range để cấu hình tạo một thanh trượt. Ngoài ra, chúng ta cũng cần dùng min (0) và max (180) để xác định góc quay tối đa cho ESP32 Servo:

<input type="range" min="0" max="180" class="slider" id="servoSlider" onchange="servo(this.value)"/>

Ngoài ra thì cũng có một số thuộc tính phụ như:

  • class điều khiển thanh trưọt
  • id xác định vị trí hiển thị của thanh trượt trên Web Server hiện tại
  • onchange gọi hàm Servo để gửi yêu cầu HTTP đến mạch ESP32 mỗi khi người dùng thay đổi giá trị của thanh trượt

Thêm JavaScript

Bước tiếp theo là chúng ta cần dùng code Javascript để cập nhật vị trí mới nhất của Slider:

var slider = document.getElementById("servoSlider");
    var servoP = document.getElementById("servoPos");
    servoP.innerHTML = slider.value;
    slider.oninput = function() {
      slider.value = this.value;
      servoP.innerHTML = this.value;
    }

Gửi yêu cầu HTTP GET trên địa chỉ IP ESP, theo một URL cụ thể:

$.ajaxSetup({timeout:1000});
function servo(pos) {
  $.get("/?value=" + pos + "&");
}

Ví dụ, khi slider ở vị trí 0, bạn sẽ nhận được yêu cầu HTTP GET qua URL như sau:

http://192.168.1.135/?value=0&

Tương tự với 180 độ. Mỗi khi ESP32 nhận được yêu cầu này, chúng sẽ điều khiển Servo đến đúng giá trị như trong URL đã hướng dẫn.

Chương trình lập trình cho ESP32 Servo

Bây giờ, chúng ta cần tích hợp Web HTML đã tạo vào chương trình điều khiển ESP32 Servo, để các thiết bị phần cứng này hoạt động sao cho phù hợp.

Dưới đây là code mẫu full:

/*********
  Rui Santos
  Complete project details at http://randomnerdtutorials.com  
*********/

#include <WiFi.h>
#include <Servo.h>

Servo myservo;  // create servo object to control a servo
// twelve servo objects can be created on most boards

// GPIO the servo is attached to
static const int servoPin = 13;

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

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Decode HTTP GET value
String valueString = String(5);
int pos1 = 0;
int pos2 = 0;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0; 
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

void setup() {
  Serial.begin(115200);

  myservo.attach(servoPin);  // attaches the servo on the servoPin to the servo object

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
            client.println(".slider { width: 300px; }</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
                     
            // Web Page
            client.println("</head><body><h1>ESP32 with Servo</h1>");
            client.println("<p>Position: <span id=\"servoPos\"></span></p>");          
            client.println("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" value=\""+valueString+"\"/>");
            
            client.println("<script>var slider = document.getElementById(\"servoSlider\");");
            client.println("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;");
            client.println("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }");
            client.println("$.ajaxSetup({timeout:1000}); function servo(pos) { ");
            client.println("$.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
           
            client.println("</body></html>");     
            
            //GET /?value=180& HTTP/1.1
            if(header.indexOf("GET /?value=")>=0) {
              pos1 = header.indexOf('=');
              pos2 = header.indexOf('&');
              valueString = header.substring(pos1+1, pos2);
              
              //Rotate the servo
              myservo.write(valueString.toInt());
              Serial.println(valueString); 
            }         
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}
1

Giải thích chương trình

Khai báo thư viện và tạo đối tượng là myservo:

#include <Servo.h> 
Servo myservo; // create servo object to control a servo

Tạo biến để khai báo cổng mà Servo đang kết nối (GPIO 13):

const int servoPin = 13;

Sửa đổi thông tin đăng nhập vào mạng WiFi của bạn sao cho phù hợp:

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

Tạo biến để theo dõi vị trí của thanh trượt, dựa trên yêu cầu HTTP

// Decode HTTP GET value 
String valueString = String(5); 
int pos1 = 0; 
int pos2 = 0;

Setting()

Gắn Servo vào chân GPIO tương ứng:

myservo.attach(servoPin); // attaches the servo on the servoPin to the servo object

loop()

Tạo Web Server và gửi HTML để trang Web có thể hiển thị. Sau đó, lấy giá trị của thanh trượt:

//GET /?value=180& HTTP/1.1 if(tiêu đề.indexOf("GET /?value=")>=0) { pos1 = tiêu đề.indexOf('= 39;);   pos2 = tiêu đề.indexOf('&');   valueString = tiêu đề.chuỗi con(pos1+1, pos2); 

Khi thanh trượt thay đổi, yêu cầu HTTP được gửi qua URL:

http://your-esp-ip-address/?value=[SLIDER_POSITION]&

Giá trị thanh trượt được lưu trong biến valueString.

Điều khiển ESP32 Servo đến vị trí cụ thể:

myservo.write(valueString.toInt());

Nạp chương trình và xem demo

Bây giờ, bạn hãy upload code vào mạch ESP32 của mình nhé! Sau đó bật Serial Monitor ở tốc độ 115200 để theo dõi.

Bước tiếp theo bạn hãy khởi động lại ESP32 và lấy địa chỉ IP trên Serial Monitor. Dán IP này vào trình duyệt trên điện thoại, bạn sẽ thấy Web Server đã tạo:

Nạp chương trình và điều khiển ESP32 Servo

Bạn hãy thử kéo thanh trượt qua lại và xem phản ứng của Servo nhé!

Chúng ta cũng có thể theo dõi yêu cầu HTTP được gửi đi như thế nào khi di chuyển thanh trượt.

Lời kết

Trong hướng dẫn này, mình đã giới thiệu cách điều khiển ESP32 Servo thông qua Web Server, dựa trên mạng Internet. Đây chỉ là một hướng dẫn cơ bản, bạn có thể sáng tạo và biến tấu nâng cao hơn tùy thích, ví dụ như sử dụng khung nhập văn bản thay vì thanh trượt. Chúc các bạn thành công.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *