← До всіх проєктів
Автоматизація Початковий

Сервопривід з веб-повзунком: плавне керування без бібліотек

Керуємо сервоприводом SG90/MG996R з телефона: тягнете повзунок — вал повертається в реальному часі. ШІМ генеруємо вбудованим модулем LEDC, без сторонніх бібліотек.

Складність
Початковий
Час
45 min
Категорія
Автоматизація

Ідея

Сервопривід — основа безлічі саморобок: годівниця для кота, поворотна камера, привід жалюзі, заслінка вентиляції. Ним керують імпульсами ШІМ частотою 50 Гц: ширина імпульсу ~0.5 мс — це 0°, ~2.4 мс — 180°.

У ESP32 є апаратний модуль LEDC, який генерує такий сигнал без навантаження на процесор. Тож обійдемося без бібліотек — лише ядро arduino-esp32 3.x.

Компоненти

  • ESP32 DevKit
  • Сервопривід SG90 (малий) або MG996R (потужний, металеві шестерні)
  • Для MG996R — окреме живлення 5–6 В ≥ 2 А

Підключення

Сервопривід ESP32
Коричневий / чорний (GND) GND
Червоний (+5 V) 5V (VIN) — лише для SG90
Помаранчевий / жовтий (сигнал) GPIO 13

Потужний сервопривід при старті споживає піковий струм понад 1 А, і ESP32 перезавантажуватиметься. Живіть його окремо, об'єднавши землі. Конденсатор 470–1000 мкФ біля сервоприводу теж допоможе.

Код

#include <WiFi.h>
#include <WebServer.h>

const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";

const int SERVO_PIN = 13;
const int PWM_FREQ  = 50;     // 50 Гц → період 20 мс
const int PWM_BITS  = 14;     // роздільна здатність 0..16383
const int MIN_US    = 500;    // імпульс для 0°
const int MAX_US    = 2400;   // імпульс для 180°

WebServer server(80);
int currentAngle = 90;

void writeAngle(int angle) {
  angle = constrain(angle, 0, 180);
  int us = map(angle, 0, 180, MIN_US, MAX_US);
  uint32_t duty = (uint32_t)us * ((1 << PWM_BITS) - 1) / 20000;
  ledcWrite(SERVO_PIN, duty);
  currentAngle = angle;
}

const char PAGE[] = R"rawliteral(
<!doctype html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Servo</title>
<style>
  body{font-family:system-ui;text-align:center;margin:40px 16px}
  input{width:100%;max-width:420px;height:40px}
  #v{font-size:3rem;font-weight:700}
</style></head><body>
<h2>Servo</h2>
<div id="v">--°</div>
<input id="s" type="range" min="0" max="180">
<script>
const s = document.getElementById('s'), v = document.getElementById('v');
let busy = false, pending = null;
async function send(angle) {
  if (busy) { pending = angle; return; }
  busy = true;
  await fetch('/angle?value=' + angle);
  busy = false;
  if (pending !== null) { const a = pending; pending = null; send(a); }
}
s.oninput = () => { v.textContent = s.value + '°'; send(s.value); };
fetch('/angle').then(r => r.text()).then(a => { s.value = a; v.textContent = a + '°'; });
</script></body></html>
)rawliteral";

void setup() {
  Serial.begin(115200);
  ledcAttach(SERVO_PIN, PWM_FREQ, PWM_BITS);   // API ядра 3.x
  writeAngle(90);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println(WiFi.localIP());

  server.on("/", [] { server.send(200, "text/html", PAGE); });
  server.on("/angle", [] {
    if (server.hasArg("value")) writeAngle(server.arg("value").toInt());
    server.send(200, "text/plain", String(currentAngle));
  });
  server.begin();
}

void loop() {
  server.handleClient();
}

Як це працює

  • ledcAttach() налаштовує апаратний ШІМ на пін: 50 Гц, 14 біт.
  • writeAngle() перетворює кут на ширину імпульсу в мікросекундах, а її — на «заповнення» (duty) від 0 до 16383.
  • JavaScript на сторінці надсилає новий кут під час руху повзунка, але не частіше, ніж плата встигає відповідати: поки запит у дорозі, запам'ятовується лише останнє значення.

Калібрування

Сервоприводи різних виробників мають різний діапазон. Якщо на краях вал «дзижчить» і впирається — звузьте MIN_US/MAX_US (наприклад, 600 і 2300). Якщо не доходить до 180° — розширте.

Ядро 2.x

У старих версіях ядра замість ledcAttach використовувались канали:

ledcSetup(0, PWM_FREQ, PWM_BITS);
ledcAttachPin(SERVO_PIN, 0);
ledcWrite(0, duty);   // пишемо в канал, а не в пін

Ідеї

  • Годівниця: за розкладом (NTP) поворот на 90° і назад — порція корму висипається.
  • Pan-tilt для ESP32-CAM: два сервоприводи й два повзунки.
  • Плавний рух: змінюйте кут по 1° кожні 15 мс замість різкого стрибка.