Кроковий двигун 28BYJ-48: точні повороти без бібліотек
Найдешевший кроковий двигун із драйвером ULN2003. Пишемо власний неблокуючий напівкроковий драйвер, керуємо кутом і швидкістю командами з Serial. Основа для жалюзі, годівниць і поворотних платформ.
Чим кроковий двигун кращий за сервопривід
Сервопривід повертається лише на 180°, а кроковий — на будь-який кут у будь-який бік, скільки завгодно обертів, із точністю до кроку. 28BYJ-48 з вбудованим редуктором 1:64 дає близько 4096 напівкроків на оберт (≈0.09° на крок) і помітний крутний момент — достатньо, щоб крутити ролету чи дозатор.
Компоненти
- ESP32 DevKit
- Двигун 28BYJ-48 (5 В) + плата драйвера ULN2003 (зазвичай продаються комплектом)
- Живлення 5 В ≥ 500 мА
Підключення
| ULN2003 | ESP32 |
|---|---|
| IN1 | GPIO 19 |
| IN2 | GPIO 18 |
| IN3 | GPIO 5 |
| IN4 | GPIO 17 |
| + / − | 5V / GND |
Двигун підключається до білого роз'єму на платі драйвера. Земля драйвера й ESP32 — спільна.
Як крутиться кроковик
Усередині чотири обмотки. Вмикаючи їх по черзі, ми «тягнемо» ротор по колу. Напівкроковий режим чергує одну та дві ввімкнені обмотки — 8 фаз на цикл: так рух плавніший, а кроків удвічі більше.
Код
const int PINS[4] = {19, 18, 5, 17}; // IN1..IN4
// Напівкрокова послідовність: біт 3 → IN1 ... біт 0 → IN4
const uint8_t SEQ[8] = {
0b1000, 0b1100, 0b0100, 0b0110,
0b0010, 0b0011, 0b0001, 0b1001
};
const long STEPS_PER_REV = 4096;
long position = 0; // поточна позиція в кроках
long target = 0; // куди їдемо
int phase = 0;
unsigned long stepIntervalUs = 1500;
unsigned long lastStepUs = 0;
void applyPhase(uint8_t mask) {
for (int i = 0; i < 4; i++) {
digitalWrite(PINS[i], (mask >> (3 - i)) & 1);
}
}
void setRpm(float rpm) {
rpm = constrain(rpm, 1.0f, 15.0f); // 28BYJ-48 пропускає кроки понад ~15 об/хв
stepIntervalUs = 60000000.0 / (rpm * STEPS_PER_REV);
Serial.printf("Speed: %.1f rpm (%lu us/step)\n", rpm, stepIntervalUs);
}
void handleCommand(String cmd) {
cmd.trim();
if (cmd.startsWith("rpm ")) {
setRpm(cmd.substring(4).toFloat());
} else if (cmd == "home") {
target = 0;
} else if (cmd.length()) {
float degrees = cmd.toFloat(); // «90», «-180», «720»
target += lround(degrees * STEPS_PER_REV / 360.0);
Serial.printf("Moving to %ld\n", target);
}
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(20);
for (int p : PINS) pinMode(p, OUTPUT);
setRpm(10);
Serial.println("Commands: <degrees> | rpm <1-15> | home");
}
void loop() {
if (Serial.available()) handleCommand(Serial.readStringUntil('\n'));
if (position != target && micros() - lastStepUs >= stepIntervalUs) {
lastStepUs = micros();
int dir = target > position ? 1 : -1;
phase = (phase + dir + 8) % 8;
applyPhase(SEQ[phase]);
position += dir;
if (position == target) {
applyPhase(0); // знімаємо струм — двигун не грітиметься
Serial.printf("Arrived at %ld (%.1f°)\n", position, position * 360.0 / STEPS_PER_REV);
}
}
}
Перевірка
Відкрийте Serial Monitor (115200, «Newline») і надсилайте:
| Команда | Дія |
|---|---|
90 |
повернути на 90° за годинниковою стрілкою |
-360 |
один повний оберт у зворотний бік |
rpm 5 |
змінити швидкість |
home |
повернутися в нульову позицію |
Чотири світлодіоди на платі ULN2003 показують, які обмотки зараз увімкнені — зручно для налагодження.
Нюанси
- Точне число кроків. Реальний редуктор — не рівно 1:64, а ≈1:63.68, тому точніше
STEPS_PER_REV = 4076. Для жалюзі різниця неважлива, для годинника — суттєва. - Утримання. Ми знімаємо струм після зупинки, щоб двигун не грівся. Якщо навантаження може провернути вал (вертикальна ролета), залишайте обмотки ввімкненими.
- Нуль після перезавантаження невідомий. Для абсолютного позиціонування додайте кінцевий вимикач і процедуру homing при старті.
- Потрібно більше сили чи швидкості — беріть NEMA17 з драйвером A4988/TMC2209.
Ідеї
- Автоматичні жалюзі: відкрити на світанку, закрити на заході сонця (NTP + розрахунок часу).
- Поворотний стіл для 360°-фото товарів: крок, пауза, знімок.
- Годівниця з дозатором-шнеком.