- Управление моторами ардуино через блютуз
- //Начало скетча Копировать этот код
- //Конец скетча
- Машинка на Arduino, управляемая Android-устройством по Bluetooth, — полный цикл (часть 1)
- Немного об уровне, авторе и предостережения
- Задача
- Понадобится
- Основа конструкции
- Установка Arduino
- Определение угла поворота
- Подключение угла и код
- Распараллеливание ходовых колес
- Подключение Bluetooth
- Система отправки команд
- Заключение первой части
Управление моторами ардуино через блютуз
Электрический привод на шаговых двигателях для слайдера под видеокамеру с управлением по Bluetooth на базе Arduino Nano
Плата Arduino Nano, Bluetooth модуль HC-05, 2 драйвера для шаговых двигателей DRV8825, макетная плата, соединительные провода, 2 биполярных шаговых двигателя Mitsumi, и Android смартфон.
Перед сборкой следует загрузить этот скетч на плату.
//Начало скетча Копировать этот код
const int stepMotor1 = 11; const int enableMotor1 = 12; const int dirMotor1 = 13; const int stepMotor2 = 8; const int enableMotor2 = 9; const int dirMotor2 = 10; int maxSpeed1 = 400; int minSpeed1 = 1; int speed_1 = 60; int steps_in_one_turn_1 = 48; int maxSpeed2 = 400; int minSpeed2 = 1; int speed_2 = 60; int steps_in_one_turn_2 = 48; unsigned long previousMillis1 = 0; unsigned long previousMillis2 = 0; int switch_IO = 0, switch_1 = 0, switch_2 = 0; int stepState1 = 0, x1; int stepState2 = 0, x2; void setup() Serial.begin(115200); pinMode( stepMotor1, OUTPUT); digitalWrite( stepMotor1, LOW); pinMode( enableMotor1, OUTPUT); digitalWrite( enableMotor1, HIGH); pinMode( dirMotor1, OUTPUT); digitalWrite( dirMotor1, LOW); pinMode( stepMotor2, OUTPUT); digitalWrite( stepMotor2, LOW); pinMode( enableMotor2, OUTPUT); digitalWrite( enableMotor2, HIGH); pinMode( dirMotor2, OUTPUT); digitalWrite( dirMotor2, LOW); > void loop() char vall = Serial.read(); if (speed_1 19) x1 = 1; > if (speed_1 >= 20) x1 = 2; > if (speed_1 >= 50) x1 = 5; > if (speed_1 >= 100) x1 = 10; > if (speed_1 >= 200) x1 = 20; > if (speed_2 19) x2 = 1; > if (speed_2 >= 20) x2 = 2; > if (speed_2 >= 50) x2 = 5; > if (speed_2 >= 100) x2 = 10; > if (speed_2 >= 200) x2 = 20; > if (vall == '1') speed_1 = speed_1 + x1; > if (vall == '2') speed_1 = speed_1 - x1; > if (vall == '3') speed_2 = speed_2 + x2; > if (vall == '4') speed_2 = speed_2 - x2; > if (speed_1 > maxSpeed1) speed_1 = maxSpeed1; > if (speed_1 minSpeed1) speed_1 = minSpeed1; > if (speed_2 > maxSpeed2) speed_2 = maxSpeed2; > if (speed_2 minSpeed2) speed_2 = minSpeed2; > long interval1 = 30000000 / steps_in_one_turn_1 / speed_1; long interval2 = 30000000 / steps_in_one_turn_2 / speed_2; unsigned long currentMillis1 = micros(); if (currentMillis1 - previousMillis1 >= interval1) previousMillis1 = currentMillis1; if (stepState1 == LOW) stepState1 = HIGH; > else stepState1 = LOW; > digitalWrite( stepMotor1, stepState1); > else digitalWrite( stepMotor1, LOW); > unsigned long currentMillis2 = micros(); if (currentMillis2 - previousMillis2 >= interval2) previousMillis2 = currentMillis2; if (stepState2 == LOW) stepState2 = HIGH; > else stepState2 = LOW; > digitalWrite( stepMotor2, stepState2); > else digitalWrite( stepMotor2, LOW); > if (vall == 'c') switch_1++; if (switch_1 > 1) switch_1 = 0; > if (switch_1 == 0) digitalWrite( enableMotor1, HIGH); > else digitalWrite( enableMotor1, LOW); > > if (vall == 'e') switch_2++; if (switch_2 > 1) switch_2 = 0; > if (switch_2 == 0) digitalWrite( enableMotor2, HIGH); > else digitalWrite( enableMotor2, LOW); > > if (vall == 'd') switch_IO++; if (switch_IO > 1) switch_IO = 0; > if (switch_IO == 0) digitalWrite( enableMotor1, HIGH); digitalWrite( enableMotor2, HIGH); > else digitalWrite( enableMotor1, LOW); digitalWrite( enableMotor2, LOW); > > if (vall == 'a') digitalWrite( dirMotor1, LOW); > if (vall == 'b') digitalWrite( dirMotor1, HIGH); > if (vall == 'j') digitalWrite( dirMotor2, LOW); > if (vall == 'k') digitalWrite( dirMotor2, HIGH); > if ((vall == 'L') && (digitalRead( enableMotor1) == HIGH)) digitalWrite( dirMotor1, LOW); digitalWrite( enableMotor1, LOW); > if (vall == 'l') digitalWrite( enableMotor1, HIGH); > if ((vall == 'R') && (digitalRead( enableMotor1) == HIGH)) digitalWrite( dirMotor1, HIGH); digitalWrite( enableMotor1, LOW); > if (vall == 'r') digitalWrite( enableMotor1, HIGH); > if ((vall == 'T') && (digitalRead( enableMotor2) == HIGH)) digitalWrite( dirMotor2, LOW); digitalWrite( enableMotor2, LOW); > if (vall == 't') digitalWrite( enableMotor2, HIGH); > if ((vall == 'S') && (digitalRead( enableMotor2) == HIGH)) digitalWrite( dirMotor2, HIGH); digitalWrite( enableMotor2, LOW); > if (vall == 's') digitalWrite( enableMotor2, HIGH); > >
//Конец скетча
После загрузки скетча можно собрать все, как показано на этой схеме.
Значения отправляемые кнопками.
Машинка на Arduino, управляемая Android-устройством по Bluetooth, — полный цикл (часть 1)
Подробная история того, как из трех двигателей была собрана машина на Arduino, управляемая Android-устройством по Bluetooth. В нескольких десятках абзацев постараюсь максимально пошагово изложить, куда подключить каждый из проводов, как написать фирменное приложение и на каких детских граблях пришлось попрыгать больше недели.
Немного об уровне, авторе и предостережения
Я, автор, пацан 16-17 лет с подмосковной деревни, специализируюсь на написании android-приложений (а там сложнее что-то сжечь), поэтому ответственность за оптимальный подход к решению задач с себя снимаю.
Практически каждый из нижеописанных этапов занимал у меня больше, чем стоило бы, времени. Наверно, именно по этой причине хочу поделиться опытом. И при этом буду очень рад, если поругаете за ошибки и подскажите за оптимизацию.
Задача
Задача легчайшая – заставить ездить машинку, управляемую Arduino, а пульт заменить андроидом. Но в большинстве моментов пришлось изобретать колесо, потому что в интернетах подходящего решения найдено не было.
Понадобится
- Arduino
- Motor Shield (в моем случае две)
- Bluetooth
- Android
- Провода обычные
Основа конструкции
За основу была взята машина Lego Outdoor Challenger (в реальности выглядит менее пафосно). Все, что от нее осталось: корпус (все элементы украшения сняты) и три двигателя.
У машинки была своя плата, но одна из задач подразумевала универсальность: это сделал я, это смогут повторить другие. Мозги вынул, поставил Arduino Uno.
Установка Arduino
Создатели почему-то не предусмотрели места для Arduino, потому крепил на шурупы, просверлив пластик. Под плату подложил фанеру, чтобы ничего не закоротило. Под шурупы лучше подсунуть что-то пластиковое (кусочек бутылки), ибо плата от железный болтов не защищена.
Поверх платы сразу поставил две motor shiled, так надо. Чтобы управлять второй, придется прокинуть один провод с любого digital порта на H1 (направление) и второй с пина с поддержкой шима (помечены знаком «~», обычно 10, 11) на E1 (скорость).
Определение угла поворота
За поворот машинки отвечает на удивление не сервопривод, а обычный двигатель. Встает проблема: хорошо бы было его не сжечь, ведь угол поворота ограничен, а крутиться двигатель может сколько угодно.
Вариант с методом тыка отпадает, так как при разном уровне батареи количество тока, подаваемое на двигатель, будет изменяться, что приведет к постоянно меняющемуся углу. Крутить до упора тоже нельзя, рано или поздно рассыплются шестеренки.
Решение проблемы: отслеживать угол через замыкание. На фото продемонстрирована небольшая штучка, которая крепится недалеко от поворотного механизма. На часть, которая крутится вместе с колесами влево/вправо двигателем, прикрепляется гребешок с железными контактами.
Принцип работы: к каждой линии припаивается провод (всего их четыре), нижний подключается к плюсу (он зажат гребешком всегда, см. картинку), остальные провода уходят на минус. Когда зубик гребешка попадает и на нижний ряд, и на, допустим, третий, происходит замыкание, ток течет, это замечает Arduino.
Благодаря различным комбинациям трех полос, можно определить до семи углов. Например, когда ток есть на всех линиях, колеса повернуты в крайнее правое положение, когда ток есть только на верхней, колеса повернуты максимально влево. В таблице предоставлены все варианты.
Подключение угла и код
Для каждого уровня был выбран свой цвет: нижний – зеленый, первый снизу – красный, второй – черный, третий – белый. На начальном этапе использовались breadboard и светодиоды для визуальной отладки.
Схема подключения показана на рисунке. Плюс тянем к зеленому, остальные протягиваем к минусу. Через резистор, установленный для устранения помех и отсутствия КЗ, подключаем провода к выходам A0-A2. Выбраны они просто из экономии остальных портов.
Код дан с комментариями. Подключаем пины и опрашиваем их через digitarRead(). Если напряжение есть, вернется значение true. Далее смотрим, если результат означает, что колеса в крайних положениях, запрещаем дальнейший поворот в эту сторону.
Небольшая хитрость: поскольку выходы на 5В и 3.3В понадобятся в будущем, можно поставить плюс на один из digital-пинов. Перед каждой проверкой угла выдавать ток через digitalWrite(whitePin), потом проверять угол и убирать ток.
int speedTurn = 180; //скорость поворота, от 0 до 255 //пины для определения поворота int pinRed = A0; int pinWhite = A1; int pinBlack = A2; int pinAngleStop = 12; //выводит ток на светодиод, если достигнут максимальный угол, нужен //только для отладки void setup() < //пины поворота на считывание pinMode(pinRed, INPUT); pinMode(pinBlack, INPUT); pinMode(pinWhite, INPUT); //светодиод pinMode(pinAngleStop, OUTPUT); //пины драйвера двигателя, направление и скорость pinMode(angleDirection, OUTPUT); pinMode(angleSpeed, OUTPUT); Serial.begin(9600); >//функция вызывается из loop(), когда приходит команда с андроида void turn(int angle) < digitalWrite(pinAngleStop, HIGH); //выдаем ток на провод, подключенный к плюсу delay(5); //немного ждем, чтобы ток "успел" дойти if(angle >149) < if( digitalRead(pinWhite) == HIGH && digitalRead(pinBlack) == LOW && digitalRead(pinBlack) == LOW) < //если достигнуто крайне правое положение, выйти из функции не подавая ток, чтобы не //сжечь мотор return; >//если угол не максимальный, поворачиваем digitalWrite(angleDirection, HIGH); analogWrite(angleSpeed, speedTurn); > else if (angle < 31) < if(digitalRead(pinRed) == HIGH && digitalRead(pinBlack) == HIGH && digitalRead(pinWhite) == HIGH) < //если достигнуто крайне левого положение, выйти из функции не подавая ток, чтобы не //сжечь мотор return; >//если угол не максимальный, поворачиваем digitalWrite(angleDirection, LOW); analogWrite(angleSpeed, speedTurn); > digitalWrite(pinAngleStop, LOW); //убираем ток с определителя угла delay(5); >
Распараллеливание ходовых колес
Изначально два ходовых двигателя соединены вместе. Их рассоединил по двум причинам: поворот эффективней, если колеса крутятся в разные стороны, и два мощных двигателя одна плата не вытянет.
Проблема: у motor shield два выхода, каждый из которых выдает до 2 ампер. Каждый двигатель ест по 0,7А. Вроде меньше, но не при максимальных нагрузках. Допустим, машинка застряла в песке или уперлась, ток возрастает выше ампера. Не критично, но потенциально опасно.
А вот критичным оказалось то, что плата греется. Через минуты полторы после заезда, motor shield нагревалась и начинала работать безобразно: токи подаются не те, колеса не крутятся и прочее.
Решение обоих проблем: один двигатель подключил к одной motor shield, второй – к другой. Как ни странно, помогло. Температура упала, перегрев отсутствует. Можно было поставить радиатор, но крепить тяжело.
Подключение Bluetooth
Я использовал модель HC-05, что сыграло роковую шутку. Подключаются все блютузы одинаково: один провод на 3.3В (иногда начинал работать только от 5В), второй на минус, еще два на порт 0 и 1 (чтение и отправка соответственно). Провод, подписанный RXD на bluetooth, втыкается в TXD ардуино, а TXD в RXD (если перепутаете, то данных не увидите).
Есть оговорка: порты 0 и 1 по умолчанию используются Serial, через который заливает скетч. То есть, пока воткнут блютуз, скетч не зальется. Есть два выхода: вынимать блютуз на время заливки или переназначить входы и выходы блютуза. Второй вариант осуществляется двумя строчками
#include \\подключение библиотеки SoftwareSerial BTSerial(8, 9); \\установка 8 и 9 пина заместо 0 и 1
Подводный камень, съевший у меня трое суток работы – скорость общения. По привычке установил 9600 и пошел пробовать. То данные не приходили, то была каша символов. И в конце концов ответ – модель HC-05 общается на 38400! Очень сильно обратите внимание на то, что в Setup() я выполню BTSerial.begin(39400), хотя Serial.begin(9600).
Система отправки команд
Статья становится слишком длинной, поэтому рассмотрение кода Arduino и Android вынесу в отдельную вторую часть, а сейчас опишу принцип.
На андроид устройстве есть джойстик (круг, о реализации которого также во второй части). Андроид считывает показания с него и конвертирует их в подходящие для ардуино числа: скорость из пикселей превращает в значение от -255 до 255 (отрицательные – задний ход), а также определяет угол. Я сознательно отдал эту задачу телефону, так как он куда мощнее и спокойно справится с подсчетом нескольких сотен значений в секунду.
После установки сокета данные отправляются в следующем формате: @скорость#*угол#. @ — говорит о том, что следующие цифры содержат скорость, # — извещает об окончании значения скорости, * — начало значения угла, # — закончить запись угла. Цикл бесконечен, команды отправляются каждые 100 миллисекунд (цифра подобрана оптимальная). Если ничего не нажато на андроиде, то ничего и не отправляется.
Алгоритм приема данных подробно описан в коде скетча. Он не раз переписывался и, как по мне, работает идеально.
Заключение первой части
В этой статье я попытался раскрыть все, что касается физической части машинки. Вероятнее всего, что-то упустил, так что обязательно спрашивайте.
Но самое интересное, как по мне, осталось на второе – программа Arduino и приложение на Android, там творится настоящая магия, по крайней мере, для молодого меня.
Если вы не найдете ответа на какую-то часть и захотите потыкать меня в недостатки лично, жду – dendolg1@mail.ru, .
UPD: вторая часть уже вышла — habr.com/post/424813