Шторка на окне дачного домика проработала сезон и однажды открылась на двести шагов больше нужного: вечером отключили питание, утром счётчик шагов проснулся с нуля, а ткань осталась наполовину закрытой. Скетч честно отмерил свои девяносто градусов от того места, где механика застыла, — и вдавил каретку в раму. Шаговик считает шаги только с момента включения: всё, что случилось с валом без питания, для него не существует.
Лечится процедурой, которую каждый 3D-принтер показывает вам при старте: каретка едет к краю, тыкается в концевик, и это место становится нулём. Называется homing, по-русски — поиск нуля или калибровка нуля. Без неё координатная механика бессмысленна: столик из прошлого урока, задвижка, поворотный стенд — все они рисуют координаты от ниоткуда.
В этом уроке повесим микроконцевик на пин D2, поедем к нему в два прохода — быстро и потом медленно, отъедем и обнулим счётчик — и добавим защиту на случай, если концевик молчит: обрыв провода не должен превращаться в минуту давления мотора в упор.
Почему шаговику нужен ноль
Позиция шаговика — не место в пространстве, а число в переменной: сколько шагов насчитали с момента старта. Перезагрузка — число в нуле. Мигнул свет — снова ноль. Механика при этом стоит где стояла: связь между счётчиком и физическим положением рвётся целиком. У серво такой беды нет — датчик внутри корпуса; у шаговика датчика нет вообще, вернее, он у вас в коде, и его надо периодически сверять с миром.
Роль сверщика играет концевик. Для первой калибровки хватает механического микрика за полсотни рублей: одна нога на D2, другая на GND, pinMode(2, INPUT_PULLUP) — кнопки мы уже читали ровно так. Не нажат — HIGH, нажат — LOW: внутренняя подтяжка прижимает вход к питанию, концевик замыкает его на землю. Паять резисторы не нужно, дрожание наводок подтяжка тоже гасит.
Ноль назначает код, а не железо: в момент срабатывания концевика вы пишете в счётчик ноль, и вся дальнейшая арифметика позиций ведётся от этой точки. Как считать ходы в градусах и откуда берутся 2048 шагов на оборот, мы уже разбирали; homing лишь привязывает эту арифметику к реальному миру — один раз за включение.
| Проход | Скорость и объём | Зачем нужен |
|---|---|---|
| Быстрый проезд | 10-15 RPM, не дальше guard шагов | До концевика можно не церемониться: время экономится здесь |
| Отъезд | 200-300 шагов от концевика | Выйти из зоны дребезга, снять выбег и прижим |
| Медленный финал | 3-5 RPM, по одному шагу | Точность касания плюс-минус один-два шага |
| Защита | Guard: ход плюс 20 процентов | Молчит концевик — стоп и сообщение вместо давления в упор |
Почему два прохода, а не один медленный? Можно и один, но долго: если концевик стоит в четверти оборота от старта, на 3 RPM до него ехать пять секунд — 512 шагов по 9,8 мс. Практика принтеров нашла компромисс: быстрый проезд на 10-15 RPM находит концевик за секунду с небольшим, но вал по инерции проскакивает точку касания — на моих каретках выбег составлял пять-восемь шагов. Именно проскоченные шаги и лечит второй проход.
Поэтому после быстрого касания отъезжаем на двести-триста шагов: снимаем выбег, прижим и заодно дребезг. Контакт микрика пружинит — замыкается и размыкается пару миллисекунд, и на быстрой езде легко насчитать два-три нажатия вместо одного. Медленный финал идёт на 3 RPM по одному шагу: шаг длится 9,8 мс, дребезг в 1-5 мс укладывается внутрь одного такта. Точность выходит плюс-минус шаг — это 0,18 градуса вала.
Отъезд делает ноль воспроизводимым, и это важнее точности. Всегда подъезжаем к концевику с одной и той же стороны и на одной скорости — люфт редуктора каждый раз выбран в одну сторону. Когда я подъезжал то слева, то справа, ноль гулял на три-четыре шага между запусками, и шторка каждое утро чуть иначе доходила до края. Механики называют это подходом к реперу с одной стороны — правило годится для любого позиционного механизма.
#include <Stepper.h> const int STEPS_PER_REV = 2048;const int ENDSTOP = 2; // микрик нулевой позицииconst long GUARD = 4000; // защита: больше любого хода Stepper slider(STEPS_PER_REV, 8, 10, 9, 11);long pos = 0; // позиция от нуляbool homed = false; void setup() { pinMode(ENDSTOP, INPUT_PULLUP); Serial.begin(9600); homeSeek();} void homeSeek() { // 1. быстрый проезд к концевику slider.setSpeed(15); long guard = 0; while (digitalRead(ENDSTOP) == HIGH && guard < GUARD) { slider.step(-20); guard = guard + 20; } if (guard >= GUARD) { Serial.println("Endstop silent: wiring or limit"); return; } // 2. отъезд от концевика slider.step(300); // 3. медленный финал по одному шагу slider.setSpeed(3); while (digitalRead(ENDSTOP) == HIGH) { slider.step(-1); } pos = 0; // вот он, ноль homed = true; Serial.println("Homed: zero set");} void loop() { if (!homed) { return; } // без калибровки мотор не трогаем if (pos == 0) { rideTo(1500); } else { rideTo(0); } delay(1000);} void rideTo(long target) { if (target > 1500) { target = 1500; } if (target < 0) { target = 0; } slider.setSpeed(6); while (pos < target) { slider.step(1); pos = pos + 1; } while (pos > target) { slider.step(-1); pos = pos - 1; }}Три места, где этот скетч отличается от наивного. Первое — guard: счётчик в условии while ограничивает поиск четырьмя тысячами шагов, когда максимум хода — полторы тысячи. Провод концевика отошёл — мотор не давит в упор до перегрева: после guard шагов он остановится и напечатает, что концевик молчит. Запас в двадцать процентов к максимуму хода — моё рабочее правило, здесь я поставил почти трёхкратный, и дальше объясню зачем.
Второе — отъезд ровно триста шагов и второй заход на 3 RPM. На 15 RPM шаг занимает 1,95 мс, и инерция редуктора провозит вал мимо точки касания; медленный финал по одному шагу съедает эту ошибку. Третье — обнуление ровно в момент срабатывания: pos присваивается ноль тогда, когда контакт замкнулся, и рабочая езда отправляется уже от этой точки, а не от какого-то среднего.
Нюанс чтения входа: INPUT_PULLUP, не нажат — HIGH, нажат — LOW. Если у вас наоборот, микрик стоит на нормально замкнутую пару — у большинства микриков три ноги, и NC-контакты работают инверсно. Это не поломка, а вариант, о котором поговорим в совете ниже. А если хотите реагировать на фронт мгновенно, а не опросом в цикле, — прерывания attachInterrupt мы уже разбирали, и в следующем уроке они понадобятся всерьёз.
Защита: если концевик молчит
Концевик — механика, а механика ломается: провод отламывается у пайки, контакт окисляется, язычок гнётся. Сценарий без защиты некрасивый: мотор едет к несуществующему концевику, упирается, пластиковые шестерни редуктора начинают проскальзывать — счётчик насчитывает полную чушь, а обмотки продолжают жрать ток. Ток удержания около 240 мА греет мотор и без нагрузки; десять минут стояния в упоре — и корпус не удержать пальцем.
Защита примитивна и потому надёжна: максимум шагов до концевика вы знаете — это длина хода плюс запас. Поиск прошёл больше и концевик не сработал — стоп, сообщение в Serial, статус не готово. Guard я делаю равным ходу плюс двадцать процентов, а на стенде с переставляемой кареткой — трёхкратным: однажды я поставил каретку не на тот конец рельса, и честный поиск проехал все четыре тысячи шагов, прежде чем сдаться.
Второй уровень защиты — статус. Пока homed ложь, рабочие команды движения не выполняются: loop крутится, Serial отвечает, мотор не трогается. Это правило хорошего тона: механизм без калибровки не принимает задачи. Пользователь жмёт открыть шторку — программа вежливо печатает, что сначала нужен homing, и не делает вид, что всё нормально.
В симуляторе концевик изображает кнопка на пине D2: нажатие замыкает вход на землю, как настоящий микрик. Каретка едет к нулю мелкими шагами, Serial печатает позицию каждые сорок шагов. Нажмите кнопку во время поиска: зуммер щёлкнет, каретка отъедет на сто шагов, счётчик обнулится, светодиод на D12 подтвердит готовность — и начнётся рабочая езда от нуля. Читать лог удобно в Serial-мониторе.
Второй сценарий — не нажимайте ничего. За четыреста шагов поиск остановится, Serial напечатает, что концевик молчит, и мотор замрёт в фазе отказа: это работает guard. Попробуйте оба пути — счастливый и аварийный. Защиту, которую не проверяли, я привык считать не написанной: она всегда ломается именно в тот день, когда нужна.
- Позиция шаговика живёт в переменной и умирает при перезагрузке: ноль находят концевиком при каждом старте
- Микрик на D2 с INPUT_PULLUP: не нажат — HIGH, нажат — LOW, резисторы не нужны
- Homing в три прохода: быстрый 10-15 RPM, отъезд 200-300 шагов, финал 3 RPM по одному шагу — точность плюс-минус шаг
- Подъезд к реперу всегда с одной стороны: люфт выбран в одну сторону, ноль воспроизводим
- Guard равен ходу плюс 20 процентов: концевик молчит — стоп, сообщение, рабочие команды не выполнять
- 1. Что знает шаговик о своей позиции после перезагрузки питания?
- 2. Какой pinMode нужен микрику, который замыкает вход на землю?
- 3. Зачем после быстрого касания концевика отъезжать на 200-300 шагов?
- 4. Сколько длится один шаг 28BYJ-48 при 3 RPM?
- 5. Ход каретки — 1500 шагов. Какой guard для поиска концевика разумнее?
Ноль найден, счётчик обнулён — но поездка к нолю была последней доверчивой. Между нулём и противоположным краем остаётся целая дорога, на которой механизм может заклинить: упереться в раму, собрать мусор, проскользнуть на редукторе. Следующий урок ставит программные границы и второй концевик на край хода, добавляет аварийную кнопку и разбирает, что физически происходит с шаговиком, когда ему не дают доехать.
А пока вернитесь к симулятору и сломайте его осознанно: нажмите кнопку на первом же такте поиска — ноль окажется далеко от настоящего края, и рабочая езда съест половину хода. Концевик честен ровно настолько, насколько честно место, где вы его поставили.
Частые вопросы по теме урока
- Что знает шаговик о своей позиции после перезагрузки питания?
- Счётчик шагов — переменная в оперативной памяти: перезагрузка обнуляет её. Связь с физическим положением вала рвётся, нужен homing. Ничего: счётчик стартует с нуля
- Какой pinMode нужен микрику, который замыкает вход на землю?
- Внутренняя подтяжка к питанию прижимает вход к HIGH, концевик замыкает его на землю. Висящий вход без подтяжки ловит наводки от моторных проводов. INPUT_PULLUP
- Зачем после быстрого касания концевика отъезжать на 200-300 шагов?
- Отъезд снимает инерционный выбег, прижим и дребезг контакта. Второй медленный подход с той же стороны делает ноль воспроизводимым. Чтобы выйти из дребезга и выбега и подъехать снова с одной стороны
- Сколько длится один шаг 28BYJ-48 при 3 RPM?
- 60 секунд делим на 3 оборота и на 2048 шагов: 9,77 мс на шаг. Дребезг микрика в 1-5 мс укладывается внутрь одного такта. 9,8 мс
- Ход каретки — 1500 шагов. Какой guard для поиска концевика разумнее?
- Правило: ход плюс двадцать процентов — 1800. Ровно 1500 оставляет ноль запаса, а тысячный guard короче самого хода. 1800