Создание робота, даже простенького с ограниченным функционалом, довольно интересная и увлекательная задача. В последнее время любительская робототехника переживает настоящий бум, ей начали увлекаться даже те люди, которые от электроники очень далеки (к ним отношусь и я). Прошло то время, когда нужно было сидеть ночами с паяльником, или травить платы. Все стало гораздо проще, нужно лишь купить Arduino, комплект проводков, сенсоры, датчики, моторчики и вперед к сборке своего первого робота. Таким образом, фокус разработчиков — любителей сместился с электроники и механической части к программированию.
Данная серия статей будет содержать информацию для быстрого старта по созданию своего первого робота, от покупки необходимых деталей до его запуска в наш мир.
Отмечу, что статьи я пишу по ходу самостоятельного изучения материала и создания своего первого робота.
Предполагается, что человек читающий статью уже имеет опыт в:
БТ – Bluetooth;
Андроид – устройство с ОС android, к примеру, смартфон;
Скетч – приложение для Arduino;
Ардуино – Arduino Nano.
Пусть робот имеет следующую общую схему (рис.1). Андроид, является «головным мозгом», в котором обрабатываются все данные и проводятся вычисления. На основании результатов вычислении по БТ каналу передаются команды, принимаемые БТ модулем и в дальнейшем поступающие по последовательному порту в Arduino. Arduino обрабатывает поступившие команды и выполняет их при помощи «мышц» (Моторы светодиоды и др.). Кроме того в Arduino поступают данные от «органов чувств» (сенсоры, датчики и др.), которые он приводит в удобный вид и отправляет при помощи БТ модуля в «головной мозг». И так все повторяет до бесконечности.
Рисунок 1
Обоснование именно такой схемы робота следующее:
-БТ модуль и Arduino Nano имеют низкую стоимость, а также маленький объем и вес.
-Андроид, не дорогое и доступное устройство, уже имеется у огромного количества людей, в том числе и у меня.
-Андроид имеет свои сенсоры, датчики и экран для вывода информации
-Простота написания кода для приложений как для андроида так и для ардуино.
Для упрощения задачи построения робота, предлагаю воспользоваться методом «от простого к сложному», т.е. как и при изучении языка программирования создадим первую программу «Hello word». Конечно, это будет не одна программа, а как минимум две (для ардуино и андроида). В каждой последующей статье функционал робота будет увеличиваться.
Собрать простенькое устройство, у которого имеются:
-2 кнопки (b1, b2).
-1 светодиод (Led).
И выполняется функционал:
-главное activity содержит 2 кнопки «Отпарвить 0» и «Отправить 1», при нажатии на которые от андроида к ардуино по БТ каналу передаются данные, соответствнно «0» и «1», ардуино их обработвает и зажигает либо тушит светодиод.
-данные о нажатии или отпускании кнопок от ардуино передаются в андроид, на экране главного activity выводится информация об этом.
1) Android устройство – смартфон LG P500 (версия Android 2.3.3), либо любое другое устройство на базе Android версией 2.3.3 и выше. Цену не указываю, так как отдельно не покупал, а использую свой смартфон.
2) Arduino NanoV3.0 ( dx.com/ru/p/nano-v3-0-avr-atmega328-p-20au-module-board-usb-cable-for-arduino-118037 ) — 11,32$
3) Модуль Bluetooth ( dx.com/ru/p/bluetooth-board-module-4-pin-121326 ) — 10,57$
4) Макетные провода ( dx.com/ru/p/30cm-breadboard-wires-for-electronic-diy-40-cable-pack-80207 ) — 2,51$
5) Кнопки — 2шт., светодиод – 1 шт. – 50 рублей
Итого: 855,20 рублей.
Соберем из имеющихся деталей схему (рис. 2)
Рисунок 2
Питание на arduino подается по USB кабелю, который подключается к компьютеру, по нему же загружается и программное обеспечение (скетч) выполняемое на нем. Необходимо отметить, что загружать скетч в arduino можно только тогда, когда отключено питание от Bluetooth модуля (Вывод (17) 3V3), в противном случае возникает ошибка.
Подробности установки среды разработки Arduino и драйверов можно найти на официальном сайте: arduino.ru/Guide/Windows
Ниже приведен скетч который необходимо загрузить в ардуино:
Объявляем переменные, напротив каждой стоит комментарий.
Инициируем последовательное соединение и задаем скорость передачи данных в бит/c (бод). Мой БТ модуль работает на скорости 38400, но может быть скорость 9600 (скорость БТ модуля можно задавать при помощи AT команд). Устанавливаем режим работы заданного входа/выхода(pin) как входа или как выхода. Подаем HIGH значение на входы и выходы.
Функция void contact_bounce(int buttton) обрабатывает нажатие кнопок и позволяет устранить дребезг контактов, который возникает при соприкосновении или расхождении контактов в механических переключающих устройствах, таких, как кнопка, происходит многократное замыкание и размыкание.
В основном цикле Loop слушаем последовательный порт, и если на него пришли данные мы их обрабатываем. Кроме того вызываем функцию обработки нажатия кнопок и устранения дребезга контактов.
Загрузив скетч в ардуино, можем проверить его работоспособность – запустив монитор порта. При нажатии кнопок в окне монитора будет выводиться надпись «Press button b». Проверить работу светодиода удастся только после написания приложения для андроида.
В данной статье для ардуино будет использоваться только этот скетч.Приступим к разработки приложения для андроида.
Для удобства отладки приложения андроида, рекомендую использовать не «Android virtual device», а реальный смартфон с ОС Android версии от 2.3.3 подключенный через USB кабель к компьютеру в режиме «Отладки». Существует огромное количество статей как это сделать.
Создаем новый проект «Android application project»
Для работы с БТ необходимо выставить права на использование его нашим приложением. Для этого заходим в манифест, выбираем закладку Permissions, нажимаем add, далее Uses permission, и устанавливаем следующие права: android.permission.BLUETOOTH, android.permission.BLUETOOTH_ADMIN
Теперь оформим основное activity, в res/layout/activity_main.xml поместим код:
Таким образом, основное activity примет вид:
Рисунок 3
Текстовое поле «txtrobot», будет отображать всю необходимую нам информацию. Кнопки b1 и b2, будут отправлять команды в arduino.
Теперь переходим в src/../MainActivity.java здесь и будет располагаться наш основной код. Подключим пакет Api для Bluetooth:
Перед тем как использовать БТ необходимо убедится, что в нашем андроиде он присутствует. Создадим экземпляр класса BluetoothAdapter (отвечающий за работу с установленным в андроиде БТ модулем):
Если андроид не имеет БТ то будет возвращено null:
В данном виде программу уже можно запустить. На экране андроида вы должны увидеть надпись: «Bluetooth присутствует».
Теперь необходимо убедиться, что БТ включен, либо предложить его включить. Добавим константу:
и код:
Запустив измененное приложение на андроиде, Вам будет выдан «Запрос разрешения на включения Bluetooth», подтвердив его, тем самым Вы активируете БТ.
Полный код приложения:
Для дальнейших экспериментов, необходимо «спарить» наш андроид и БТ модуль, для этого на андроиде в настройках БТ, выполним поиск и подключимся к БТ модулю, пароль «1234».
Если вдруг удаленный БТ модуль не будет нормально работать, просто отключите от него все провода (VCC, GND,RX,TX), тем самым сделав жесткую перезагрузку, и снова подключите их — это должно помочь.
Теперь попробуем программно подключится к удаленному БТ модулю: основной код подключения разместим в onResume. onResume – это одно из состояний нашего Activity, а именно, Activity видно на экране, оно находится в фокусе, пользователь может с ним взаимодействовать. Ниже приведен основной код Activity:
Доступ к удаленному БТ модулю получаем по его МАС адресу btAdapter.getRemoteDevice(MacAdress). Узнать MAC-адрес БТ модуля можно, при помощи программы для андроида: Bluetooth Terminal.
Что бы убедится, что доступ к БТ модулю получен, используем метод getName(), который позволяет получить имя удаленного БТ модуля, и выводим результат на экран андроида.
Также в этом примере, была добавлена возможность ведения лога, который по ходу выполнения программы можно просматривать, и обнаруживать существующие ошибки. Кроме того создана функция MyError, которая вызывается в том случае если нужно аварийно завершить приложение.
Запустив приложение, на экране андроида в текстовом поле «txtrobot» отобразится имя удаленного БТ модуля.
Доступ к удаленному БТ модулю получен, следующий наш шаг передать данные от андроида к нему. Для этого в onResume(), создадим сокет:
где UUID (Universally Unique Identifier) — это стандарт идентификации, используемый в создании программного обеспечения. Добавим в определение константу UUID:
Чтобы не тормозить соединение отменим поиск других БТ устройств:
Попытаемся подсоединиться:
При не удаче закроем сокет:
Так как методы отправки и получения данных являются блокирующими, их следует выполнять в отдельном потоке, что бы предотвратить зависание основного приложения. Для этого создадим класс:
В конструкторе public ConnectedThred(BluetoothSocket socket) создается объект управляющий передачей данных через сокет:
Для отправки данных из главного activity вызывается метод sendData(String message) с параметром текстового сообщения, которое преобразуется к типу byte. Метод cancel() позволяет закрыть сокет.
Напишем, обработчики нажатия кнопок b1 и b2, содержащие вызов функции sendData(String message) и сделаем запись об этом в логе. Полный код приложения приведен ниже:
Написанное нами приложение, позволяет передавать по БТ от андроида к ардуино данные – «0» и «1», которые в свою очередь для ардуино являются командами «1» — зажечь светодиод, а «0» — потушить. Таким образом, с учетом общей схемы робота, мы научились отдавать команды от «головного мозга» к «мышцам». Осталось дело за малым, научить андроид принимать данные от «органов чувств».
Получение данных также как и отправку, необходимо выполнять в отдельном потоке, чтобы избежать зависание главного activity. Принятые данные от БТ модуля мы будем выводить на экран главного activity в текстовом поле – MyText. Но возникает трудность — работа с view-компонентами доступна только из основного потока. А новые потоки, которые мы создаем, не имеют доступа к элементам экрана. Для решения данной проблемы воспользуемся механизмом Handler.
Handler — это механизм, который позволяет работать с очередью сообщений. Он привязан к конкретному потоку (thread) и работает с его очередью. Handler умеет помещать сообщения в очередь. При этом он ставит самого себя в качестве получателя этого сообщения. И когда приходит время, система достает сообщение из очереди и отправляет его адресату (т.е. в Handler) на обработку.
Объявим Handler:
Создадим свой Handler:
в нем реализуем метод обработки сообщений handleMessage. Мы извлекаем из сообщения атрибут what, obj и аргументы типа int. Преобразуем полученное сообщение в строку и выводим его в текстовое поле главного activity: mytext.setText(«Данные от Arduino: » + strIncom);
В потоке для передачи данных добавим функцию запуска этого потока, и разместим там цикл с функцией чтения данных:
Полный код приложения приведен ниже:
В шаге 4, представлено приложение, которое позволяет, как передавать по БТ от андроида к ардуино команды, так и получать от ардуино данные. Таким образом, мы научились отдавать команды и принимать данные, а следовательно выполнили поставленную задачу.
Кроме того данное приложение является шаблоном, на его базе можно создавать более сложные приложения, которые в свою очередь смогут обрабатывать данные например от ультразвукового сенсора, а также отдавать команды моторчикам, для передвижения робота.
К следующей статье я сделал заказ новых деталей и модулей. Ниже приведен список и цены:
ИТОГ: 1933,8
Данная серия статей будет содержать информацию для быстрого старта по созданию своего первого робота, от покупки необходимых деталей до его запуска в наш мир.
Отмечу, что статьи я пишу по ходу самостоятельного изучения материала и создания своего первого робота.
Предполагается, что человек читающий статью уже имеет опыт в:
- Установке и настройке среды разработки для android устройств.
- Написании первой программы Hello World для android устройства.
- Установке драйверов и среды разработки для Arduino.
- Написании первой программы (включать, выключать) светодиоды на Arduino.
Сокращения
БТ – Bluetooth;
Андроид – устройство с ОС android, к примеру, смартфон;
Скетч – приложение для Arduino;
Ардуино – Arduino Nano.
Общая схема робота
Пусть робот имеет следующую общую схему (рис.1). Андроид, является «головным мозгом», в котором обрабатываются все данные и проводятся вычисления. На основании результатов вычислении по БТ каналу передаются команды, принимаемые БТ модулем и в дальнейшем поступающие по последовательному порту в Arduino. Arduino обрабатывает поступившие команды и выполняет их при помощи «мышц» (Моторы светодиоды и др.). Кроме того в Arduino поступают данные от «органов чувств» (сенсоры, датчики и др.), которые он приводит в удобный вид и отправляет при помощи БТ модуля в «головной мозг». И так все повторяет до бесконечности.
Рисунок 1
Обоснование именно такой схемы робота следующее:
-БТ модуль и Arduino Nano имеют низкую стоимость, а также маленький объем и вес.
-Андроид, не дорогое и доступное устройство, уже имеется у огромного количества людей, в том числе и у меня.
-Андроид имеет свои сенсоры, датчики и экран для вывода информации
-Простота написания кода для приложений как для андроида так и для ардуино.
Для упрощения задачи построения робота, предлагаю воспользоваться методом «от простого к сложному», т.е. как и при изучении языка программирования создадим первую программу «Hello word». Конечно, это будет не одна программа, а как минимум две (для ардуино и андроида). В каждой последующей статье функционал робота будет увеличиваться.
Постановка задачи
Собрать простенькое устройство, у которого имеются:
-2 кнопки (b1, b2).
-1 светодиод (Led).
И выполняется функционал:
-главное activity содержит 2 кнопки «Отпарвить 0» и «Отправить 1», при нажатии на которые от андроида к ардуино по БТ каналу передаются данные, соответствнно «0» и «1», ардуино их обработвает и зажигает либо тушит светодиод.
-данные о нажатии или отпускании кнопок от ардуино передаются в андроид, на экране главного activity выводится информация об этом.
Закуп необходимых деталей и узлов.
1) Android устройство – смартфон LG P500 (версия Android 2.3.3), либо любое другое устройство на базе Android версией 2.3.3 и выше. Цену не указываю, так как отдельно не покупал, а использую свой смартфон.
2) Arduino NanoV3.0 ( dx.com/ru/p/nano-v3-0-avr-atmega328-p-20au-module-board-usb-cable-for-arduino-118037 ) — 11,32$
3) Модуль Bluetooth ( dx.com/ru/p/bluetooth-board-module-4-pin-121326 ) — 10,57$
4) Макетные провода ( dx.com/ru/p/30cm-breadboard-wires-for-electronic-diy-40-cable-pack-80207 ) — 2,51$
5) Кнопки — 2шт., светодиод – 1 шт. – 50 рублей
Итого: 855,20 рублей.
Приступим к работе
Arduino
Соберем из имеющихся деталей схему (рис. 2)
Рисунок 2
Питание на arduino подается по USB кабелю, который подключается к компьютеру, по нему же загружается и программное обеспечение (скетч) выполняемое на нем. Необходимо отметить, что загружать скетч в arduino можно только тогда, когда отключено питание от Bluetooth модуля (Вывод (17) 3V3), в противном случае возникает ошибка.
Подробности установки среды разработки Arduino и драйверов можно найти на официальном сайте: arduino.ru/Guide/Windows
Ниже приведен скетч который необходимо загрузить в ардуино:
//Объявляем переменные
int led = 12; // Светодиод
int b1 = 11; // Кнопка
int b2 = 10; // Кнопка
int value_1,value_2 = 0; // Переменные необходимые для устранения дребезга контак
char incomingbyte; // переменная для приема данных
//Инициализация переменных
void setup() {
Serial.begin(38400);
pinMode(led,OUTPUT);
digitalWrite(led, HIGH);
pinMode(b1,INPUT);
digitalWrite(b1, HIGH);
pinMode(b2,INPUT);
digitalWrite(b2, HIGH);
}
//Обрабатываем нажатие кнопок и устраняем дребезг контактов
void contact_bounce(int buttton){
value_1 = digitalRead(buttton);
if (!value_1){
delay(80);
value_2 = digitalRead(buttton);
if (!value_2){
Serial.print("Press button b");
Serial.println(buttton);
}
}
}
//Основной цикл программы
void loop() {
if (Serial.available() > 0){
incomingbyte = Serial.read();
if (incomingbyte == '1'){
digitalWrite(led,HIGH);
Serial.println("LED ON");
}
if (incomingbyte=='0'){
digitalWrite(led,LOW);
Serial.println("LED OFF");
}
}
contact_bounce(b1);
contact_bounce(b2);
}
Объявляем переменные, напротив каждой стоит комментарий.
Инициируем последовательное соединение и задаем скорость передачи данных в бит/c (бод). Мой БТ модуль работает на скорости 38400, но может быть скорость 9600 (скорость БТ модуля можно задавать при помощи AT команд). Устанавливаем режим работы заданного входа/выхода(pin) как входа или как выхода. Подаем HIGH значение на входы и выходы.
Функция void contact_bounce(int buttton) обрабатывает нажатие кнопок и позволяет устранить дребезг контактов, который возникает при соприкосновении или расхождении контактов в механических переключающих устройствах, таких, как кнопка, происходит многократное замыкание и размыкание.
В основном цикле Loop слушаем последовательный порт, и если на него пришли данные мы их обрабатываем. Кроме того вызываем функцию обработки нажатия кнопок и устранения дребезга контактов.
Загрузив скетч в ардуино, можем проверить его работоспособность – запустив монитор порта. При нажатии кнопок в окне монитора будет выводиться надпись «Press button b». Проверить работу светодиода удастся только после написания приложения для андроида.
В данной статье для ардуино будет использоваться только этот скетч.Приступим к разработки приложения для андроида.
Android
Для удобства отладки приложения андроида, рекомендую использовать не «Android virtual device», а реальный смартфон с ОС Android версии от 2.3.3 подключенный через USB кабель к компьютеру в режиме «Отладки». Существует огромное количество статей как это сделать.
Android ШАГ 1
Создаем новый проект «Android application project»
Для работы с БТ необходимо выставить права на использование его нашим приложением. Для этого заходим в манифест, выбираем закладку Permissions, нажимаем add, далее Uses permission, и устанавливаем следующие права: android.permission.BLUETOOTH, android.permission.BLUETOOTH_ADMIN
Теперь оформим основное activity, в res/layout/activity_main.xml поместим код:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Отправить "0"" />
<Button
android:id="@+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Отправить "1"" />
</LinearLayout>
<TextView
android:id="@+id/txtrobot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Поле текстовых сообщений" />
</LinearLayout>
Таким образом, основное activity примет вид:
Рисунок 3
Текстовое поле «txtrobot», будет отображать всю необходимую нам информацию. Кнопки b1 и b2, будут отправлять команды в arduino.
Теперь переходим в src/../MainActivity.java здесь и будет располагаться наш основной код. Подключим пакет Api для Bluetooth:
import android.bluetooth.*;
Перед тем как использовать БТ необходимо убедится, что в нашем андроиде он присутствует. Создадим экземпляр класса BluetoothAdapter (отвечающий за работу с установленным в андроиде БТ модулем):
btAdapter = BluetoothAdapter.getDefaultAdapter();
Если андроид не имеет БТ то будет возвращено null:
TextView mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
mytext.setText("Bluetooth присутствует");
}else
{
mytext.setText("Bluetooth отсутствует");
}
В данном виде программу уже можно запустить. На экране андроида вы должны увидеть надпись: «Bluetooth присутствует».
Теперь необходимо убедиться, что БТ включен, либо предложить его включить. Добавим константу:
private static final int REQUEST_ENABLE_BT = 1;
и код:
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
Запустив измененное приложение на андроиде, Вам будет выдан «Запрос разрешения на включения Bluetooth», подтвердив его, тем самым Вы активируете БТ.
Полный код приложения:
package com.robot.bluetest;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;
import android.bluetooth.*;
import android.content.Intent;
public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 0;
public BluetoothAdapter btAdapter;
public TextView mytext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}else
{
mytext.setText("Bluetooth отсутствует");
}
}
}
Android ШАГ 2
Для дальнейших экспериментов, необходимо «спарить» наш андроид и БТ модуль, для этого на андроиде в настройках БТ, выполним поиск и подключимся к БТ модулю, пароль «1234».
Если вдруг удаленный БТ модуль не будет нормально работать, просто отключите от него все провода (VCC, GND,RX,TX), тем самым сделав жесткую перезагрузку, и снова подключите их — это должно помочь.
Теперь попробуем программно подключится к удаленному БТ модулю: основной код подключения разместим в onResume. onResume – это одно из состояний нашего Activity, а именно, Activity видно на экране, оно находится в фокусе, пользователь может с ним взаимодействовать. Ниже приведен основной код Activity:
package com.robot.bluetoothrob2;
import java.io.IOException;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;
public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 0;
final String LOG_TAG = "myLogs";
public BluetoothAdapter btAdapter;
private BluetoothSocket btSocket = null;
// MAC-адрес Bluetooth модуля
private static String MacAdress = "20:11:02:47:01:60";
public TextView mytext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}else
{
MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
}
}
@Override
public void onResume() {
super.onResume();
Log.d(LOG_TAG, "***Пытаемся соединиться***");
// Получаем удаленное устройство по его MAC адресу
BluetoothDevice device = btAdapter.getRemoteDevice(MacAdress);
mytext.setText("***Получили device = " + device.getName() + "***");
}
private void MyError(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
}
Доступ к удаленному БТ модулю получаем по его МАС адресу btAdapter.getRemoteDevice(MacAdress). Узнать MAC-адрес БТ модуля можно, при помощи программы для андроида: Bluetooth Terminal.
Что бы убедится, что доступ к БТ модулю получен, используем метод getName(), который позволяет получить имя удаленного БТ модуля, и выводим результат на экран андроида.
Также в этом примере, была добавлена возможность ведения лога, который по ходу выполнения программы можно просматривать, и обнаруживать существующие ошибки. Кроме того создана функция MyError, которая вызывается в том случае если нужно аварийно завершить приложение.
Запустив приложение, на экране андроида в текстовом поле «txtrobot» отобразится имя удаленного БТ модуля.
Android ШАГ 3
Доступ к удаленному БТ модулю получен, следующий наш шаг передать данные от андроида к нему. Для этого в onResume(), создадим сокет:
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
где UUID (Universally Unique Identifier) — это стандарт идентификации, используемый в создании программного обеспечения. Добавим в определение константу UUID:
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Чтобы не тормозить соединение отменим поиск других БТ устройств:
btAdapter.cancelDiscovery();
Попытаемся подсоединиться:
btSocket.connect();
При не удаче закроем сокет:
btSocket.close();
Так как методы отправки и получения данных являются блокирующими, их следует выполнять в отдельном потоке, что бы предотвратить зависание основного приложения. Для этого создадим класс:
//Класс отдельного потока для передачи данных
private class ConnectedThred extends Thread{
private final BluetoothSocket copyBtSocket;
private final OutputStream OutStrem;
public ConnectedThred(BluetoothSocket socket){
copyBtSocket = socket;
OutputStream tmpOut = null;
try{
tmpOut = socket.getOutputStream();
} catch (IOException e){}
OutStrem = tmpOut;
}
public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(LOG_TAG, "***Отправляем данные: " + message + "***" );
try {
OutStrem.write(msgBuffer);
} catch (IOException e) {}
}
public void cancel(){
try {
copyBtSocket.close();
}catch(IOException e){}
}
public Object status_OutStrem(){
if (OutStrem == null){return null;
}else{return OutStrem;}
}
}
В конструкторе public ConnectedThred(BluetoothSocket socket) создается объект управляющий передачей данных через сокет:
tmpOut = socket.getOutputStream();
Для отправки данных из главного activity вызывается метод sendData(String message) с параметром текстового сообщения, которое преобразуется к типу byte. Метод cancel() позволяет закрыть сокет.
Напишем, обработчики нажатия кнопок b1 и b2, содержащие вызов функции sendData(String message) и сделаем запись об этом в логе. Полный код приложения приведен ниже:
package com.robot.bluetoothrob2;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;
import com.robot.bluetoothrob2.R;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;
public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 1;
final String LOG_TAG = "myLogs";
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private ConnectedThred MyThred = null;
public TextView mytext;
Button b1, b2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}else
{
MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
}
b1 = (Button) findViewById(R.id.b1);
b2 = (Button) findViewById(R.id.b2);
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
mytext.setText("Отправлены данные: 0");
}
});
b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("1");
mytext.setText("Отправлены данные: 1");
}
});
}
@Override
public void onResume() {
super.onResume();
BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.d(LOG_TAG, "...Создали сокет...");
} catch (IOException e) {
MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
}
btAdapter.cancelDiscovery();
Log.d(LOG_TAG, "***Отменили поиск других устройств***");
Log.d(LOG_TAG, "***Соединяемся...***");
try {
btSocket.connect();
Log.d(LOG_TAG, "***Соединение успешно установлено***");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
}
}
MyThred = new ConnectedThred(btSocket);
}
@Override
public void onPause() {
super.onPause();
Log.d(LOG_TAG, "...In onPause()...");
if (MyThred.status_OutStrem() != null) {
MyThred.cancel();
}
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
}
}
private void MyError(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
//Отдельный поток для передачи данных
private class ConnectedThred extends Thread{
private final BluetoothSocket copyBtSocket;
private final OutputStream OutStrem;
public ConnectedThred(BluetoothSocket socket){
copyBtSocket = socket;
OutputStream tmpOut = null;
try{
tmpOut = socket.getOutputStream();
} catch (IOException e){}
OutStrem = tmpOut;
}
public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(LOG_TAG, "***Отправляем данные: " + message + "***" );
try {
OutStrem.write(msgBuffer);
} catch (IOException e) {}
}
public void cancel(){
try {
copyBtSocket.close();
}catch(IOException e){}
}
public Object status_OutStrem(){
if (OutStrem == null){return null;
}else{return OutStrem;}
}
}
}
Написанное нами приложение, позволяет передавать по БТ от андроида к ардуино данные – «0» и «1», которые в свою очередь для ардуино являются командами «1» — зажечь светодиод, а «0» — потушить. Таким образом, с учетом общей схемы робота, мы научились отдавать команды от «головного мозга» к «мышцам». Осталось дело за малым, научить андроид принимать данные от «органов чувств».
Android ШАГ 4
Получение данных также как и отправку, необходимо выполнять в отдельном потоке, чтобы избежать зависание главного activity. Принятые данные от БТ модуля мы будем выводить на экран главного activity в текстовом поле – MyText. Но возникает трудность — работа с view-компонентами доступна только из основного потока. А новые потоки, которые мы создаем, не имеют доступа к элементам экрана. Для решения данной проблемы воспользуемся механизмом Handler.
Handler — это механизм, который позволяет работать с очередью сообщений. Он привязан к конкретному потоку (thread) и работает с его очередью. Handler умеет помещать сообщения в очередь. При этом он ставит самого себя в качестве получателя этого сообщения. И когда приходит время, система достает сообщение из очереди и отправляет его адресату (т.е. в Handler) на обработку.
Объявим Handler:
Handler h;
Создадим свой Handler:
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ArduinoData:
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
mytext.setText("Данные от Arduino: " + strIncom);
break;
}
};
};
в нем реализуем метод обработки сообщений handleMessage. Мы извлекаем из сообщения атрибут what, obj и аргументы типа int. Преобразуем полученное сообщение в строку и выводим его в текстовое поле главного activity: mytext.setText(«Данные от Arduino: » + strIncom);
В потоке для передачи данных добавим функцию запуска этого потока, и разместим там цикл с функцией чтения данных:
public void run()
{
byte[] buffer = new byte[1024];
int bytes;
while(true){
try{
bytes = InStrem.read(buffer);
h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
}catch(IOException e){break;}
}
}
Полный код приложения приведен ниже:
package com.robot.bluetoothrob2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;
import com.robot.bluetoothrob2.R;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;
public class MainActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 1;
final int ArduinoData = 1;
final String LOG_TAG = "myLogs";
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private ConnectedThred MyThred = null;
public TextView mytext;
Button b1, b2;
Handler h;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btAdapter = BluetoothAdapter.getDefaultAdapter();
mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
if (btAdapter.isEnabled()){
mytext.setText("Bluetooth включен. Все отлично.");
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}else
{
MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
}
b1 = (Button) findViewById(R.id.b1);
b2 = (Button) findViewById(R.id.b2);
b1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("0");
//mytext.setText("Отправлены данные: 0");
}
});
b2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyThred.sendData("1");
// mytext.setText("Отправлены данные: 1");
}
});
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ArduinoData:
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
mytext.setText("Данные от Arduino: " + strIncom);
break;
}
};
};
}
@Override
public void onResume() {
super.onResume();
BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.d(LOG_TAG, "...Создали сокет...");
} catch (IOException e) {
MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
}
btAdapter.cancelDiscovery();
Log.d(LOG_TAG, "***Отменили поиск других устройств***");
Log.d(LOG_TAG, "***Соединяемся...***");
try {
btSocket.connect();
Log.d(LOG_TAG, "***Соединение успешно установлено***");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
}
}
MyThred = new ConnectedThred(btSocket);
MyThred.start();
}
@Override
public void onPause() {
super.onPause();
Log.d(LOG_TAG, "...In onPause()...");
if (MyThred.status_OutStrem() != null) {
MyThred.cancel();
}
try {
btSocket.close();
} catch (IOException e2) {
MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
}
}
private void MyError(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
//Отдельный поток для передачи данных
private class ConnectedThred extends Thread{
private final BluetoothSocket copyBtSocket;
private final OutputStream OutStrem;
private final InputStream InStrem;
public ConnectedThred(BluetoothSocket socket){
copyBtSocket = socket;
OutputStream tmpOut = null;
InputStream tmpIn = null;
try{
tmpOut = socket.getOutputStream();
tmpIn = socket.getInputStream();
} catch (IOException e){}
OutStrem = tmpOut;
InStrem = tmpIn;
}
public void run()
{
byte[] buffer = new byte[1024];
int bytes;
while(true){
try{
bytes = InStrem.read(buffer);
h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
}catch(IOException e){break;}
}
}
public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(LOG_TAG, "***Отправляем данные: " + message + "***" );
try {
OutStrem.write(msgBuffer);
} catch (IOException e) {}
}
public void cancel(){
try {
copyBtSocket.close();
}catch(IOException e){}
}
public Object status_OutStrem(){
if (OutStrem == null){return null;
}else{return OutStrem;}
}
}
}
В шаге 4, представлено приложение, которое позволяет, как передавать по БТ от андроида к ардуино команды, так и получать от ардуино данные. Таким образом, мы научились отдавать команды и принимать данные, а следовательно выполнили поставленную задачу.
Кроме того данное приложение является шаблоном, на его базе можно создавать более сложные приложения, которые в свою очередь смогут обрабатывать данные например от ультразвукового сенсора, а также отдавать команды моторчикам, для передвижения робота.
К следующей статье я сделал заказ новых деталей и модулей. Ниже приведен список и цены:
Наименовние | Ссылка | Цена y.e | Цена руб | Кол-во | Сумма |
Макет | dx.com/p/solderless-breadboard-with-400-tie-point-white-121534 | 3,1 | 102,3 | 2 | 204,6 |
Ультразвуковой сенсор | dx.com/p/ultrasonic-sensor-distance-measuring-module-138563 | 3,9 | 128,7 | 3 | 386,1 |
Драйвер для мотора 2 шт. | dx.com/ru/p/hg7881-two-channel-motor-driver-board-blue-green-black-2-5-12v-215795 | 2,8 | 92,4 | 2 | 184,8 |
Платформа | dx.com/ru/p/zl-4-smart-car-chassis-kit-for-arduino-black-yellow-152992 | 28,2 | 930,6 | 1 | 930,6 |
Проводки соединения | dx.com/p/breadboard-jumper-wire-cord-kit-for-arduino-diy-140-piece-pack-138220 | 6,9 | 227,7 | 1 | 227,7 |
Питание | dx.com/ru/p/dc-power-converter-module-for-electronic-diy-219232 | 2,3 | 75,9 | 1 | 75,9 |
ИТОГ: 1933,8