MotorStep V6 — Документация

Система управления поворотным столом на шаговом моторе · ESP32-C3 Mini

1. Назначение

MotorStep V6 — прошивка для управления поворотным столом на шаговом моторе через драйвер TB6600. Предназначена для производственных задач, где требуется:

  • Точное позиционирование стола в диапазоне 0…360°
  • Плавное движение (трапециевидный профиль скорости) для тяжёлых столов (50–1500 кг)
  • Автоматический поиск нуля (концевик) при старте
  • Ручное управление через энкодер и кнопки
  • Внешнее управление через Serial (Console) — в дальнейшем Wi-Fi/WebSocket
  • Обратная связь по углу с оптического энкодера MBL3806
Ключевая архитектура: 7 независимых классов (Display, Buttons, EncoderHW040, Settings, Position, Motor, Console), связанных через главный файл MotorStep_v6.ino. Классы не знают друг о друге — только через main.

2. Возможности

🎯 Точность

0.1° по MBL3806 (24000 импульсов на оборот стола при GEAR_RATIO=10)

⚡ Плавность

Трапециевидный профиль: разгон, плато, торможение. Настройка через NVS.

🏠 Автопоиск нуля

4 этапа: отход, быстрый поиск, откат, медленный подход. Контроль по 405°.

🛑 Мгновенный stop

Остановка за один шаг (≤800 мкс). Без инерции, без «догонов».

📟 Дисплей TM1637

3 значения: цель, угол стола, MBL3806. Режимы d1/d2. Индикация Run, EROR, BLOC/OFF.

💾 NVS-память

Сохранение шага, флага findHome, accel/cruise/start в энергонезависимую память.

🔄 Протокол Console

Команды move/stop/home/... + запросы status?/motor?/mbl?. Ответы OK/BUSY/DONE/READY.

🎛️ Инверсия MBL

Переключение направления через #define POSITION_INVERTED — без перепайки.

3. Подключение элементов

3.1. Карта пинов

GPIO Функция Что подключено Примечание
0EncoderHW040 CLKHW-040 CLKISR, CHANGE
1EncoderHW040 DTHW-040 DTISR, CHANGE
2Кнопка дисплеяКнопка (на GND)Strapping — свой опрос
3EncoderHW040 SWHW-040 SWGyverButton
4TM1637 CLKДисплей TM1637—
5TM1637 DIOДисплей TM1637—
6TB6600 STEPTB6600 PUL+Импульсы шага
7TB6600 DIRTB6600 DIR+Направление
8Концевик HOMEМеханический/индуктивныйINPUT_PULLUP, LOW = сработал
9Резерв—Свободен
10TB6600 ENATB6600 ENA+LOW = мотор включён
20MBL3806 AMBL3806 AUART0 RX — 1 кОм, INPUT_PULLUP
21MBL3806 BMBL3806 BUART0 TX — 1 кОм, INPUT_PULLUP
Внимание! GPIO 20 и 21 — это UART0 (RX/TX). В V6 Serial для Console использует USB CDC (виртуальный порт), который не занимает GPIO 20/21. Поэтому MBL3806 работает на этих пинах без конфликтов.

3.2. Схема соединений

TB6600 (драйвер шагового мотора)

ESP32-C3           TB6600
─────────────────────────────
GPIO 6  ──── 1кОм ──── PUL+ (STEP)
GPIO 7  ──── 1кОм ──── DIR+
GPIO 10 ──── 1кОм ──── ENA+
GND     ────────────── PUL- / DIR- / ENA- (общий)

MBL3806 (оптический энкодер на моторе)

ESP32-C3           MBL3806
─────────────────────────────
+5V     ────────────── VCC (красный)
GND     ────────────── GND (чёрный)
GPIO 20 ──── 1кОм ──── A (зелёный)
GPIO 21 ──── 1кОм ──── B (белый)
Важно: MBL3806 — с открытым коллектором (NPN). Подтяжка к 3.3 В — внутренняя (INPUT_PULLUP). Резистор 1 кОм в линии A/B защищает GPIO. Питание MBL3806 — строго 5 В.

HW-040 (энкодер ручного управления)

ESP32-C3           HW-040
─────────────────────────────
+3.3V   ────────────── VCC
GND     ────────────── GND
GPIO 0  ────────────── CLK
GPIO 1  ────────────── DT
GPIO 3  ────────────── SW (кнопка)

TM1637 (дисплей)

ESP32-C3           TM1637
─────────────────────────────
+5V     ────────────── VCC
GND     ────────────── GND
GPIO 4  ────────────── CLK
GPIO 5  ────────────── DIO

Кнопка дисплея

ESP32-C3
─────────────────────────────
GPIO 2 ──[Кнопка]── GND

Концевик HOME

ESP32-C3
─────────────────────────────
GPIO 8 ──[Концевик NO]── GND

При срабатывании замыкается на GND → LOW. В покое — HIGH (INPUT_PULLUP).

3.3. Питание

ПотребительНапряжениеТокИсточник
ESP32-C3 Mini5 В (USB)~250 мАUSB или БП
TB6600 (логика)5 В~20 мАБП 5 В
TB6600 (мотор)24–48 Вдо 3 АОтдельный БП
MBL38065 В~50 мАБП 5 В
TM16375 В~30 мАБП 5 В
HW-0403.3 В~5 мАESP32
Общий GND обязателен! GND всех устройств (ESP32, TB6600, MBL3806, TM1637, БП) должны быть соединены.

4. Настройки

4.1. Пины (в коде)

Пины заданы в соответствующих .cpp файлах классов:

КлассФайлЧто настраивается
DisplayDisplay.cppPIN_CLK_DISP=4, PIN_DIO_DISP=5
ButtonsButtons.cppPIN_ENC_SW=3, PIN_DISP_BTN=2, PIN_HOME=8
EncoderHW040EncoderHW040.cppPIN_ENC_CLK=0, PIN_ENC_DT=1
PositionPosition.cppPIN_MBL_A=20, PIN_MBL_B=21
MotorMotor.cppPIN_STEP=6, PIN_DIR=7, PIN_ENA=10

4.2. Механика (в коде)

Motor.cpp — в начале файла

static const int32_t STEPS_PER_REV = 800;   // шагов мотора на оборот (1/4 микрошаг)
static const float   GEAR_RATIO    = 10.0;  // ← РЕДУКТОР (мотор:стол = 10:1)
static const int32_t ANGLE_MIN = 0;
static const int32_t ANGLE_MAX = 360;
GEAR_RATIO — это главный механический параметр. Зависит от производства (1..100). Меняется в коде — перепрошивка.

Параметры плавности (в Motor, дефолты — в Settings)

ПараметрДефолтДиапазонЧто значит
accel10001..2000Шагов на разгон/торможение
cruise80050..10000 мксЗадержка на плато (макс. скорость)
start5000100..20000 мксЗадержка на старте/стопе

Position.cpp — инверсия

#define POSITION_INVERTED  0   // 0 = прямой, 1 = инверсный (A/B поменяны)

4.3. NVS (энергонезависимая память)

Сохраняются в NVS (namespace "motorstep"):

КлючТипДефолтЧто значит
stepIdxint4Индекс шага (0..4 → 180°/90°/10°/5°/1°)
findHomebooltrueИскать ноль при старте
accelint1000Параметр плавности
cruiseint800Параметр плавности
startint5000Параметр плавности

Управление через команды accel/cruise/start/save/load (см. раздел 5.1).

5. Управление

5.1. Console — команды (Serial)

Скорость: 115200. Line Ending: Newline или Both NL & CR.

Действия (без "?")

КомандаЧто делаетОтвет
move <angle>Перейти к углу (0..360)OK / BUSY
homeПоиск нуляOK
resetСброс позиции в 0OK
holdУдержание/освобождение мотораOK
stopМгновенный стопOK
stop!Стоп с ошибкойOK
clearСброс ошибкиOK
step <idx>Установить индекс шага (0..4)OK
nextСледующий шагOK
accel <n>Установить accel (сохр. в NVS)OK
cruise <n>Установить cruise (сохр. в NVS)OK
start <n>Установить start (сохр. в NVS)OK
saveСохранить в NVSOK
loadЗагрузить из NVSOK
hb <0|1>Heartbeat вкл/выклOK
helpСписок командOK + help

Запросы (с "?")

КомандаОтвет
ready?READY / BUSY
status?IDLE / MOVING
angle?Целевой угол
motor?Угол стола (факт)
mbl?Угол MBL3806
step?Индекс шага (0..4)
error?OK / ERROR
hold?ON / OFF
hb?ON / OFF
accel? / cruise? / start?Значение параметра

Асинхронные события (ESP → ПК)

СобытиеКогда
DONEМотор завершил движение
READYКонтроллер свободен
PROGRESS: <angle>Каждый 1° во время движения (если включено)
HBHeartbeat (если включён)
PROGRESS блокирует мотор! Каждый Serial.println блокирует loop на ~1 мс, что даёт рывки. В V6 по умолчанию PROGRESS может быть закомментирован — рекомендую не использовать во время движения.

5.2. Кнопки

КнопкаДействиеЧто происходит
HW-040 SW (GPIO 3)Короткий кликСмена шага (180° → 90° → 10° → 5° → 1° → 180°)
HW-040 SWДвойной кликУдержание мотора (BLOC / OFF)
HW-040 SWДлинное удержаниеСброс позиции (motor + position + target = 0). Если ошибка — сброс ошибки
GPIO 2КликПереключение режима дисплея (d1 ↔ d2)

5.3. Энкодер HW-040

Вращение энкодера накапливает целевой угол. После паузы (1 сек) — мотор плавно едет к цели.

ДействиеЧто происходит
Вращение вправо_targetAngle += step, дисплей показывает цель
Вращение влево_targetAngle -= step
Пауза 1 секmotor.moveTo(_targetAngle) — плавное движение

5.4. Дисплей TM1637

Режимы (переключаются кнопкой GPIO 2)

РежимИндикаторФорматЧто показывает
d1d 1XXXУгол стола (факт мотора)
d2d 2_XXXУгол MBL3806 (с подчёркиванием)

Приоритет отображения (по убыванию)

  1. EROR — мигает при ошибке
  2. Run — во время движения мотора
  3. S XX — 1 сек после смены шага
  4. BLOC / OFF — 1 сек после переключения удержания
  5. d1 / d2 — 1 сек после переключения режима
  6. Цель — 1 сек после вращения энкодера
  7. Угол — основное (d1: мотор, d2: MBL)

Специальные сообщения

ТекстЧто значит
RunМотор движется (R большая, u n маленькие)
ERORОшибка (мигает)
S 90Выбран шаг 90°
BLOCМотор удерживается
OFFМотор освобождён (можно крутить руками)
d 1 / d 2Режим дисплея (угол стола / MBL)

6. Как компилировать

6.1. Плата

ПараметрЗначение
BoardESP32C3 Dev Module
ЧипESP32-C3
ПлатаESP32-C3 Mini V1 (или аналог)

6.2. Библиотеки

Установить через Library Manager (Sketch → Include Library → Manage Libraries):

БиблиотекаАвторДля чего
TM1637DisplayAvishay OrpazДисплей
GyverButtonAlexGyverКнопка энкодера (SW)
GyverEncoderAlexGyverНЕ используется в V6 (только для справки)
GyverEncoder НЕ используется в V6! Класс EncoderHW040 написан вручную (ISR + квадратурный декодер). Библиотека GyverEncoder не требуется.

6.3. Настройки Arduino IDE (Tools)

НастройкаЗначениеПочему
BoardESP32C3 Dev ModuleПравильный чип
USB CDC On BootEnabledSerial через USB (Console работает)
CPU Frequency160 MHzСкорость
Flash Size4MB (32Mb)Стандарт
Upload Speed921600Быстрая прошивка
JTAG AdapterDisabledНе занимает GPIO 0-5
USB DFU On BootDisabledНе нужно
USB CDC On Boot = Enabled обязателен! Без него Serial не будет работать, Console не примет команды.

6.4. Прошивка

  1. Подключить ESP32-C3 через USB
  2. Выбрать порт в Tools → Port
  3. Нажать Upload (Ctrl+U)
  4. Если не прошивается: зажать кнопку BOOT на плате, нажать RESET, отпустить RESET, отпустить BOOT. Затем Upload
  5. После прошивки — нажать RESET
Первое подключение: после прошивки ESP32-C3 перезагружается, USB-порт пересоздаётся. Serial Monitor открывать через 2-3 сек.

7. Известные проблемы и особенности

Плавность vs Console

Serial-вывод (Serial.println) блокирует loop на ~1 мс. Это вызывает рывки мотора. Решение: не выводить PROGRESS во время движения; используйте запрос motor? после DONE.

Подергивание при активном дисплее

TM1637 — I2C-подобный протокол (не аппаратный). Каждое обновление ~1-2 мс. Решение: обновлять дисплей только когда мотор не движется (display.setRun(motorBusy) уже это делает).

Пропуски энкодера HW-040

Механический энкодер даёт дребезг. Решение: антидребезг 2000 мкс в ISR + REPORT_MIN_INTERVAL_MS = 50 мс. Редкие пропуски возможны.

Двойное срабатывание кнопки GPIO 2

Strapping-пин, дешёвая кнопка. Решение: state machine в Buttons.cpp, антидребезг 50 мс.

Конфликт GPIO 20/21 (UART0)

GPIO 20/21 — это UART0 (RX/TX). Serial для Console использует USB CDC (виртуальный COM-порт), который не занимает эти пины. Поэтому MBL3806 работает на них.

8. Проверено и однозначно не работает

ЧтоРезультатПричина
GyverButton на GPIO 2 НЕ работает GPIO 2 — strapping-пин. GyverButton не срабатывает стабильно. Заменено на state machine.
GyverEncoder на ESP32-C3 НЕ работает GyverEncoder писалась под AVR/ESP8266. На C3 работает нестабильно (пропуски, двойные срабатывания). Заменено на собственный ISR-декодер.
Точка на TM1637 НЕ работает showNumberDecEx не выводит точку (особенность библиотеки/дисплея). Заменено на подчёркивание _ перед числом.
GPIO 20/21 как обычные GPIO НЕ работает с Serial.begin() Если вызвать Serial.begin() для UART0 (не USB CDC) — GPIO 20/21 заняты. В V6 Serial через USB CDC — конфликта нет.
PROGRESS каждые 1° НЕ работает плавно Serial.println блокирует loop → рывки мотора. Рекомендуется отключить.
Пины 0,1 при загрузке Осторожно GPIO 0 — strapping (boot). HW-040 CLK на GPIO 0 — если вращать при загрузке, может помешать. На практике работает.
GPIO 3 при загрузке Осторожно GPIO 3 — strapping (JTAG source). HW-040 SW на GPIO 3. Если нажать при загрузке — не критично. С JTAG Adapter = Disabled — работает.
GPIO 8 при загрузке Осторожно GPIO 8 — strapping. Концевик HOME на GPIO 8. Должен быть HIGH при загрузке (концевик разомкнут). Если концевик NC — не запустится.
GPIO 9 при загрузке ОК Strapping (BOOT). Не используется. Свободен.

9. Состав проекта

MotorStep_v6/
├── MotorStep_v6.ino       ← main (дирижёр)
├── Display.h  / Display.cpp        ← TM1637 (3 значения, режимы, индикация)
├── Buttons.h  / Buttons.cpp        ← HW-040 SW + GPIO 2 + концевик
├── EncoderHW040.h / EncoderHW040.cpp ← HW-040 (свой ISR-декодер)
├── Settings.h / Settings.cpp       ← NVS (step, findHome, accel, cruise, start)
├── Position.h / Position.cpp       ← MBL3806 (ISR + GEAR_RATIO + инверсия)
├── Motor.h    / Motor.cpp          ← шаговый мотор (трапеция, findHome, stop)
└── Console.h  / Console.cpp        ← Serial-протокол (команды/ответы)

Зависимости между классами

Классы не знают друг о друге. Все связи — через main:

main → motor.moveTo(angle)     ← main передаёт данные
main → display.setMotorAngle() ← main передаёт данные
main → settings.getStep()      ← main читает
main → position.getAngle()     ← main читает
main → console.sendDone()      ← main отправляет события
Архитектура V6: каждый класс — независимый модуль. Замена одного класса (например, Display на LCD) не требует изменений в других.