Пролог этого урока обошёлся мне в ночную печать: на седьмом часу кончился пруток, сопло два часа рисовало в воздух, и я узнал об этом утром по глянцевой яме в модели. Принтер не умеет просить добавки — он честно отдаёт команду подачи, даже когда за ней пустота. Тогда я собрал боковой податчик: катушка стоит рядом, пруток проходит через роликовую пару на 28BYJ-48 и уходит в экструдер, а плата отмеряет подачу дозами — досыпать полметра, остановиться, подождать, пока голова выйдет на температуру. Мотор тут нужен не быстрый, а аккуратный: считать каждый шаг, не спешить, уметь стоять. Под эти три требования 28BYJ-48 подходит как ни один другой мотор из моего ящика, а арифметика у него та же, что мы уже считали в уроке про передаточное число и 2048 шага.
Механика до смешного простая: два вала, зажатый между ними пруток, прижим. Нижний вал сидит на выходном валу мотора — он ведущий. Верхний крутится свободно на оси и прижимается к прутку резинкой или слабой пружиной. Пруток 1,75 мм зажат между рифлёным ведущим валом и гладким прижимным: проскальзывать ему некуда, пока прижим честный. Точно такая же пара стоит в податчике любого 3D-принтера — там она зовётся экструдером, только мотор посерьёзнее. Нашему податчику не нужно давить пруток в горячее сопло: он гонит его по тефлоновой трубке, где трение мизерное, и скромного момента 28BYJ-48 с запасом хватает. Схема подключения — стандартная, через ULN2003 на пины 8, 10, 9, 11, ничего добавлять не нужно.
В уроке посчитаем дозу в шагах, соберём скетч с кнопкой и тумблером, заведём счётчик выданных сантиметров в Serial и прогоним всё это в симуляторе — там кнопка выдаёт порцию, тумблер гонит непрерывную подачу, а счётчик тикает в мониторе порта на ваших глазах.
Окружность ролика — цена шага
Вся арифметика дозатора живёт в одной строке: за оборот ведущий вал выдаёт прутка ровно столько, сколько сантиметров в его окружности. Вал радиусом 8 мм за оборот отдаёт 2π×8 = 50,3 мм — около 5 см прутка. На оборот уходит 2048 шагов, значит один шаг — 0,0245 мм. Сантиметр прутка — это 407 шагов, доза в десять сантиметров — 4074 шага, и при 10 RPM такая доза занимает одиннадцать секунд: мотор молотит тысячи шагов, а на выходе — скромный кусочек пластика. Для дозатора это нормальный ритм, спешить тут некуда.
Числа верны до тех пор, пока вы посчитали окружность по настоящему рабочему радиусу. Пруток лежит не на вершине рифления, а на дне канавки: мой ведущий вал снаружи имеет 11 мм, но эффективный радиус в канавке — 8 мм, и я неделю считал по внешнему диаметру, пока не замерил линейкой. Разница вышла в треть метража — счётчик уверенно показывал тридцать сантиметров там, где вышло двадцать. Вторая ловушка — проскальзывание: ослабнет прижим, и пруток проворачивается в роликах на старте дозы. Счётчик тикает, пруток стоит. Прижим делайте с запасом: у меня канцелярская резинка, перекинутая через ось прижимного вала, — сомнительная деталь с рекордной выживаемостью.
Отсюда правило, которое я наклеил на корпус: константу шага на сантиметр проверяют линейкой, а не калькулятором. Калькулятор даёт теорию по номинальному радиусу, линейка — фактическую длину конкретно вашего ролика, и верить надо второй. Как поверять — покажу в заметке ниже, после скетча.
| Единица | Длина прутка | Комментарий |
|---|---|---|
| 1 шаг | 0,245 мм | при эффективном радиусе ролика 8 мм |
| 1 см | 407 шагов | базовая константа скетча, проверяется линейкой |
| 1 оборот (2048 шага) | 50,3 мм — около 5 см | окружность вала: 2π×8 мм |
| Доза 10 см | 4074 шага | около 12 секунд при 10 RPM |
Доза кнопкой, подача тумблером
Управление я развёл на два органа. Кнопка выдаёт одну дозу — у меня 4074 шага, десятисантиметровый кусок. Тумблер включает непрерывную подачу: мотор гонит пруток, пока тумблер замкнут, — этот режим нужен при заправке длинного пути в экструдер, когда доза-за-нажатием уже утомляет. И то и другое живёт в одном loop() без блокировки: подача идёт порциями на millis, и между порциями остаётся окно, в котором опрашиваются кнопка, тумблер и счётчик.
Порция — восемь шагов. При 10 RPM один шаг занимает 2,9 мс, восемь шагов — 23 мс, и каждые 25 миллисекунд loop() успевает оглядеть органы управления. Увеличьте порцию до двухсот шагов — кнопка начнёт отвечать с полусекундной задержкой; отдайте команду 4074 шага за раз — и loop() замрёт на двенадцать секунд, ровно это и происходит, когда step() зовут одной целиковой командой. Дробление — единственная цена неблокирующей подачи, и она чисто символическая.
Счётчик веду в сантиметрах с одним знаком: каждый выданный шаг прибавляет 0,00245 см, и после каждого целого сантиметра Serial печатает строку вида vydano_cm=. Печать парами имя=значение — привычка из урока про монитор порта: такие строки читаются по диагонали, а Serial Plotter, если захотите построить кривую подачи, не перепутает ряды. Кнопку читаю по фронту с гашением дребезга — обработка дребезга из первых уроков здесь не украшение: без неё одна качка пальца выдаст две-три дозы, и счётчик сразу перестанет сходиться с фактом.
#include <Stepper.h> const int STEPS_PER_REV = 2048;const int DOZA = 4074; // 10 см прутка при радиусе ролика 8 ммconst float CM_PER_STEP = 0.00245; // 50,3 мм на оборот / 2048 шагов Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11); int ostalos = 0; // сколько шагов осталось выдатьfloat cm = 0.0; // счётчик выданного, нарастающим итогомfloat cmPechat = 0.0; // отметка последней печатиunsigned long tKorm = 0;int knopByla = HIGH; void setup() { stepper.setSpeed(10); // выше 15 RPM редуктор гудит на месте Serial.begin(9600); pinMode(2, INPUT_PULLUP); // кнопка: доза 10 см pinMode(3, INPUT); // тумблер: непрерывная подача Serial.println("Dozator gotov");} void loop() { int knop = digitalRead(2); if (knop == LOW && knopByla == HIGH) { ostalos += DOZA; Serial.println("doza 10 cm"); } knopByla = knop; if (digitalRead(3) == HIGH && millis() - tKorm >= 25) { ostalos += 8; // непрерывная подача: 8 шагов за такт tKorm = millis(); } if (ostalos > 0) { int porciya = 8; if (porciya > ostalos) { porciya = ostalos; } stepper.step(porciya); ostalos -= porciya; cm += porciya * CM_PER_STEP; if (cm - cmPechat >= 1.0) { cmPechat = cm; Serial.print("vydano_cm="); Serial.println(cm, 1); } } delay(20);}В скетче три места, на которые стоит смотреть отдельно. Первое — флаг knopByla: доза начисляется только на фронте, когда кнопка перешла из HIGH в LOW. Второе — порция: даже при остатке в четыре тысячи шагов мотор выдаёт по восемь шагов за проход, и loop() остаётся живым от первого шага до последнего. Третье — печать по достижению целого сантиметра: без отметки cmPechat монитор заливало бы строкой на каждую порцию, а так в Serial попадают только события.
Числа для самопроверки. Доза 4074 шага при 10 RPM — это 11,9 секунды моторного времени. Непрерывная подача при темпе восемь шагов в 25 мс — 320 шагов в секунду, то есть 9,4 RPM и около 47 см прутка в минуту. Быстрее 15 RPM я бы не лез: потолок скорости 28BYJ-48 мы меряли отдельным уроком, выше него редуктор гудит на месте и пропускает шаги. Для дозатора срыв шагов — самая дорогая разновидность вранья: счётчик продолжает складывать, будто всё выдал, а на выходе дыра в метраже. Счётчик я не сбрасываю кнопкой — он копится нарастающим итогом от включения, как одометр; чистый лист нужен только при перезапуске питания.
Пробуем дозатор в симуляторе
В симуляторе та же схема: кнопка D2 выдаёт дозу, тумблер D3 включает непрерывную подачу, Serial ведёт счётчик сантиметров, циферблат повторяет обороты ролика. Одна честная оговорка: AccelStepper в движке симулятора не эмулируется, поэтому подача везде — и на плате, и в симе — идёт порциями с фиксированным темпом. Для дозатора это не потеря: пруток инерции не имеет, разгон ему не нужен, а логика порций в симе ровно та же, что в скетче выше. В демо-версии доза укорочена до сантиметра — так прогон занимает секунды, а не десятки секунд.
Прогон такой: нажмите кнопку и следите за vydano_cm= — счётчик подрастёт ровно на единицу. Потом включите тумблер: строка будет выходить примерно каждые полторы секунды, и с каждой счётчик подрастёт на сантиметр. Выключите тумблер посреди подачи — мотор встанет на середине остатка, а счётчик останется честным: он складывает выданные шаги, а не выданные намерения. Это, кстати, удобный тест на понимание: остаток в переменной ostalos и счётчик cm живут отдельно, и пауза между ними — то самое окно, в котором работает кнопка.
- Доза — окружность ролика, умноженная на долю оборота: радиус 8 мм даёт 50,3 мм за 2048 шага, сантиметр — 407 шагов
- Окружность считайте по дну канавки, а не по внешнему диаметру: разница у моего вала вышла в треть метража
- Подачу дробите на порции по 8-16 шагов: loop() остаётся живым, кнопка и тумблер опрашиваются каждые 25 мс
- Слабый прижим — проскальзывание — врущий счётчик; константу подачи поверяйте линейкой, а не калькулятором
- Выше 15 RPM 28BYJ-48 срывает шаги: для дозатора это не шум, а дыра в выданном метраже
- 1. Сколько прутка выдаёт ведущий вал за полный оборот при эффективном радиусе 8 мм?
- 2. Пруток проскальзывает в слабом прижиме. Что происходит со счётчиком сантиметров?
- 3. Доза 10 см при радиусе ролика 8 мм — сколько это шагов?
- 4. Зачем подача дробится на порции по 8 шагов?
- 5. Что станет с дозой, если выставить setSpeed(20) на 28BYJ-48?
Частые вопросы по теме урока
- Сколько прутка выдаёт ведущий вал за полный оборот при эффективном радиусе 8 мм?
- Окружность вала: 2π×8 = 50,3 мм. Радиус берите по дну канавки, где реально лежит пруток, а не по внешнему диаметру рифления. 50,3 мм — около 5 см
- Пруток проскальзывает в слабом прижиме. Что происходит со счётчиком сантиметров?
- Мотор отрабатывает шаги, счётчик их честно складывает, а пруток стоит: счёт уходит в плюс против факта. Лечится прижимом, а не кодом. Насчитывает больше фактической длины
- Доза 10 см при радиусе ролика 8 мм — сколько это шагов?
- Сантиметр — 407 шагов, десять сантиметров — 4074. При 10 RPM доза занимает около двенадцати секунд моторного времени. 4074
- Зачем подача дробится на порции по 8 шагов?
- step() блокирует loop(): при порции в 8 шагов органы управления опрашиваются каждые 25 мс, а командой в 4074 шага loop() замер бы на двенадцать секунд. Чтобы loop() оставался живым для кнопки и тумблера
- Что станет с дозой, если выставить setSpeed(20) на 28BYJ-48?
- Потолок 28BYJ-48 — около 15 RPM. Выше него редуктор гудит, шаги срываются, и счётчик складывает то, что не было выдано. Мотор загудит на месте и сорвёт шаги — счётчик соврёт