Флажок-указатель на батарейках был проектом на один вечер и на один облом. Между поворотами я хотел, чтобы серво отдыхала и не ела батарейку, поэтому обвешал скетч вызовами detach() — и с удивлением смотрел, как указатель медленно сползает под собственным весом, хотя «должен же он держаться, он же серво». А при возврате attach() серво рванулась к цели с такой скоростью, что слетела качалка. Браком это не было: я просто не понимал, что detach выключает не «ожидание», а само удержание, и что read() после этого врёт мне в лицо. Разобраться пришлось по исходникам библиотеки.
В этом уроке — весь словарь Servo.h, которого хватает девяти проектам из десяти: attach в трёх формах, write с его скрытой ловушкой, read, который читает заказ, а не реальность, writeMicroseconds для точной работы и detach с его честной ценой. Библиотека стоит в Arduino IDE из коробки, устанавливать ничего не нужно. Схему подключения и первый скетч мы собирали в уроке про SG90, физику удержания разобрали в уроке про устройство серво — здесь остаётся язык команд.
attach: одна команда, три формы
attach() знакомит объект Servo с пином и запускает фоновую генерацию импульсов — после него вручную дёргать пин не нужно, таймер шлёт посылки каждые 20 мс сам. Формы вызова три. attach(9) — простейшая: библиотека берёт границы импульса по умолчанию 544–2400 микросекунд, растянутые на весь диапазон 0–180 градусов. attach(9, 1000, 2000) — та же команда с явными границами: угол 0 получит импульс 1000 мкс, угол 180 — ровно 2000. Именно эта форма — мой рецепт для SG90: паспортная вилка серво шире, чем 1000–2000, и на границах дефолта она бьётся об механические упоры с перегрузом каждые 20 мс. Пара лишних строк в setup экономит пластиковые шестерни.
Третья форма — attach уже после detach: тот же пин, та же настройка, генерация возобновляется. Важная деталь, о которой редко пишут: числа границ — это микросекунды, а не градусы, и менять их местами не стоит. Если серво ведёт себя криво только на краях диапазона — первым делом попробуйте явные границы 1000–2000, это самый частый фикс из всех, что я знаю. О форматах импульсов и откуда взялись эти числа, я писал в разборе плавного движения серво — там же, как использовать диапазон для скорости.
Откуда взялся дефолт 544–2400? Библиотека проектировалась под широкий парк серво, от крошечных до крупных, и разработчики выбрали самую широкую вилку из даташитов, чтобы write(0..180) покрывал почти любое железо без настроек. Для крупной серво с размахом 600–2400 мкс такой дефолт удобен, а вот крошке вроде SG90 с её упорами широкая вилка только вредит: крайние градусы диапазона превращаются в удары мотора о стопор. Поэтому мой setup для SG90 всегда выглядит одинаково: attach с явными 1000–2000, и платы это стоит копейки — две лишние константы в строке вызова.
write(угол) отдаёт приказ: повернуть вал на 0–180 градусов. Вызывать можно сколько угодно часто, серво поедет к новой цели — но без пауз между целями она физически не успеет: паспортная скорость SG90 — 0,1 с на 60 градусов, значит полный ход 0–180 занимает около трети секунды. read() возвращает последний записанный угол — заказ, а не факт: серво не отчитывается о реальном положении, у неё нет канала наружу для этого. Поэтому sv.read() сразу после sv.write(45) честно вернёт 45, пока вал ещё едет. Для фактического угла существует только потенциометр внутри, и наружу он не выведен.
Ловушка, на которой я обжигался: в настоящей библиотеке аргумент write() больше 180 трактуется не как угол, а как микросекунды. write(1500) — это не «полтора оборота» и не ошибка, это writeMicroseconds(1500), то есть примерно 90 градусов. Код компилируется, серво слушается, и баг может жить месяцами, маскируясь под особенности железа. Правило простое: хотите микросекунды — пишите writeMicroseconds(), хотите градусы — следите, чтобы аргумент не вылезал за 180. Для точной подстройки под конкретный экземпляр серво writeMicroseconds незаменим: у моей партии 180 градусов наступали на 2050 мкс, и writeMicroseconds(2000) держал край без упора. Ниже — живая лаборатория сервопривода: подвигайте ползунок угла и смотрите, как каждый градус превращается в ширину импульса в микросекундах.
#include <Servo.h> Servo sv; void setup() { sv.attach(9, 1000, 2000); // границы импульса под SG90 Serial.begin(9600); Serial.println("Угол 0-180 через монитор");} void loop() { if (Serial.available() > 0) { int ugol = Serial.parseInt(); if (ugol >= 0 && ugol <= 180) { sv.write(ugol); Serial.print("zadano="); Serial.println(sv.read()); } }}| Метод | Что делает | Нюанс, о который бьются |
|---|---|---|
| attach(pin) | включает импульсы на пине | на Uno занимает Timer1: ШИМ на пинах 9 и 10 глохнет |
| attach(pin, min, max) | то же с границами в мкс | дефолт 544–2400 — SG90 бьётся об упоры |
| write(deg) | приказ угла 0–180 | аргумент больше 180 читается как микросекунды |
| read() | возвращает последний приказ | это заказ, а не фактический угол вала |
| detach() | прекращает генерацию импульсов | серво теряет удержание, вал крутится пальцем |
Ширина импульса 544–2400 мкс превращается в угол 0–180°.
Подгружаем интерактив…
detach: свобода имеет цену
detach() приказывает библиотеке перестать слать импульсы. Внутри серво нет команды «спать» — плата просто перестаёт получать команды, выходные транзисторы закрываются, мотор обесточен. Вал становится свободным: серво больше не держит позицию, и любая нагрузка — вес флажка, пружина, ветер — двигает вал куда хочет. Зачем это нужно? Первое: батарейное питание, серво на удержании ест около 10 мА круглосуточно, а в отпуске — доли миллиампера. Цифры для сравнения: за сутки удержания набегает четверть ампер-часа, пара банок АА улетает за вечер-другой; после detach потребление падает почти до нуля, потому что обесточен сам мотор и большая часть платы. В моём флажковом указателе это выглядело так: поворот по кнопке, десять секунд удержания, detach — и батарейки стали жить неделями вместо часов. Второе: механики, которые обязаны уметь падать в исходное положение при отказе, — шлюзы, заслонки, стопоры: деталь должна двигаться рукой человека, когда электроника молчит. Третье: сниженный нагрев, когда серво часами стоит под прижимающей нагрузкой и контур непрерывно давит мотором.
Оборотная сторона — три грабли, на каждой из которых я лежал. Первая: detach не запоминает, что вал сместили, — при повторном attach серво рванёт к последней цели с полного хода, и качалка слетит именно в этот момент; перед attach возвращайте вал на место рукой или согласовывайте механику. Вторая: read() после detach продолжает возвращать старый приказ — библиотека о механике не знает, и доверять этому числу нельзя. Третья: detach останавливает импульсы, но не пин — на Uno таймер всё равно занят, пока объект существует, и ШИМ на 9 и 10 не вернётся, хотя серво уже свободна. Проверка attached() говорит, жива ли генерация: после detach она вернёт ложь.
Про ресурс платы: библиотека Servo на Uno работает через Timer1, тот же таймер обслуживает analogWrite на пинах 9 и 10 — мы это разбирали в уроке про ШИМ. До 12 объектов Servo живут на пинах 2–13 одновременно, таймер общий, импульсы идут всем параллельно; на Mega потолок заметно выше — несколько десятков. Как именно таймер режет время на микросекундные куски, показано в обзоре таймеров ATmega328 — после него фраза «Servo занимает Timer1» перестаёт быть заклинанием. Приём команд из Serial в скетче выше построен на parseInt — если незнаком, есть урок про чтение чисел из порта.
Задания в симуляторе. Первое: замените attach(9, 1000, 2000) на attach(9) и прогоните тот же маршрут — виджет не заметит разницы, а вот на реальной SG90 крайние углы изменятся, это и есть сужение вилки. Второе: добавьте detach() с паузой после середины прогона, затем attach() и write() на прежний угол — предсказывайте по тексту урока, что случится с валом на живой плате, и проверьте attached(). Третье: отправьте серво в write(1500) и объясните в комментарии к коду, почему это законные 90 градусов — эта ловушка теперь ваша. Четвёртое, на живой плате: измерьте потребление мультиметром в разрыв красного провода в трёх состояниях — удержание позиции, движение между углами, после detach. Три цифры перестанут быть абстракциями из урока и станут вашими собственными, а заодно вы узнаете, сколько реально отдаёт ваша батарейка, а не табличка на ней.
- attach(pin) включает импульсы; attach(pin, 1000, 2000) — мой дефолт для SG90, чтобы не биться об упоры
- write() — угол 0–180; аргумент больше 180 трактуется как микросекунды, для точности есть writeMicroseconds()
- read() возвращает последний приказ, а не фактический угол: канала обратной связи наружу у серво нет
- detach() снимает удержание: вал свободен, read() продолжает врать, attach() заставляет серво рвануть к старой цели
- На Uno Servo занимает Timer1 — ШИМ на пинах 9 и 10 недоступен, до 12 серво на пинах 2–13
- attached() отвечает, идёт ли генерация импульсов: ложь после detach, истина после attach
- 1. Что вернёт sv.read() сразу после sv.detach(), если последним был sv.write(120)?
- 2. Запись sv.write(1500) в настоящей библиотеке Servo означает...
- 3. Зачем вызывают attach(9, 1000, 2000) вместо attach(9) для SG90?
- 4. После sv.detach() механика сдвинула вал на 40 градусов. Что случится при sv.attach()?
- 5. Сколько объектов Servo можно поднять на Arduino Uno одновременно?
Частые вопросы по теме урока
- Что вернёт sv.read() сразу после sv.detach(), если последним был sv.write(120)?
- read() возвращает последний записанный угол. После detach вал может быть сдвинут нагрузкой, но read() об этом не узнает. 120 — последний приказ, о факте библиотека не знает
- Запись sv.write(1500) в настоящей библиотеке Servo означает...
- Аргумент больше 180 трактуется как микросекунды, эквивалент writeMicroseconds(1500). Код скомпилируется — будьте внимательны. Импульс 1500 мкс — примерно 90 градусов
- Зачем вызывают attach(9, 1000, 2000) вместо attach(9) для SG90?
- Дефолтные границы 544–2400 мкс шире штатной вилки SG90: на краях серво давит мотором на упор. Явные границы бережут шестерни. Чтобы задать границы импульса и не бить серво об механические упоры
- После sv.detach() механика сдвинула вал на 40 градусов. Что случится при sv.attach()?
- attach возобновляет импульсы с прежней целью: серво с полного хода поедет к старому приказу. Качалка в этот момент слетает чаще всего. Серво рванёт к последней заказанной цели
- Сколько объектов Servo можно поднять на Arduino Uno одновременно?
- Библиотека Servo обслуживает до 12 серво на Uno через общий Timer1; на Mega — несколько десятков. До 12 на пинах 2–13