Веб-сервер ESP32 для управления сервомотором в Arduino IDE

В этом руководстве вы узнаете, как создать веб-сервер на ESP32, который управляет положением вала сервомотора с помощью ползунка. Сначала мы кратко рассмотрим, как управлять сервомотором с помощью ESP32, а затем создадим веб-сервер.

Веб-сервер ESP32 для управления сервомотором в Arduino IDE

Обновлено 13 июня 2024 г.

Необходимые компоненты

Для выполнения этого руководства вам потребуются следующие компоненты:

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Подключение сервомотора к ESP32

Сервомоторы имеют три провода: питание, земля и сигнал. Провод питания обычно красный, провод GND — чёрный или коричневый, а сигнальный провод обычно жёлтый, оранжевый или белый.

Распиновка сервомотора
Назначение проводов сервомотора

Провод

Цвет

Питание

Красный

GND

Чёрный или коричневый

Сигнал

Жёлтый, оранжевый или белый

При использовании небольшого сервомотора, такого как S0009, показанного на рисунке ниже, вы можете питать его напрямую от ESP32.

Хобби-сервомоторы S0009

Но если вы используете несколько сервомоторов или другую модель, вам, вероятно, потребуется запитать сервомоторы от внешнего источника питания.

Если вы используете небольшой сервомотор, такой как S0009, вам нужно подключить:

  • GND -> вывод GND на ESP32;

  • Питание -> вывод VIN на ESP32;

  • Сигнал -> GPIO 13 (или любой вывод с поддержкой ШИМ).

Примечание: в данном случае вы можете использовать любой GPIO ESP32, потому что любой GPIO может генерировать сигнал ШИМ. Однако мы не рекомендуем использовать GPIO 9, 10 и 11, которые подключены к встроенной SPI-флеш-памяти и не рекомендуются для других целей.

Рекомендуем прочитать: :doc:`Справочник по выводам ESP32: Какие GPIO выводы следует использовать? <../esp32-pinout-reference-gpios/index>`_

Схема подключения

В наших примерах мы подключим сигнальный провод к GPIO 13. Поэтому вы можете следовать приведённой ниже схеме для подключения вашего сервомотора.

Подключение ESP32 к сервомотору SG90

(На этой схеме используется модуль ESP32 DEVKIT V1 с 36 GPIO — если вы используете другую модель, пожалуйста, проверьте распиновку для вашей платы.)

Как управлять сервомотором?

Вы можете установить вал сервомотора в различные положения от 0 до 180 градусов. Сервомоторами управляют с помощью сигнала широтно-импульсной модуляции (ШИМ). Коэффициент заполнения ШИМ-сигнала, отправляемого на мотор, определяет положение вала.

Углы поворота сервомотора от 0 до 180 градусов

Для управления мотором вы можете просто использовать возможности ШИМ ESP32, отправляя сигнал 50 Гц с соответствующей шириной импульса. Или вы можете использовать библиотеку, чтобы значительно упростить эту задачу.

ESP32 с сервомотором

Подготовка Arduino IDE

Перед началом работы убедитесь, что вы установили платы ESP32 в Arduino IDE и библиотеку ESP32Servo.

ESP32 в Arduino IDE

Мы будем программировать ESP32 с помощью Arduino IDE. Поэтому убедитесь, что у вас установлен аддон для ESP32. Если вы ещё этого не сделали, следуйте следующему руководству:

Также вы можете захотеть программировать ESP32 с помощью VS Code и расширения PlatformIO:

Установка библиотеки ESP32Servo

Библиотека ESP32Servo упрощает управление сервомотором с помощью ESP32 в Arduino IDE. Выполните следующие шаги для установки библиотеки в Arduino IDE:

  1. Перейдите в Sketch > Include Library > Manage Libraries…

  2. Найдите ESP32Servo.

  3. Установите библиотеку.

Установка библиотеки ESP32 Servo в Arduino IDE

Тестирование примера

После установки библиотеки откройте Arduino IDE и скопируйте следующий код.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-servo-motor-web-server-arduino-ide/
  Based on the ESP32Servo Sweep Example
*********/

#include <ESP32Servo.h>

static const int servoPin = 13;

Servo servo1;

void setup() {

  Serial.begin(115200);
  servo1.attach(servoPin);
}

void loop() {
  for(int posDegrees = 0; posDegrees <= 180; posDegrees++) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }

  for(int posDegrees = 180; posDegrees >= 0; posDegrees--) {
    servo1.write(posDegrees);
    Serial.println(posDegrees);
    delay(20);
  }
}

Посмотреть исходный код

Разбор кода

Этот скетч поворачивает сервомотор на 180 градусов в одну сторону и на 180 градусов в другую. Давайте разберёмся, как он работает.

Сначала нужно подключить библиотеку Servo:

#include <ESP32Servo.h>

Определите вывод, подключённый к сигнальному контакту сервомотора. В данном случае мы подключаем к GPIO 13, но вы можете использовать любой другой подходящий вывод.

static const int servoPin = 13;

Узнайте больше о GPIO ESP32: :doc:`Справочник по выводам ESP32: Какие GPIO выводы следует использовать? <../esp32-pinout-reference-gpios/index>`_

Затем нужно создать объект Servo. В данном случае он называется servo1.

Servo servo1;

setup()

В функции setup() мы инициализируем последовательное соединение для отладки и привязываем GPIO 13 к объекту servo.

void setup() {
  Serial.begin(115200);
  servo1.attach(servoPin);
}

loop()

В функции loop() мы меняем положение вала мотора от 0 до 180 градусов, а затем от 180 до 0 градусов. Чтобы установить вал в определённое положение, нужно просто использовать метод write() для объекта Servo. В качестве аргумента передаётся целое число с указанием позиции в градусах.

myservo.write(pos);

Тестирование скетча

Загрузите код на ESP32. После загрузки кода вы должны увидеть, как вал мотора вращается в одну сторону, а затем в другую.

Сервомотор в первом положении Сервомотор во втором положении Сервомотор в третьем положении

Создание веб-сервера ESP32

Теперь, когда вы знаете, как управлять сервомотором с помощью ESP32, давайте создадим веб-сервер для управления им. Веб-сервер, который мы создадим:

  • Содержит ползунок от 0 до 180, который вы можете настраивать для управления положением вала сервомотора;

  • Текущее значение ползунка автоматически обновляется на веб-странице без необходимости её обновления. Для этого мы используем AJAX для отправки HTTP-запросов к ESP32 в фоновом режиме;

  • Обновление веб-страницы не изменяет значение ползунка и положение вала.

Веб-сервер ESP32 для управления сервомотором

Создание HTML-страницы

Давайте начнём с рассмотрения HTML-текста, который ESP32 должен отправить в ваш браузер.

<!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>

Посмотреть исходный код

Создание ползунка

HTML-страница для этого проекта включает создание ползунка. Чтобы создать ползунок в HTML, используется тег <input>. Тег <input> определяет поле, в которое пользователь может ввести данные.

Существует множество типов элементов ввода. Чтобы определить ползунок, используйте атрибут «type» со значением «range». Для ползунка также необходимо определить минимальный и максимальный диапазон с помощью атрибутов «min» и «max».

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

Вам также нужно определить другие атрибуты, такие как:

  • class — для стилизации ползунка

  • id — для обновления текущей позиции, отображаемой на веб-странице

  • И, наконец, onchange — для вызова функции servo при отправке HTTP-запроса к ESP32 при перемещении ползунка.

Добавление JavaScript в HTML-файл

Далее вам нужно добавить JavaScript-код в ваш HTML-файл, используя теги <script> и </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;
}

А следующие строки выполняют HTTP GET-запрос по IP-адресу ESP с конкретным URL-путём /?value=[ПОЗИЦИЯ_ПОЛЗУНКА]&.

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

Например, когда ползунок находится на отметке 0, выполняется HTTP GET-запрос по следующему URL:

http://192.168.1.135/?value=0&

А когда ползунок находится на отметке 180 градусов, запрос будет выглядеть следующим образом:

http://192.168.1.135/?value=180&

Таким образом, когда ESP32 получает GET-запрос, он может извлечь значение параметра из URL и переместить сервомотор в соответствующее положение.

Код

Теперь нам нужно включить предыдущий HTML-текст в скетч и поворачивать сервомотор соответствующим образом. Следующий скетч делает именно это.

Скопируйте следующий код в Arduino IDE, но пока не загружайте его. Сначала мы кратко рассмотрим, как он работает.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-servo-motor-web-server-arduino-ide/
*********/

#include <WiFi.h>
#include <ESP32Servo.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("");
  }
}

Посмотреть исходный код

Как работает код

Сначала мы подключаем библиотеку Servo и создаём объект Servo с именем myservo.

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

Мы также создаём переменную для хранения номера GPIO, к которому подключён сервомотор. В данном случае — GPIO13.

const int servoPin = 13;

Не забудьте изменить следующие две строки, чтобы указать ваши сетевые данные.

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Затем создаём переменные, которые будут использоваться для извлечения позиции ползунка из HTTP-запроса.

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

setup()

В функции setup() необходимо привязать сервомотор к GPIO, к которому он подключён, с помощью myservo.attach().

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

loop()

Первая часть функции loop() создаёт веб-сервер и отправляет HTML-текст для отображения веб-страницы. Мы используем тот же метод, что и в :doc:`этом проекте веб-сервера <../esp32-web-server-arduino-ide/index>`_.

Следующая часть кода извлекает значение ползунка из HTTP-запроса.

//GET /?value=180& HTTP/1.1
if(header.indexOf("GET /?value=")>=0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);

Когда вы перемещаете ползунок, вы отправляете HTTP-запрос по следующему URL, который содержит позицию ползунка между знаками = и &.

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

Значение позиции ползунка сохраняется в переменной valueString.

Затем мы устанавливаем сервомотор в определённое положение с помощью myservo.write(), передавая переменную valueString в качестве аргумента. Переменная valueString является строкой, поэтому нам нужно использовать метод toInt() для преобразования её в целое число — тип данных, принимаемый методом write().

myservo.write(valueString.toInt());

Тестирование веб-сервера

Теперь вы можете загрузить код на ESP32 — убедитесь, что вы выбрали правильную плату и COM-порт. Также не забудьте изменить код, чтобы указать ваши сетевые данные.

После загрузки кода откройте монитор порта на скорости 115200 бод. Нажмите кнопку EN/RST на ESP32, чтобы перезагрузить плату, и скопируйте IP-адрес ESP32, который отображается в мониторе порта.

IP-адрес веб-сервера ESP32 в мониторе порта

Откройте браузер, вставьте IP-адрес ESP и вы должны увидеть веб-страницу, которую вы создали ранее. Перемещайте ползунок для управления сервомотором.

Демонстрация веб-сервера ESP32 для управления сервомотором

В мониторе порта вы также можете видеть HTTP-запросы, которые вы отправляете на ESP32 при перемещении ползунка.

Значение ползунка в мониторе порта ESP32

Поэкспериментируйте с вашим веб-сервером некоторое время, чтобы убедиться, что он работает правильно.

Смотрите видеоурок

Это руководство доступно в видеоформате (смотрите ниже) и в текстовом формате (продолжайте читать).

Заключение

Подводя итог, в этом руководстве вы узнали, как управлять сервомотором с помощью ESP32 и как создать веб-сервер с ползунком для управления его положением.

Это лишь пример управления сервомотором. Вместо ползунка вы можете использовать текстовое поле ввода, несколько кнопок с предустановленными углами или любые другие подходящие элементы ввода.

У нас есть руководства по подключению других моторов к ESP32:

Узнайте больше об ESP32 и создании веб-серверов из наших ресурсов:

Надеемся, что это руководство было для вас полезным.

Спасибо за чтение.


Источник: :doc:`ESP32 Servo Motor Web Server with Arduino IDE <../esp32-servo-motor-web-server-arduino-ide/index>`_