Пакет из маркетплейса шуршал многообещающе: внутри лежала синяя коробочка размером с крупную виноградину — сервопривод SG90, а рядом россыпью белые пластиковые качалки. Я тогда уже умел мигать светодиодом и был уверен, что теперь всё получится за пять минут. Воткнул три провода, залил скетч из первого попавшегося гайда — и попал в цирк: серво мелко дёргалась, гудела, а Arduino Uno уходила в перезагрузку каждый раз, когда рычаг пытался съехать с места. Полчаса я подозревал брак, прежде чем понял: серво исправна, сломано было моё представление о питании.
В этом уроке мы подключим SG90 так, как она того просит. Разберём три провода и их цвета, поймём, на каком языке серво понимает команды — короткими импульсами длиной 1000–2000 микросекунд, повторяющимися 50 раз в секунду, — напишем первый скетч с attach() и write() и соберём осмысленную механику «разворот по кнопке». Всё сразу можно пробовать в симуляторе ниже, настоящая плата понадобится, когда захотите потрогать рычаг пальцем.
Чем сервопривод не похож на обычный моторчик
Моторчик — наивный зверь: подал на него напряжение — крутится, убрал — стоит, скорость регулируешь силой тока. Он не знает, на сколько градусов повернулся, и честно в этом признаётся люфтом вала. Сервопривод устроен хитрее: в одном корпусе спрятаны маленький моторчик, редуктор из пластиковых шестерёнок и потенциометр, механически связанный с выходным валом. Плата внутри постоянно сверяет два числа — какой угол ей заказали и где вал находится прямо сейчас — и гонит мотор в сторону рассогласования.
Поэтому серво не крутится по кругу. Она идёт к заказанному углу и держит его, сопротивляясь, если вы пытаетесь рычаг сдвинуть. У SG90 рабочий ход — от 0 до 180 градусов, усилие — около 1,8 кг·см, вес — 9 граммов. Пока серво держит позицию, а вы давите на рычаг, она гудит и греется — именно в этот момент ток подскакивает к максимумам, о которых ниже. Существуют ещё серво с непрерывным вращением: у них внутри вместо потенциометра стоит обманка, и write() задаёт не угол, а скорость — но сегодня речь про классические.
Я считаю, что серво — лучший второй шаг после светодиода: результат виден без мультиметра и плоттеров, механика интуитивна, а грабли питания дают урок электроники, который не забывается. Если руки уже чешутся собрать роборуку из шести серво — я бы всё же начал с одной: иначе упрётесь в питание и перепайку одновременно, и не поймёте, чей это глюк.
Три провода и язык импульсов: 50 Гц и 1000–2000 мкс
У SG90 три провода, и их цвета почти всегда одинаковы: оранжевый (у некоторых партий жёлтый) — сигнал, красный — плюс питания 5 В, коричневый (иногда чёрный) — земля. Первая ассоциация новичка: «сигнальный провод передаёт напряжение, чем больше — тем больше угол». Это не так, и именно поэтому попытка управлять серво через analogWrite() заканчивается конфузом: серво вяло шевелится или молчит.
Серво понимает длительность импульсов. Каждые 20 миллисекунд (это частота 50 Гц — пятьдесят команд в секунду) на сигнальный провод надо подать высокий уровень на короткое время: от 1000 до 2000 микросекунд. Ширина импульса и есть команда: 1000 мкс — «встань на 0°», 1500 мкс — «встань на 90°», 2000 мкс — «180°, дальше упор». Между импульсами линия лежит на нуле, и серво терпеливо ждёт следующей посылки. Вся хитрость в том, что эти импульсы не надо слать руками — библиотека Servo.h делает это за вас в фоне, силами таймера микроконтроллера.
| Ширина импульса | Угол | Что происходит |
|---|---|---|
| 1000 мкс | 0° | крайнее левое положение |
| 1250 мкс | 45° | четверть хода |
| 1500 мкс | 90° | середина, нейтраль |
| 1750 мкс | 135° | три четверти хода |
| 2000 мкс | 180° | крайнее правое положение |
Числа с оговоркой: в даташите SG90 заявлен диапазон примерно 500–2400 мкс, и мой экземпляр послушно уезжал дальше штатных углов при 600 и 2400. Но за 0° и 180° у серво стоит механический упор, в который она бьётся с перегрузом каждые 20 мс — пластиковые шестерёнки этого не любят и со временем облизываются. Библиотека Servo.h по умолчанию шлёт импульсы 544–2400 мкс, так что write(0) и write(180) в неё укладываются, однако я советую на первых порах не выходить за вилку 1000–2000: пара месяцев спокойной работы дороже пары лишних градусов размаха.
Собираем схему: SG90 и Arduino Uno
Схема — три провода: коричневый в любой GND, красный в 5V, оранжевый в цифровой пин 9. Почему именно 9? Строго говоря, сигнал серво можно повесить на любой цифровой пин — библиотека сама организует импульсы. Но на Uno библиотека Servo работает через таймер Timer1, который заодно обслуживает analogWrite() на пинах 9 и 10: после sv.attach() эти два пина перестают отдавать обычный ШИМ. Поэтому их традиционно отдают серво, а обычный ШИМ переносят на пины 3, 5, 6 или 11.
В макетку серво ставится рядом с платой; провода у SG90 короткие, для первой схемы этого хватает. Если качалка ещё не надета — наденьте рычаг на вал так, чтобы в середине хода он смотрел перпендикулярно корпусу. Иначе на write(90) серво будет поворачиваться «вбок», и вы решите, что ошиблись в коде, хотя ошиблись в механике.
Servo.h: attach(), write() и read()
Библиотека Servo.h входит в Arduino IDE и не требует установки. Работа с ней строится на трёх вызовах. attach(пин) — «с этого пина я буду слать импульсы», вызывается один раз в setup(). write(угол) — приказ занять позицию от 0 до 180; вызывать можно сколько угодно раз, серво поедет к новой цели. read() — возвращает последний записанный угол, и это важно: заказанный, а не фактический. Спросить серво, где её вал в реальности, нельзя — она не отчитывается, внутри у неё только заказ и механика.
После attach() библиотека сама, в фоне, шлёт импульсы каждые 20 мс — вручную дёргать пин не нужно. На одну Uno можно повесить до 12 серво на пины 2–13, таймер у них общий. Ниже — первый скетч: серво шагает из 0° в 180° и обратно, раз в секунду меняя цель.
#include <Servo.h> Servo sv; // объект-сервопривод void setup() { sv.attach(9); // сигнальный провод на пине 9 Serial.begin(9600); Serial.println("Серво на пине 9. Поехали!");} void loop() { Serial.println("Цель: 0 градусов"); sv.write(0); // приказ: угол 0 delay(1000); // секунда, чтобы серво успела доехать Serial.println("Цель: 180 градусов"); sv.write(180); delay(1000);}Разворот по кнопке
Соберём первую осмысленную механику: по нажатию кнопки серво переворачивается между двумя позициями — 0° и 90°. Кнопку подключаем между пином 2 и GND, пин настраиваем как INPUT_PULLUP: внутренняя подтяжка держит его на HIGH, а нажатие прижимает линию к земле, и digitalRead() возвращает LOW. Запомнить «нажата — значит LOW» полезно заранее: половина ошибок новичков с кнопками живёт именно здесь.
Ловим не «нажата», а «нажата только что»: сравниваем текущее состояние с предыдущим. Это называется фиксацией фронта. Без неё, пока палец лежит на кнопке, серво будет переворачиваться пятьдесят раз в секунду — кнопка не «событие», а уровень. После срабатывания ставим паузу 200 мс — грубая, но рабочая защита от дребезга, когда контакты в момент замыкания раз десять успевают мигнуть. Для кнопки-переключателя этого достаточно; изящные решения с millis() приходят в следующих уроках.
#include <Servo.h> Servo sv;const int BUTTON = 2; // кнопка между пином 2 и GNDbool at90 = false; // false — серво на 0, true — на 90int lastState = HIGH; void setup() { pinMode(BUTTON, INPUT_PULLUP); // внутренняя подтяжка к 5 В sv.attach(9); sv.write(0); Serial.begin(9600); Serial.println("Серво готова. Нажимайте кнопку.");} void loop() { int state = digitalRead(BUTTON); if (state == LOW && lastState == HIGH) { // нажали только что if (at90) { sv.write(0); Serial.println("Позиция: 0 градусов"); at90 = false; } else { sv.write(90); Serial.println("Позиция: 90 градусов"); at90 = true; } delay(200); // защита от дребезга } lastState = state;}Эксперименты для закрепления
Прочитанное без рук не работает — код нужно пощупать, желательно до того, как он пощупает вас. Четыре задачи прямо в симуляторе, по возрастанию сложности.
- В скетче-развороте замените позиции 0 и 90 на 30 и 150. Затем добавьте третью позицию 180 — пусть каждое нажатие листает позиции по кругу. Подсказка: переменная-счётчик и один if со сбросом значения.
- В sweep-скетче уменьшите паузы с 1000 до 300 мс. Серво перестанет доезжать — проверьте расчётом: SG90 проходит 60° примерно за 0,1 с, значит на 180° ей нужно около 0,3 с. Пауза всегда должна быть больше времени хода.
- Выведите в Serial sv.read() сразу после каждого write() и убедитесь, что возвращается заказанный угол — даже если серво физически ещё едет к нему.
- Перенесите сигнал на пин 10 — менять придётся только строку с attach(). А вот analogWrite(9, ...) теперь молчит без ошибок: вспомните, кто занял Timer1.
- SG90: красный провод — 5 В, коричневый — GND, оранжевый (или жёлтый) — сигнал
- Команда серво — импульс: 1000–2000 мкс каждые 20 мс (частота 50 Гц); 1500 мкс = 90°
- Servo.h: attach(пин) один раз в setup(), write(0–180) сколько нужно, read() вернёт последний заказанный угол
- На Uno Servo занимает Timer1 — analogWrite на пинах 9 и 10 после attach() не работает
- Ток SG90: около 10 мА в покое, 100–250 мА в движении, до 650–700 мА при заклинивании
- Серво дёргается, плата перезагружается — питание от USB кончилось: отдельный источник 5–6 В и общий GND
- 1. Какой провод у SG90 сигнальный?
- 2. Импульс какой ширины повернёт серво на 90°?
- 3. С какой частотой нужно повторять импульсы?
- 4. Серво дёргается, и Arduino перезагружается при каждом движении. Первая версия диагноза?
- 5. Что произойдёт с analogWrite(9, 128) после вызова sv.attach(9) на Arduino Uno?
- 6. Что вернёт sv.read() сразу после sv.write(45)?
Во втором уроке раздела заставим серво двигаться плавно — по градусу за 15 мс, — подключим потенциометр как ручку управления через map() и перепишем движение на millis(), чтобы loop() не блокировался. А тупик с перезагрузками закончим по-взрослому: отдельное питание серво, общий провод — и ни одного сброса.
Частые вопросы по теме урока
- Какой провод у SG90 сигнальный?
- Красный — плюс питания 5 В, коричневый — земля, оранжевый — сигнал с пина Arduino. Оранжевый (или жёлтый)
- Импульс какой ширины повернёт серво на 90°?
- 1500 мкс — середина диапазона 1000–2000 мкс, соответствует 90°. 1500 мкс
- С какой частотой нужно повторять импульсы?
- Стандарт серво: импульс каждые 20 мс (50 Гц). Ширина задаёт угол, частота — ритм посылки. 50 Гц — каждые 20 мс
- Серво дёргается, и Arduino перезагружается при каждом движении. Первая версия диагноза?
- Стартовый ток SG90 — 100–250 мА, при заклинивании до 650–700 мА, а USB даёт около 500 мА. Лечится отдельным питанием 5–6 В с общим GND. Питание от USB не тянет пусковой ток серво
- Что произойдёт с analogWrite(9, 128) после вызова sv.attach(9) на Arduino Uno?
- Servo на Uno работает через Timer1, который обслуживает ШИМ пинов 9 и 10. После attach() обычный analogWrite на них недоступен. Перестанет работать: Timer1 занят библиотекой Servo
- Что вернёт sv.read() сразу после sv.write(45)?
- read() возвращает последний записанный угол, а не фактическое положение — серво не отчитывается о реальной позиции. 45 — последний заказанный угол