Arduino блютуз работа с системой без программ. Уроки Arduino: управление устройствами со смартфона для чайников
Для беспроводного обмена информации, между различными устройствами на небольших дистанциях, можно использовать Bluetooth-технологию. Для этих целей китайской промышленностью выпускаются различные Bluetooth-модули, среди которых широкую популярность получили модели HC-05 и HC-06. В своё время на Алиэкспресс я приобрёл HC-06, на котором и буду проводить дальнейшие эксперименты, а так же подключать к своему китайскому аналогу Arduino.
Непосредственно сам Bluetooth-модуль. В таком виде он может использоваться в различных устройствах, где впаивается непосредственно в плату. Модуль имеет 34 вывода, среди которых: выводы для соединения с компьютером по UART, передачи звука по методу PCM (импульсно кодовая модуляция), аналоговый вход и выход, контакты для подключения к USB разъёму, SPI интерфейс, а также порты общего назначения. На самом деле не все эти выводы доступны к использованию, заблокирован доступ к выводам передачи звука (5-8), аналоговым входу и выходу (9, 10), а так же к группе контактов для подключения к USB (15, 20).
HC-06 построен на чипе BC417 (BlueCore4-External device), спецификация Bluetooth V2.0. На борту так же имеется кварц на 26 МГц и флеш-память на 8 Мб. Питание осуществляется от 3,3В.
Для своих конструкций удобнее всего использовать модуль с макетной платой, на которой выведены необходимые контакты для подключений, а так же имеется стабилизатор напряжения, который позволяет питаться от 5 вольт.
При покупки подобных модулей, можете столкнуться с различными их маркировками. По сути это одно и то же устройство, отличающееся прошивкой, которая определяет режим модуля (master / slave).
Отличие HC-05 от HC-06 состоит в том, что HC-05 можно использовать в качестве ведомого (master) или ведущего (slave). Данные режимы задаются с помощью АТ-команд. Кнопка на плате модуля переключает его в режим ввода АТ-команд. Режим master даёт возможность модулю самому обнаруживать подобное устройство и организовывать связь с ним. Модуль HC-06 может работать только в одном фиксированном режиме slave.
Так же существуют модели HC-03 и HC-04, но они выпускаются для промышленных целей.
Обозначение контактов на плате HC-06.
STATE
- проверка состояние модуля. На этом контакте дублируется сигнал, который отображает встроенный на плате светодиод. Если он очень быстро мигает, значит модуль находится в активном состоянии. В режиме установления связи ток составляет 30-40 мА. Когда модуль устанавливает связь с другим устройством, светодиод просто горит. В режиме передачи данных ток составляет 8 мА.
RX
- получение данных.
TX
- передача данных.
GND
- подключается к земле.
+5V
- питание +5 Вольт
EN
- включение / выключение модуля. Если на этот контакт подать логическую 1 (5 вольт), модуль выключится, если подать логический 0 или не подключаться к этому контакту, модуль будет работать.
По-умолчанию HC-06 имеет имя для обнаружения другими устройствами «HC-06 », с пин-кодом «1234 ».
Для изменения этих данных нужно модуль подключить к компьютеру и с помощью программы-терминала ввести АТ-команды. Это можно сделать с помощью переходника USB to COM с TTL уровнями. Для этих целей подойдёт адаптер на PL2303HX , CH340G , либо другой подобный. На компьютере к этому моменту уже должен быть установлен драйвер для USB адаптера.
Контакты соединяем следующим образом:
HC-06 | Переходник USB to COM |
+5V | +5V |
GND | GND |
RX | TX |
TX | RX |
При подключении этой сборки к компьютеру, красный светодиод на плате Bluetooth-модуля начнёт быстро мигать. Для ввода АТ-команд можно воспользоваться любой терминальной программой, я использую для этой цели Arduino IDE.
В настройках выбираем номер виртуального COM-порта, на котором подключен USB адаптер, в моём случае это COM44. Откроем окно «Монитора последовательного порта ».
Введём заглавными буквами команду «AT », если устройство удачно подключилось, в ответ должно возвратится «ОК ». По-умолчанию скорость обмена данными 9600 бод.
Для проверки версии прошивки, введём команду «AT+VERSION », в моём случае ответ возвратился «OKlinvorV1.8 ».
Командой «AT+NAMEyyy », где ууу - произвольный набор символов на латинице, можем сменить имя, по которому будут находить наш модуль bluetooth-устройства (по-умолчанию HC-06). В своём примере я ввёл «AT+NAMEfox », ответ возвращается OKsetname .
Командой «AT+PINxxxx », где xxxx – произвольный набор цифр, можно сменить пин-код (по-умолчанию 1234). В ответ возвращается «OKsetPIN ».
Командой «AT+BAUDx », где x – значение от 1 до 9, можно изменить скорость в бодах (по-умолчанию 9600).
1 - 1200
2 - 2400
3 - 4800
4 - 9600
5 - 19200
6 - 38400
7 - 57600
8 - 115200
9 - 230400
Допустим, если ведём команду «AT+BAUD8
», где 8 - скорость 115200 бод, в ответ возвратится «OK115200
».
Подключение HC-06 к Arduino.
Пример 1. Зальём в Arduino скетч, с помощью которого будем включать и выключать светодиод, распаянный на 13 пине Arduino, через Bluetooth-модуль.
int data; int LED = 13; void setup() { Serial.begin(9600); pinMode(LED, OUTPUT); digitalWrite(LED, HIGH); } void loop() { if (Serial.available()) { data = Serial.read(); if (data == "1") { digitalWrite(LED, HIGH); } if (data == "0") { digitalWrite(LED, LOW); } } } |
Если у вас нет на плате данного светодиода, подключите его к 13 пину по схеме, номиналы резисторов можно взять от 220 Ом до 1кОм.
Обесточим Arduino и подключим HC-06. Контакт TX модуля подключаем к контакту RX Ардуино (пин 0 на плате UNO), а контакт RX , к контакту TX (пин 1). Контакты +5V и GND , подключаются к соответствующим пинам Ардуино.
Конструкция в сборе. Подав питание на Ардуино, светодиод на HC-06 должен быстро мигать.
Управление Arduino через HC-06, с помощью компьютера.
Пример буду показывать на Windows XP. К компьютеру должен быть подключен любой USB Bluetooth-адаптер и установленны на него драйвера.
В моём случае использовался вот такой адаптер.
При его подключении к ПК, в трее (возле часов) появляется соответствующая иконка.
Нажимаем правой кнопкой мыши на данной иконке и в контекстном меню выбираем «Добавить устройство Bluetooth ».
Ставим переключатель «Устройство установлено и готово к обнаружению » и нажимаем «Далее ».
В списке обнаруженных устройств отобразится наш Bluetooth-модуль, с новым именем, которое я поменял ранее. Выбираем его и нажимаем «Далее ».
В следующем окне вводим пин-код для подключения к модулю, выбрав пункт «Использовать ключ доступа из документации: ». По-умолчанию это «1234», но я его сменил на «9999». Нажимаем «Далее ».
Bluetooth-модуль успешно связался с компьютером, о чём можно узнать с соответствующего окна. Следует обратить внимание, при образовании связи между двумя Bluetooth-устройствами, на компьютере создастся два новых виртуальных COM-порта (будут отображаться в диспетчере устройств), один для исходящих данных, другой для входящих. Нам понадобится порт для исходящих данных, в моём случае это COM45.
Вновь открываем какую-нибудь программу-терминал, я воспользуюсь Arduino IDE. В настройках выбираем исходящий порт (у меня COM45), открываем «Монитор последовательного порта », вводим переменно команды «1 » и «0 ». При команде «1» светодиод загорается, при «0» гаснет.
Управление Arduino через HC-06 с помощью смартфона.
Наверное уже все современные смартфоны имеют в своём составе встроенный Bluetooth. Остаётся только установить программу терминал. В моём случае использовалась Lumia 640 DS , с установленной Windows mobile 10 . В качестве программы терминала использую «». На Android-смартфонах всё аналогично, в качестве программы можно использовать «Bluetooth Terminal ».
Для обладателей смартфонов компании Apple не получится работать с Bluetooth-модулями HC-05 и HC-06 (Bluetooth V2.0), для них придётся использовать адаптеры с технологией BLE (Bluetooth low energy), спецификация Bluetooth 4.0. Ярким представителем является модуль HM-10.
Сопрягаем HC-06 с блютузом смартфона.
1. В «Параметрах
» смартфона выбираем пункт «Устройства
».
2. Далее «Bluetooth и другие устройства
».
3. Включаем ползунком блютуз смартфона и нажимаем на «плюс в квадрате
» для поиска и добавления нового Bluetooth-устройства, в нашем случае HC-06.
4. В разделе «Добавить устройство
» выбираем «».
5. Смартфон произведёт поиск Bluetooth-устройств находящихся в округе и отобразит их имена. В моём примере найден модуль HC-06, который я переименовал в «fox
».
6. Вводим пин-код устройства, к которому подключаемся, нажимаем кнопку «Подключиться ».
Устанавливаем и запускаем терминальную программу «». Подключенный к смартфону модуль HC-06, отображается в списке, выбираем его.
Появится поле для ввода команд и кнопка для отправки. Вводим попеременно «1 » и «0 » и наблюдаем за светодиодом на 13 пине, который будет зажигаться или гаснуть.
Пример 2 . Подключим к Arduino три светодиода и попробуем управлять ими через Bluetooth-модуль HC-06.
Светодиоды подключил к 9, 11 и 13 пину, если будите использовать другие пины, их нужно указать в скетче.
int data; int LED1 = 9; int LED2 = 11; int LED3 = 13; void setup() { Serial.begin(9600); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); } void loop() { if (Serial.available()) { data = Serial.read(); if (data == "1") { digitalWrite(LED1, HIGH); } if (data == "2") { digitalWrite(LED1, LOW); } if (data == "3") { digitalWrite(LED2, HIGH); } if (data == "4") { digitalWrite(LED2, LOW); } if (data == "5") { digitalWrite(LED3, HIGH); } if (data == "6") { digitalWrite(LED3, LOW); } } } |
При заливке скетча модуль HC-06 нужно отсоединить от Ардуино, потому что и компьютер, и блютуз работают по UART. Ардуина не может на одном UART работать с несколькими устройствами.
Запускаем программу-терминал и пробуем скетч в действии. Смысл управления светодиодами следующий, имеется три светодиода, каждый из которых может быть либо включенный, либо выключенный. Получается 6 команд. Команды представляют из себя цифры от 1 до 6 (1, 3, 5 - зажечь 1, 2 и 3 светодиоды. 2, 4, 6 - погасить). Цифры можно вводить по одной, либо в виде комбинаций. Например: 145 - зажечь первый, погасить 2-й, зажечь 3-й.
Пример 3 . Подключим к Arduino реле-модуль из статьи « » и попробуем управлять мощной нагрузкой через Bluetooth. По сути, у нас получится «умная розетка». К реле-модулям можно подключать различные нагрузки (лампочки, насосы, вентиляторы) и управлять ими на расстоянии.
Скетч по этому примеру появится в ближайшее время.
В этой статье приведены разные варианты управления реле в скетчах ардуино. Примеры тестировались на Arduino Uno, но они могут быть легко применимы для работы на других платах Arduino: Uno, Mega, Nano.
Схема подключения
В данном примере используется стандартный , на котором уже установлены все необходимые элементы для подключения к. Схема подключения очень проста: модуль реле присоединяется к 5 пину платы Ардуино. При этом для простоты мы можем даже не присоединять реальную нагрузку – реле будет щелкать при каждом изменении состояния, мы услышим эти щелчки и будем понимать, что скетч работает.
Скетч для работы с реле
/* * Скетч для управления реле с помощью ардуино * Используем реле SONGLE SRD-05VDC * Реле ОТКРЫВАЕТСЯ при подаче низкого уровня сигнала (LOW) на управляющий пин. * Реле ЗАКРЫВАЕТСЯ при подаче высокого уровня сигнала (HIGH) на управляющий пин. * * В данном примере мы просто открываем и закрываем реле раз в 5 секунд. * * PIN_RELAY содержит номер пина, к которому подключено реле, которым мы будем управлять * * В функции setup устанавливаем начальное положение реле (закрытое) * Если к реле будет подключена нагрузка(например, лампочка), то после запуска скетча она будет включаться и выключаться каждые 5 секунд * * Для изменения периода мигания нужно изменить параметр функции delay(): поставив 1000 миллисекунд, выполучите 1 секунду задержки * * В реальных проектах реле включается в ответ на обнаружение каких-либо внешних событий через подключение датчиков * */ #define PIN_RELAY 5 // Определяем пин, используемый для подключения реле // В этой функции определяем первоначальные установки void setup() { pinMode(PIN_RELAY, OUTPUT); // Объявляем пин реле как выход digitalWrite(PIN_RELAY, HIGH); // Выключаем реле - посылаем высокий сигнал } void loop() { digitalWrite(PIN_RELAY, LOW); // Включаем реле - посылаем низкий уровень сигнала delay(5000); digitalWrite(PIN_RELAY, HIGH); // Отключаем реле - посылаем высокий уровень сигнала delay(5000); }Скетч управления реле с датчиком движения
В реальных проектах изменение состояния реле должно происходить в ответ на какую то реакцию среды. Например, в ответ на сигнал сработавшего датчика движения можно включить свет, замкнув цепь с помощью реле. В данном скетче мы рассмотрим такой вариант подключения.
Схема подключения реле
Следует понимать, что в реальных проектах обходятся вообще без ардуино – просто подключая сигнальный выход датчика к реле.
Пример скетча
В данном примере мы добавим в цикл loop проверку состояния PIR датчика с помощью функции digitalRead (). Если мы получаем HIGH, то это означает сработку датчика и мы выполняем действие – включаем реле. Если к нему присоединить лампочку, то она загорится. Но, как и в прошлом примере, можно просто послушать щелчки.
/* Скетч для управления реле ардуино с помощью PIR датчика PIN_RELAY содержит номер пина, к которому подключено реле, которым мы будем управлять PIN_PIR содержит номер пина с подключенным PIR-сенсором В функции setup устанавливаем начальное положение реле (закрытое) В теле функции loop проверяем наличия высокого уровня сигнала от датчика с помощью функции digitalRead Для отладки текущее значение датчика выводим в окно монитора порта */ #define PIN_RELAY 8 // Определяем пин, используемый для подключения реле #define PIN_PIR 5 // Определяем пин, используемый для подключения PIR-датчика // В этой функции определяем первоначальные установки void setup() { Serial.begin(9600); pinMode(PIN_RELAY, OUTPUT); // Объявляем пин реле как выход digitalWrite(PIN_RELAY, HIGH); // Выключаем реле - посылаем высокий сигнал } void loop() { int val = digitalRead(PIN_PIR); // Считваем значение с датчика движения в отдельную переменную if (val == HIGH) { Serial.println("Датчик сработал"); digitalWrite(PIN_RELAY, LOW); // Включаем реле - посылаем низкий уровень сигнала } else { digitalWrite(PIN_RELAY, HIGH); // Отключаем реле - посылаем высокий уровень сигнала } delay(1000); // Проверяем значения один раз в секунду. }
В этой статье представлена пошаговая инструкция, которая поможет вам самостоятельно создать приложение для Android-смартфона, предназначенное для управления чем-либо через Bluetooth. Для демонстрации мы подробно разберем пример мигания светодиодом на Arduino по командам с телефона или планшета. В результате выполнения наших инструкций вы научитесь делать вот так:
Для управления домашним роботом достаточно добавить кнопок и обработать их команды на стороне Arduino.
Что для этого потребуется
- Любая Arduino-совместимая плата
- Bluetooth-модуль
- Устройство на котором установлена ОС Android
В качестве Bluetooth-модуля лучше всего использовать HC-05. Его легко купить в китайском интернет магазине или на eBay. Модуль питается от 3.3 В, но его линии I/O могут работать и с 5-вольтовой логикой, что позволяет подключать его UART к Arduino.
Bluetooth-модуль HC-05
Подключение Bluetooth-модуля к Arduino
Так теперь нам нужно подключить нашу Arduino с Bluetooth. Если на Arduino нет вывода с 3.3В, а только 5В то нужен будет поставить стабилизатор чтобы снизить питание. Назначение выводов HC-05 легко найти в интернете. Для использования рекомендуем вам сделать плату с выведенными линиями питания, Rx и Tx. Подключение к Arduino необходимо производить в следующем порядке:
- вывод Arduino 3.3В или (5В через стабилизатор!) — к 12 пину модуля Bluetooth
- вывод Arduino GND — к 13 пину модуля Bluetooth
- вывод Arduino TX — к 2 пину модуля RX Bluetooth
- вывод Arduino RX — к 1 пину модуля TX Bluetooth
После подключения необходимо проверить работоспособность Bluetooth модуля. Подключим Светодиод к 12 выводу Arduino и загрузим на плату следующий скетч:
Char incomingByte; // входящие данные int LED = 12; // LED подключен к 12 пину void setup() { Serial.begin(9600); // инициализация порта pinMode(LED, OUTPUT); //Устанавливаем 12 вывод как выход Serial.println("Press 1 to LED ON or 0 to LED OFF..."); } void loop() { if (Serial.available() > 0) { //если пришли данные incomingByte = Serial.read(); // считываем байт if(incomingByte == "0") { digitalWrite(LED, LOW); // если 1, то выключаем LED Serial.println("LED OFF. Press 1 to LED ON!"); // и выводим обратно сообщение } if(incomingByte == "1") { digitalWrite(LED, HIGH); // если 0, то включаем LED Serial.println("LED ON. Press 0 to LED OFF!"); } } }
В данной статье будет подробно расписано создание небольшого приложения для мобильной операционной системы Android и скетча для Arduino. На Arduino Uno будет стоять Wireless Shield с Bluetooth-модулем. Приложение будет подключаться к Bluetooth-модулю и посылать некую команду. В свою очередь скетч по этой команде будет зажигать или гасить один из подключенных к Arduino светодиодов.
Нам понадобится
Создание приложения для Android
Заготовка
Разработка для ОС Android ведется в среде разработки ADT, Android Development Tools. Которую можно скачать с портала Google для разработчиков. После скачивания и установке ADT, смело его запускаем. Однако, еще рано приступать к разработке приложения. Надо еще скачать Android SDK нужной версии. Для этого необходимо открыть Android SDK Manager «Window → Android SDK Manager». В списке необходимо выбрать нужный нам SDK, в нашем случае Android 2.3.3 (API 10). Если телефона нет, то выбирайте 2.3.3 или выше; а если есть - версию, совпадающую с версией ОС телефона. Затем нажимаем на кнопку «Install Packages», чтобы запустить процесс установки.
После завершения скачивания и установки мы начинаем создавать приложение. Выбираем «File → New → Android Application Project». Заполним содержимое окна так, как показано на рисунке.
Application Name - то имя приложения, которое будет показываться в Google Play Store. Но выкладывать приложение мы не собираемся, поэтому имя нам не особо важно.
Project Name - имя проекта в ADT.
Package Name - идентификатор приложения. Он должен быть составлен следующим образом: название Вашего сайта задом наперед, плюс какое-либо название приложения.
В выпадающих списках «Minimum Required SDK», «Target SDK», «Compile With» выбираем ту версию, которую мы скачали ранее. Более новые версии SDK поддерживают графические темы для приложений, а старые нет. Поэтому в поле «Theme» выбираем «None». Нажимаем «Next».
Снимаем галочку с «Create custom launcher icon»: в рамках данной статьи не будем заострять внимание на создании иконки приложения. Нажимаем «Next».
В появившемся окне можно выбрать вид «Activity»: вид того, что будет на экране, когда будет запущено приложение. Выбираем «Blank activity», что означает, что мы хотим начать всё с чистого листа. Нажимаем «Next».
В нашем приложении будет всего одно Activity, поэтому в появившемся окне можно ничего не менять. Поэтому просто жмем на «Finish».
Все, наше приложение создано.
Настройка эмулятора
Отладка приложений для Android производится на реальном устройстве или, если такового нет, то на эмуляторе. Сконфигурируем свой.
Для этого запустим «Window → Android Virtual Device Manager». В появившемся окне нажмем «New». Заполняем поля появившейся формы. От них зависит сколько и каких ресурсов будет предоставлять эмулятор «телефону». Выберите разумные значения и нажимайте «ОК».
В окне Android Virtual Device Manager нажимаем кнопку «Start». Это запустит эмулятор. Запуск занимает несколько минут. Так что наберитесь терпения.
В результате вы увидите окно эмулятора подобное этому:
Заполнение Activity
Activity - это то, что отображается на экране телефона после запуска приложения. На нем у нас будет две кнопки «Зажечь красный светодиод» и «Зажечь синий светодиод». Добавим их. В панели «Package Explorer» открываем res/layout/activity_main.xml . Его вид будет примерно таким же, как на скриншоте.
Перетаскиваем 2 кнопки «ToggleButton» на экранную форму. Переключаемся во вкладку «activity_main.xml» и видим следующий код:
activity_main_aiutogen.xmlЭто ни что иное, как наша Activity, которая отображается не в виде графики, а описанная в формате XML.
Сделаем имена компонентов более понятными. Изменим поля android:id следующим образом.
А еще добавим им подписи, изменим их цвет и размер текста. Результирующий код разметки будет выглядеть следующим образом.
activity_main.xmlЭти же изменения можно сделать и в графическом режиме, воспользовавшись вкладкой «Outline/Properties».
Пробный запуск
Мы можем запустить только что созданное приложение на эмуляторе. Идем в настройки запуска «Run» → Run Configurations», в левой части нажимаем на «Android Application». Появляется новая конфигурация «New_configuration». В правой части окна выбираем вкладку «Target» и выбираем опцию «Launch on all compatible devices/AVD».
Нажимаем «Apply», а затем «Run». Приложение запустится в эмуляторе.
Можно понажимать кнопки. Но ничего происходить не будет, поскольку обработчики нажатий еще нами не написаны.
Чтобы запустить приложение на реальном устройстве, необходимо включить в его настройках опцию «Отладка USB» и подключить его к компьютеру.
На реальном устройстве приложение выглядит абсолютно аналогично.
Написание кода для Android
Правка манифеста
Каждое Android-приложение должно сообщить системе о том, какие права необходимо ему предоставить. Перечисление прав идет в так называемом файле манифеста AndroidManifest.xml . В нем мы должны указать тот факт, что хотим использовать Bluetooth в своем приложении. Для этого достаточно добавить буквально пару строк:
AndroidManifest.xmlДобавляем основной код
Пришла пора вдохнуть жизнь в наше приложение. Открываем файл MainActivity.java (src → ru.amperka.arduinobtled). Изначально он содержит следующий код:
MainActivityAutogen.java package ru.amperka.arduinobtled ; import android.os.Bundle ; import android.app.Activity ; import android.view.Menu ; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate (savedInstanceState) ; setContentView(R.layout .activity_main ) ; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater() .inflate (R.menu .main , menu) ; return true ; } }Дополним код в соответствии с тем, что нам нужно:
Будем включать Bluetooth, если он выключен.
Будем обрабатывать нажатия на кнопки
Будем посылать информацию о том, какая кнопка была нажата.
Передавать на Arduino мы будем один байт с двузначным числом. Первая цифра числа - номер пина, к которому подключен тот или иной светодиод, вторая - состояние светодиода: 1 - включен, 0 - выключен.
Число-команда, рассчитывается очень просто: Если нажата красная кнопка, то берется число 60 (для красного светодиода мы выбрали 6-й пин Arduino) и к нему прибавляется 1 или 0 в зависимости от того, должен ли сейчас гореть светодиод или нет. Для зеленой кнопки всё аналогично, только вместо 60 берется 70 (поскольку зеленый светодиод подключен к 7 пину). В итоге, в нашем случае, возможны 4 команды: 60, 61, 70, 71.
Напишем код, который реализует всё сказанное.
MainActivity.java package ru.amperka.arduinobtled ; import java.io.IOException ; import java.io.OutputStream ; import java.lang.reflect.InvocationTargetException ; import java.lang.reflect.Method ; import android.app.Activity ; import android.bluetooth.BluetoothAdapter ; import android.bluetooth.BluetoothDevice ; import android.bluetooth.BluetoothSocket ; import android.content.Intent ; import android.os.Bundle ; import android.util.Log ; import android.view.Menu ; import android.view.View ; import android.view.View.OnClickListener ; import android.widget.Toast ; import android.widget.ToggleButton ; public class MainActivity extends Activity implements View .OnClickListener { //Экземпляры классов наших кнопок ToggleButton redButton; ToggleButton greenButton; //Сокет, с помощью которого мы будем отправлять данные на Arduino BluetoothSocket clientSocket; //Эта функция запускается автоматически при запуске приложения @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate (savedInstanceState) ; setContentView(R.layout .activity_main ) ; //"Соединям" вид кнопки в окне приложения с реализацией redButton = (ToggleButton) findViewById(R.id .toggleRedLed ) ; greenButton = (ToggleButton) findViewById(R.id .toggleGreenLed ) ; //Добавлем "слушатель нажатий" к кнопке redButton.setOnClickListener (this ) ; greenButton.setOnClickListener (this ) ; //Включаем bluetooth. Если он уже включен, то ничего не произойдет String enableBT = BluetoothAdapter.ACTION_REQUEST_ENABLE ; startActivityForResult(new Intent(enableBT) , 0 ) ; //Мы хотим использовать тот bluetooth-адаптер, который задается по умолчанию BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter () ; //Пытаемся проделать эти действия try { //Устройство с данным адресом - наш Bluetooth Bee //Адрес опредеяется следующим образом: установите соединение //между ПК и модулем (пин: 1234), а затем посмотрите в настройках //соединения адрес модуля. Скорее всего он будет аналогичным. BluetoothDevice device = bluetooth.getRemoteDevice ("00:13:02:01:00:09" ) ; //Инициируем соединение с устройством Method m = device.getClass () .getMethod ( "createRfcommSocket" , new Class { int .class } ) ; clientSocket = (BluetoothSocket) m.invoke (device, 1 ) ; clientSocket.connect () ; //В случае появления любых ошибок, выводим в лог сообщение } catch (IOException SecurityException e) { Log.d ("BLUETOOTH" , e.getMessage () ) ; } catch (NoSuchMethodException e) { Log.d ("BLUETOOTH" , e.getMessage () ) ; } catch (IllegalArgumentException e) { Log.d ("BLUETOOTH" , e.getMessage () ) ; } catch (IllegalAccessException e) { Log.d ("BLUETOOTH" , e.getMessage () ) ; } catch (InvocationTargetException e) { Log.d ("BLUETOOTH" , e.getMessage () ) ; } //Выводим сообщение об успешном подключении Toast.makeText (getApplicationContext() , "CONNECTED" , Toast.LENGTH_LONG ) .show () ; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater() .inflate (R.menu .main , menu) ; return true ; } //Как раз эта функция и будет вызываться @Override public void onClick(View v) { //Пытаемся послать данные try { //Получаем выходной поток для передачи данных OutputStream outStream = clientSocket.getOutputStream () ; int value = 0 ; //В зависимости от того, какая кнопка была нажата, //изменяем данные для посылки if (v == redButton) { value = (redButton.isChecked () ? 1 : 0 ) + 60 ; } else if (v == greenButton) { value = (greenButton.isChecked () ? 1 : 0 ) + 70 ; } //Пишем данные в выходной поток outStream.write (value) ; } catch (IOException e) { //Если есть ошибки, выводим их в лог Log.d ("BLUETOOTH" , e.getMessage (, OUTPUT) ; pinMode(7 , OUTPUT) ; } void loop() { //Если данные пришли if (Serial.available () > 0 ) { //Считываем пришедший байт byte incomingByte = Serial.read () ; //Получаем номер пина путем целочисленного деления значения принятого байта на 10 //и нужное нам действие за счет получения остатка от деления на 2: //(1 - зажечь, 0 - погасить) digitalWrite(incomingByte / 10 , incomingByte % 2 ) ; } }Особенности заливки скетча
Для связи Bluetooth-Bee с контроллером используются те же пины (0 и 1), что и для прошивки. Поэтому при программировании контроллера переключатель «SERIAL SELECT» на «Wireless Shield» должен быть установлен в положение «USB», а после прошивки его надо вернуть в положение «MICRO».
Результат
Заключение
В данной статье мы научились создавать приложения для операционной системы Android и передавать данные по Bluetooth. Теперь при нажатии на кнопку на экране телефона на базе операционной системы Android, произойдет изменение состояния светодиода на плате.
Вы можете развить мысль и сделать более дружественный интерфейс на Android, управлять с его помощью гораздо более сложными устройствами, публиковать классные приложения в Android Market и ещё много-много всего интересного!
Машинка на Arduino, управляемая Android-устройством по Bluetooth, - код приложения и мк (часть 2)
О первый части
В первой части я описал физическую часть конструкции и лишь небольшой кусок кода. Теперь рассмотрим программную составляющую - приложение для Android и скетч Arduino.Вначале приведу подробное описание каждого момента, а в конце оставлю ссылки на проекты целиком + видео результата, которое должно вас разочаровать ободрить.
Android-приложение
Программа для андроида разбита на две части: первая - подключение устройства по Bluetooth, вторая - джойстик управления.Предупреждаю - дизайн приложения совсем не прорабатывался и делался на тяп-ляп, лишь бы работало. Адаптивности и UX не ждите, но вылезать за пределы экрана не должно.
Верстка
Стартовая активность держится на верстке, элементы: кнопки и layout для списка устройств. Кнопка запускает процесс нахождения устройств с активным Bluetooth. В ListView отображаются найденные устройства.
Экран управления опирается на верстку, в которой есть только кнопка, которая в будущем станет джойстиком. К кнопки, через атрибут background, прикреплен стиль, делающий ее круглой.
TextView в финальной версии не используется, но изначально он был добавлен для отладки: выводились цифры, отправляемые по блютузу. На начальном этапе советую использовать. Но потом цифры начнут высчитываться в отдельном потоке, из которого сложно получить доступ к TextView.
Файл button_control_circle.xml (стиль), его нужно поместить в папку drawable:
Также нужно создать файл item_device.xml, он нужен для каждого элемента списка:
Манифест
На всякий случай приведу полный код манифеста. Нужно получить полный доступ к блютузу через uses-permission и не забыть обозначить вторую активность через тег activity.
Основная активность, сопряжение Arduino и Android
Наследуем класс от AppCompatActivity и объявляем переменные:Public class MainActivity extends AppCompatActivity {
private BluetoothAdapter bluetoothAdapter;
private ListView listView;
private ArrayList
Метод onCreate() опишу построчно:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //обязательная строчка
//прикрепляем ранее созданную разметку
setContentView(R.layout.activity_main);
//цепляем кнопку из разметки
Button buttonStartFind = (Button) findViewById(R.id.button_start_find);
//цепляем layout, в котором будут отображаться найденные устройства
listView = (ListView) findViewById(R.id.list_device);
//устанавливаем действие на клик
buttonStartFind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//если разрешения получены (функция ниже)
if(permissionGranted()) {
//адаптер для управления блютузом
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothEnabled()) { //если блютуз включен (функция ниже)
findArduino(); //начать поиск устройства (функция ниже)
}
}
}
});
//цепляем кнопку для перехода к управлению
buttonStartControl = (Button) findViewById(R.id.button_start_control);
buttonStartControl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//объект для запуска новых активностей
Intent intent = new Intent();
//связываем с активностью управления
intent.setClass(getApplicationContext(), ActivityControl.class);
//закрыть эту активность, открыть экран управления
startActivity(intent);
}
});
}
Нижеприведенные функции проверяют, получено ли разрешение на использование блютуза (без разрешение пользователя мы не сможем передавать данные) и включен ли блютуз:
Private boolean permissionGranted() {
//если оба разрешения получены, вернуть true
if (ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.BLUETOOTH) == PermissionChecker.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.BLUETOOTH_ADMIN) == PermissionChecker.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this, new String {Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN}, 0);
return false;
}
}
private boolean bluetoothEnabled() {
//если блютуз включен, вернуть true, если нет, вежливо попросить пользователя его включить
if(bluetoothAdapter.isEnabled()) {
return true;
} else {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 0);
return false;
}
}
Если все проверки пройдены, начинается поиск устройства. Если одно из условий не выполнено, то высветится уведомление, мол, «разрешите\включите?», и это будет повторяться, пока проверка не будет пройдена.
Поиск устройства делится на три части: подготовка списка, добавление в список найденных устройств, установка соединения с выбранным устройством.
Private void findArduino() {
//получить список доступных устройств
Set
Когда Bluetooth-модуль, повешенный на Arduino (подробнее об этом далее), будет найден, он появится в списке. Нажав на него, вы начнете создание socket (возможно, после клика придется подождать 3-5 секунд или нажать еще раз). Вы поймете, что соединение установлено, по светодиодам на Bluetooth-модуле: без соединения они мигают быстро, при наличии соединения заметно частота уменьшается.
Управление и отправка команд
После того как соединение установлено, можно переходить ко второй активности - ActivityControl. На экране будет только синий кружок - джойстик. Сделан он из обычной Button, разметка приведена выше.Public class ActivityControl extends AppCompatActivity {
//переменные, которые понадобятся
private Button buttonDriveControl;
private float BDCheight, BDCwidth;
private float centerBDCheight, centerBDCwidth;
private String angle = "90"; //0, 30, 60, 90, 120, 150, 180
private ConnectedThread threadCommand;
private long lastTimeSendCommand = System.currentTimeMillis();
}
В методе onCreate() происходит все основное действо:
//без этой строки студия потребует вручную переопределить метод performClick()
//нам оно не недо
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
//обязательная строка
super.onCreate(savedInstanceState);
//устанавливаем разметку, ее код выше
setContentView(R.layout.activity_control);
//привязываем кнопку
buttonDriveControl = (Button) findViewById(R.id.button_drive_control);
//получаем информацию о кнопке
final ViewTreeObserver vto = buttonDriveControl.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//получаем высоту и ширину кнопки в пикселях(!)
BDCheight = buttonDriveControl.getHeight();
BDCwidth = buttonDriveControl.getWidth();
//находим центр кнопки в пикселях(!)
centerBDCheight = BDCheight/2;
centerBDCwidth = BDCwidth/2;
//отключаем GlobalListener, он больше не понадобится
buttonDriveControl.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
//устанавливаем листенер, который будет отлавливать прикосновения
//его код представлен ниже
buttonDriveControl.setOnTouchListener(new ControlDriveInputListener());
//создаем новый поток, он будет занят отправкой данных
//в качестве параметра передаем сокет, созданный в первой активности
//код потока представлен ниже
threadCommand = new ConnectedThread(MainActivity.clientSocket);
threadCommand.run();
}
Обратите внимание (!) - мы узнаем, сколько пикселей занимает кнопка. Благодаря этому получаем адаптивность: размер кнопки будет зависеть от разрешения экрана, но весь остальной код легко под это подстроится, потому что мы не фиксируем размеры заранее. Позже научим приложение узнавать, в каком месте было касание, а после переводить это в понятные для ардуинки значения от 0 до 255 (ведь касание может быть в 456 пикселях от центра, а МК с таким числом работать не будет).
Далее приведен код ControlDriveInputListener(), данный класс располагается в классе самой активности, после метода onCreate(). Находясь в файле ActivityControl, класс ControlDriveInputListener становится дочерним, а значит имеет доступ ко всем переменным основного класса.
Не обращайте пока что внимание на функции, вызываемые при нажатии. Сейчас нас интересует сам процесс отлавливания касаний: в какую точку человек поставил палец и какие данные мы об этом получим.
Обратите внимание, использую класс java.util.Timer: он позволяет создать новый поток, который может иметь задержку и повторятся бесконечное число раз через каждое энное число секунд. Его нужно использовать для следующей ситуации: человек поставил палец, сработал метод ACTION_DOWN, информация пошла на ардуинку, а после этого человек решил не сдвигать палец, потому что скорость его устраивает. Второй раз метод ACTION_DOWN не сработает, так как сначала нужно вызвать ACTION_UP (отодрать палец от экрана).
Чтож, мы запускаем цикл класса Timer() и начинаем каждые 10 миллисекунд отправлять те же самые данные. Когда же палец будет сдвинут (сработает ACTION_MOVE) или поднят (ACTION_UP), цикл Timer надо убить, чтобы данные от старого нажатия не начали отправляться снова.
Public class ControlDriveInputListener implements View.OnTouchListener {
private Timer timer;
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
//получаем точки касания в пикселях
//отсчет ведется от верхнего левого угла (!)
final float x = motionEvent.getX();
final float y = motionEvent.getY();
//узнаем, какое действие было сделано
switch(motionEvent.getAction()) {
//если нажатие
//оно сработает всегда, когда вы дотронетесь до кнопки
case MotionEvent.ACTION_DOWN:
//создаем таймер
timer = new Timer();
//запускаем цикл
//аргументы указывают: задержка между повторами 0,
//повторять каждые 10 миллисекунд
timer.schedule(new TimerTask() {
@Override
public void run() {
//функцию рассмотрим ниже
calculateAndSendCommand(x, y);
}
}, 0, 10);
break;
//если палец был сдвинут (сработает после ACTION_DOWN)
case MotionEvent.ACTION_MOVE:
//обязательно (!)
//если ранее был запущен цикл Timer(), завершаем его
if(timer != null) {
timer.cancel();
timer = null;
}
//создаем новый цикл
timer = new Timer();
//отправляем данные с той же частотой, пока не сработает ACTION_UP
timer.schedule(new TimerTask() {
@Override
public void run() {
calculateAndSendCommand(x, y);
}
}, 0, 10);
break;
//если палец убрали с экрана
case MotionEvent.ACTION_UP:
//убиваем цикл
if(timer != null) {
timer.cancel();
timer = null;
}
break;
}
return false;
}
}
Обратите еще раз внимание: отсчет x и y метод onTouch() ведет от верхнего левого угла View. В нашем случае точка (0; 0) находится у Button тут:
Теперь, когда мы узнали, как получить актуальное расположение пальца на кнопки, разберемся, как преобразовать пиксели (ведь x и y - именно расстояние в пикселях) в рабочие значения. Для этого использую метод calculateAndSendCommand(x, y), который нужно разместить в классе ControlDriveInputListener. Также понадобятся некоторые вспомогательные методы, их пишем в этот же класс после calculateAndSendCommand(x, y).
Private void calculateAndSendCommand(float x, float y) {
//все методы описаны ниже
//получаем нужные значения
//четверть - 1, 2, 3, 4
//чтобы понять, о чем я, проведите через середину кнопки координаты
//и да, дальше оно использоваться не будет, но для отладки пригождалось
int quarter = identifyQuarter(x, y);
//функция переводит отклонение от центра в скорость
//вычитаем y, чтобы получить количество пикселей от центра кнопки
int speed = speedCalculation(centerBDCheight - y);
//определяет угол поворота
//вспомните первую часть статьи, у нас есть 7 вариантов угла
String angle = angleCalculation(x);
//если хотите вывести информацию на экран, то используйте этот способ
//но в финальной версии он не сработает, так как затрагивает отдельный поток
/*String resultDown = "x: "+ Float.toString(x) + " y: " + Float.toString(y)
+ " qr: " + Integer.toString(quarter) + "\n"
+ "height: " + centerBDCheight + " width: " + centerBDCwidth + "\n"
+ "speed: " + Integer.toString(speed) + " angle: " + angle; */
//viewResultTouch.setText(resultDown);
//все данные полученные, можно их отправлять
//но делать это стоить не чаще (и не реже), чем в 100 миллисекунд
if((System.currentTimeMillis() - lastTimeSendCommand) > 100) {
//функцию рассмотрим дальше
threadCommand.sendCommand(Integer.toString(speed), angle);
//перезаписываем время последней отправки данных
lastTimeSendCommand = System.currentTimeMillis();
}
}
private int identifyQuarter(float x, float y) {
//смотрим, как расположена точка относительно центра
//возвращаем угол
if(x > centerBDCwidth && y > centerBDCheight) {
return 4;
} else if (x < centerBDCwidth && y >centerBDCheight) {
return 3;
} else if (x < centerBDCwidth && y < centerBDCheight) {
return 2;
} else if (x > centerBDCwidth && y < centerBDCheight) {
return 1;
}
return 0;
}
private int speedCalculation(float deviation) {
//получаем коэффициент
//он позволит превратить пиксели в скорость
float coefficient = 255/(BDCheight/2);
//высчитываем скорость по коэффициенту
//округляем в целое
int speed = Math.round(deviation * coefficient);
//если скорость отклонение меньше 70, ставим скорость ноль
//это понадобится, когда вы захотите повернуть, но не ехать
if(speed > 0 && speed < 70) speed = 0;
if(speed < 0 && speed > - 70) speed = 0;
//нет смысла отсылать скорость ниже 120
//слишком мало, колеса не начнут крутиться
if(speed < 120 && speed > 70) speed = 120;
if(speed > -120 && speed < -70) speed = -120;
//если вы унесете палец за кнопку, ACTION_MOVE продолжит считывание
//вы сможете получить отклонение больше, чем пикселей в кнопке
//на этот случай нужно ограничить скорость
if(speed > 255) speed = 255;
if(speed < - 255) speed = -255;
//пометка: скорость > 0 - движемся вперед, < 0 - назад
return speed;
}
private String angleCalculation(float x) {
//разделяем ширину кнопки на 7 частей
//0 - максимально влево, 180 - вправо
//90 - это когда прямо
if(x < BDCwidth/6) {
angle = "0";
} else if (x > BDCwidth/6 && x < BDCwidth/3) {
angle = "30";
} else if (x > BDCwidth/3 && x < BDCwidth/2) {
angle = "60";
} else if (x > BDCwidth/2 && x < BDCwidth/3*2) {
angle = "120";
} else if (x > BDCwidth/3*2 && x < BDCwidth/6*5) {
angle = "150";
} else if (x > BDCwidth/6*5 && x < BDCwidth) {
angle = "180";
} else {
angle = "90";
}
return angle;
}
Когда данные посчитаны и переведены, в игру вступает второй поток. Он отвечает именно за отправку информации. Нельзя обойтись без него, иначе сокет, передающий данные, будет тормозить отлавливание касаний, создастся очередь и все конец всему короче.
Класс ConnectedThread также располагаем в классе ActivityControl.
Private class ConnectedThread extends Thread { private final BluetoothSocket socket; private final OutputStream outputStream; public ConnectedThread(BluetoothSocket btSocket) { //получаем сокет this.socket = btSocket; //создаем стрим - нить для отправки данных на ардуино OutputStream os = null; try { os = socket.getOutputStream(); } catch(Exception e) {} outputStream = os; } public void run() { } public void sendCommand(String speed, String angle) { //блютуз умеет отправлять только байты, поэтому переводим byte speedArray = speed.getBytes(); byte angleArray = angle.getBytes(); //символы используются для разделения //как это работает, вы поймете, когда посмотрите принимающий код скетча ардуино String a = "#"; String b = "@"; String c = "*"; try { outputStream.write(b.getBytes()); outputStream.write(speedArray); outputStream.write(a.getBytes()); outputStream.write(c.getBytes()); outputStream.write(angleArray); outputStream.write(a.getBytes()); } catch(Exception e) {} } }
Подводим итоги Андроид-приложения
Коротко обобщу все громоздкое вышеописанное.- В ActivityMain настраиваем блютуз, устанавливаем соединение.
- В ActivityControl привязываем кнопку и получаем данные о ней.
- Вешаем на кнопку OnTouchListener, он отлавливает касание, передвижение и подъем пальца.
- Полученные данные (точку с координатами x и y) преобразуем в угол поворота и скорость
- Отправляем данные, разделяя их специальными знаками
Скетч Arduino
Андроид-приложение разобрано, написано, понято… а тут уже и попроще будет. Постараюсь поэтапно все рассмотреть, а потом дам ссылку на полный файл.Переменные
Для начала рассмотрим константы и переменные, которые понадобятся. #include
Метод setup()
В методе setup() мы устанавливаем параметры пинов: будут работать они на вход или выход. Также установим скорость общения компьютера с ардуинкой, блютуза с ардуинкой.Void setup() { pinMode(dirLeft, OUTPUT); pinMode(speedLeft, OUTPUT); pinMode(dirRight, OUTPUT); pinMode(speedRight, OUTPUT); pinMode(pinRed, INPUT); pinMode(pinBlack, INPUT); pinMode(pinWhite, INPUT); pinMode(pinAngleStop, OUTPUT); pinMode(angleDirection, OUTPUT); pinMode(angleSpeed, OUTPUT); //данная скорость актуальна только для модели HC-05 //если у вас модуль другой версии, смотрите документацию BTSerial.begin(38400); //эта скорость постоянна Serial.begin(9600); }
Метод loop() и дополнительные функции
В постоянно повторяющемся методе loop() происходит считывание данных. Сначала рассмотрим основной алгоритм, а потом функции, задействованные в нем.Void loop() {
//если хоть несчитанные байты
if(BTSerial.available() > 0) {
//считываем последний несчитанный байт
char a = BTSerial.read();
if (a == "@") {
//если он равен @ (случайно выбранный мною символ)
//обнуляем переменную val
val = "";
//указываем, что сейчас считаем скорость
readSpeed = true;
} else if (readSpeed) {
//если пора считывать скорость и байт не равен решетке
//добавляем байт к val
if(a == "#") {
//если байт равен решетке, данные о скорости кончились
//выводим в монитор порта для отладки
Serial.println(val);
//указываем, что скорость больше не считываем
readSpeed = false;
//передаем полученную скорость в функцию езды
go(val.toInt());
//обнуляем val
val = "";
//выходим из цикла, чтобы считать следующий байт
return;
}
val+=a;
} else if (a == "*") {
//начинаем считывать угол поворота
readAngle = true;
} else if (readAngle) {
//если решетка, то заканчиваем считывать угол
//пока не решетка, добавляем значение к val
if(a == "#") {
Serial.println(val);
Serial.println("-----");
readAngle = false;
//передаем значение в функцию поворота
turn(val.toInt());
val= "";
return;
}
val+=a;
}
//получаем время последнего приема данных
lastTakeInformation = millis();
} else {
//если несчитанных байтов нет, и их не было больше 150 миллисекунд
//глушим двигатели
if(millis() - lastTakeInformation > 150) {
lastTakeInformation = 0;
analogWrite(angleSpeed, 0);
analogWrite(speedRight, 0);
analogWrite(speedLeft, 0);
}
}
}
Получаем результат: с телефона отправляем байты в стиле "@скорость#угол#" (например, типичная команда "@200#60#". Данный цикл повторяется каждый 100 миллисекунд, так как на андроиде мы установили именно этот промежуток отправки команд. Короче делать нет смысла, так как они начнут становится в очередь, а если сделать длиннее, то колеса начнут двигаться рывками.
Все задержки через команду delay(), которые вы увидите далее, подобраны не через физико-математические вычисления, а опытным путем. Благодаря всем выставленным задрежам, машинка едет плавно, и у всех команд есть время на отработку (токи успевают пробежаться).
В цикле используются две побочные функции, они принимают полученные данные и заставляют машинку ехать и крутится.
Void go(int mySpeed) {
//если скорость больше 0
if(mySpeed > 0) {
//едем вперед
digitalWrite(dirRight, HIGH);
analogWrite(speedRight, mySpeed);
digitalWrite(dirLeft, HIGH);
analogWrite(speedLeft, mySpeed);
} else {
//а если меньше 0, то назад
digitalWrite(dirRight, LOW);
analogWrite(speedRight, abs(mySpeed) + 30);
digitalWrite(dirLeft, LOW);
analogWrite(speedLeft, abs(mySpeed) + 30);
}
delay(10);
}
void turn(int angle) {
//подаем ток на плюс определителя угла
digitalWrite(pinAngleStop, HIGH);
//даем задержку, чтобы ток успел установиться
delay(5);
//если угол 150 и больше, поворачиваем вправо
//если 30 и меньше, то влево
//промежуток от 31 до 149 оставляем для движения прямо
if(angle > 149) {
//если замкнут белый, но разомкнуты черный и красный
//значит достигнуто крайнее положение, дальше крутить нельзя
//выходим из функции через return
if(digitalRead(pinWhite) == HIGH && digitalRead(pinBlack) == LOW && digitalRead(pinRed) == 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);
}
Поворачивать, когда андроид отправляет данные о том, что пользователь зажал угол 60, 90, 120, не стоит, иначе не сможете ехать прямо. Да, возможно сразу не стоило отправлять с андроида команду на поворот, если угол слишком мал, но это как-то коряво на мой взгляд.