
Рисунок 1
Оглавление
Статья 1. РОБОТ на базе: android, arduino, bluetooth. Начало
Статья 2. РОБОТ на базе: android, arduino, bluetooth. Рефлексный. Часть 2.
Предполагается, что человек читающий статью уже имеет представление о:
-Базовых понятиях электроники
-Предыдущей статье
Постановка задачи
Создать робота который выполняет следующий функционал:
-Имеет удаленное управление при помощи смартфона (передвижение вперед, назад, налево, направо)
-Передает на смартфон данные о расстоянии до объекта находящегося перед ним(на базе ультра звукового датчика)
-Имеет режим автономного управления: непрерывно перемещается по помещению, при встрече препятствий меняет направление своего движения, тем самым объезжая препятствие.
Немного теории
Наш мир является сложнейшей системой, в которой взаимодействуют между собой огромное количество объектов, подчиненных определенным законам физики, поэтому создание робота функционирующего в рамках этой системы, является очень трудоемкой задачей. Для упрощения процесса создания первого робота воспользуемся понятием абстрагирования среды(в которую помещен робот) и действий робота. В дальнейших статьях будем усложнять среду и соответственно действия робота.
Абстрагирование среды
Среда где будет обитать наш первый робот будет представлять собой двухмерный мир и обладать следующими характеристиками:
1) Полностью наблюдаемая, т.е. датчики робота предоставляют доступ к полной информации о состоянии среды в каждый момент времени. Полностью наблюдаемые варианты среды являются удобными, поскольку роботу не требуется поддерживать какое-либо внутреннее состояние для того, чтобы быть в курсе всего происходящего в этом мире.
2) Детерминированная. Если следующее состояние среды полностью определяется текущим состоянием и действием, выполненным роботом, то такая среда называется детерминированной; в противном случае она является стохастической.
3) Эпизодическая. В эпизодической проблемной среде опыт робота состоит из неразрывных эпизодов. Каждый эпизод включает в себя восприятие среды роботом, а затем выполнение одного действия. При этом крайне важно то, что следующий эпизод не зависит от действий, предпринятых в предыдущих эпизодах. В эпизодических вариантах среды выбор действия в каждом эпизоде зависит только от самого эпизода.
4) Статическая. Если среда может измениться в ходе того, как робот выбирает очередное действие, то такая среда называется динамической для данного робота; в противном случае она является статической.
5) Непрерывная — Различие между дискретными и непрерывными вариантами среды может относиться к состоянию среды, способу учета времени, а также восприятием и действиям агента. В нашем случае считается что состояние среды меняется непрерывно. К примеру игра в шахматы является дискретной, так как имеет конечное количество различных состояний.
6) Одноагентная это среда в которой находится один объект(робот), и другие объекты на него не влияют и не конкурируют с ним.
Абстрагирование действий робота
1) Движение – робот может передвигаться в двух направлениях(взад, вперед) и разворачиваться на месте(налево, направо)
2) Датчики робота (ультразвуковой сенсор), позволяет определить расстояние до объекта. Расстояние может быть определено от 0,02 метра до 4 метров.
Таким образом, определим, что создаваемый в этой статье робот является простым рефлексным роботом. Подобные роботы выбирают действия на основе текущего акта восприятия, игнорируя всю остальную историю актов восприятия.
Краткая информация по используемым деталям и модулям
Драйвер двигателей HG7881. Для управления двигателями робота необходимо устройство, которое бы преобразовывало управляющие сигналы малой мощности в токи, достаточные для управления моторами. Такое устройство называют драйвером двигателей.

HG7881 – это двухканальный драйвер двигателей, питание возможно от источника 2,5 – 12 В. Описание выходов драйвера:
Таблица 1
| Вывод | Описание |
| B-IA | Двигатель B Вход A (IA) |
| B-IB | Двигатель B Вход B (IB) |
| GND | Земля (Минус) |
| VCC | Рабочее напряжение 2.5-12V (Плюс) |
| A-IA | Двигатель A Вход A (IA) |
| A-IB | Двигатель A Вход B (IB) |
Для того чтобы заставить двигатели работать нужным нам образом на выводы (B-IA, B-IB, A-IA, A-IB) необходимо подавать логические сигналы (HIGH,LOW). Таблица истинности двигателей:
Таблица 2
| IA | IB | Состояние двигателя |
| L | L | Off |
| H | L | Forward |
| L | H | Reverse |
| H | H | Off |
Ультразвуковой сенсор измерения расстояния HC-SR04. Определяет расстояние до объекта, измеряя время отражения звуковой волны от объекта.

Сенсор излучает короткий ультразвуковой импульс (в момент времени 0), который отражается от объекта и принимается сенсором. Расстояние рассчитывается исходя из времени до получения эха и скорости звука в воздухе.
На вывод (Trig) подаётся импульс длительностью 10 мкс, ультразвуковой модуль излучает 8 пачек ультразвукового сигнала с частотой 40кГц и обнаруживает их эхо. Измеренное расстояние до объекта пропорционально ширине эха (Echo) и может быть рассчитано по формуле:
Ширина импульса/58 = расстояние в см.
Сборка робота и подключение всех модулей
Собираем платформу (рис.2).

Рисунок 2
Подключаем двигатели к драйверу (рис. 3). По два двигателя на один разъем драйвера, т.е. двигатели левой стороны платформы к разъему “Motor B”, двигатели правой стороны — “Motor A”. Управление платформой будет произведено аналогично гусеничной. При движении вперед и назад все двигатели работают синхронно в одном направлении, при повороте направо двигатели правой стороны платформы останавливаются, а левой двигаются синхронно, при повороте налево двигатели левой стороны останавливаются, а правой двигаются синхронно.

Рисунок 3
Прикручиваем верхнюю часть платформы. Соединяем драйвер двигателей, ардуино, аккумуляторы, БТ модуль и ультразвуковой сенсор к макетной плате (рис.4)

Рисунок 4
Схема подключения представлена на (рис.5). Питание ардуино, ультразвукового сенсора и драйвера двигателей (следовательно и самих двигателей) обеспечивают 4 подключенных последовательно аккумулятора (1,2 В., 2700 мА/ч), на БТ модуль питание подается от выхода ардуино 3,3 В.

Рисунок 5
Робот собран, необходимо его заставить выполнять команды, отправленные с андроида.
Скетч для Arduino ШАГ 1 – Удаленное управление роботом
Загрузим скетч в ардуино, не забыв перед этим отключить питания от БТ модуля (в противном случае загрузить его не удастся):
//Объявляем переменные char incomingbyte; // переменная для приема данных //motors A (RIGHT) int R_A_IA = 9; // A-IA int R_A_IB = 10; // A-IB //motors B (LEFT) int L_B_IA = 11; // B-IA int L_B_IB = 12; // B-IB //Инициализация переменных void setup() { Serial.begin(38400); //motors RIGHT pinMode(R_A_IA,OUTPUT); digitalWrite(R_A_IA, HIGH); pinMode(R_A_IB,OUTPUT); digitalWrite(R_A_IB, HIGH); //motors LEFT pinMode(L_B_IA,OUTPUT); digitalWrite(L_B_IA, HIGH); pinMode(L_B_IB,OUTPUT); digitalWrite(L_B_IB, HIGH); } void go_forward(){ //motors LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, HIGH); //motors RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, HIGH); } void go_back(){ //motors LEFT digitalWrite(L_B_IA, HIGH); digitalWrite(L_B_IB, LOW); //motors RIGHT digitalWrite(R_A_IA, HIGH); digitalWrite(R_A_IB, LOW); } void go_right(){ //motors LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, HIGH); //motors RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, LOW); } void go_left(){ //motors LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, LOW); //motors RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, HIGH); } void stop_robot(){ //motors LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, LOW); //motors RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, LOW); } //Основной цикл программы void loop() { if (Serial.available() > 0){ incomingbyte = Serial.read(); if (incomingbyte == '1'){ go_forward(); Serial.println("FORWARD"); } if (incomingbyte == '2'){ go_back(); Serial.println("BACK"); } if (incomingbyte == '3'){ go_right(); Serial.println("RIGHT"); } if (incomingbyte == '4'){ go_left(); Serial.println("LEFT"); } if (incomingbyte=='0'){ stop_robot(); Serial.println("STOP"); } } }
Объявляем переменные: R_A_IA, R_A_IB – определяют номера выводов управляющих двигателем А (двигатели правой стороны), L_B_IA, L_B_IB — выводы управляющие двигателем B(двигатели левой стороны. Инициируем последовательное соединение и задаем скорость передачи данных в бит/c (бод) – 38400. Устанавливаем режим работы выводов управляющих двигателями – OUTPUT (выходы). Подаем на все выходы значение HIGH, что означает — двигатели отключены(таблица 2).
Определяем функции: go_forward(), go_back(), go_right(), go_left(), stop_robot(), которые запускают двигатели в прямом или обратном направлении вращения, тем самым приводя робота в движение – вперед, назад, направо, налево, стоп, соответственно.
В основном цикле программы происходит считывание и обработка данных полученных в последовательный порт от БТ модуля. В зависимости от полученной команды выполняется та или иная функция и по последовательному порту передается текст об ее выполнении.
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" > <TextView android:id="@+id/txtrobot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Поле текстовых сообщений" /> <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:layout_weight="100" android:text="Вперед" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="100" > <Button android:id="@+id/b4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Налево" /> <Button android:id="@+id/b0" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Стоп" /> <Button android:id="@+id/b3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Направо" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/b2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="100" android:text="Назад" /> </LinearLayout> </LinearLayout>
Вот так будет выглядеть основное activity:

Текстовое поле «txtrobot», будет отображать всю необходимую нам информацию. Кнопки b0, b1, b2, b3, b4 будут отправлять команды в arduino (0, 1, 2, 3, 4)
Переходим в src/../MainActivity.java здесь и будет располагаться наш основной код.
В предыдущей статье на шаге 4, был представлен код позволяющий передавать и принимать данные по БТ. За основу возьмем этот код.
В состояния активити onPause() и onResume() добавим условие проверки существования БТ у андроида и определения включен ли он. В предыдущей статье это условие отсутствовало в связи, с чем при запуске приложения, если был отключен БТ, оно завершалось с ошибкой и только после этого предлагало включить БТ.
if (btAdapter != null){ if (btAdapter.isEnabled()){ //Код приложения } }
Объявим переменные для хранения кнопок:
Button b0, b1, b2, b3, b4;
Находим их по ID:
b0 = (Button) findViewById(R.id.b0);//Стоп b1 = (Button) findViewById(R.id.b1);//Вперед b2 = (Button) findViewById(R.id.b2);//Назад b3 = (Button) findViewById(R.id.b3);//Направо b4 = (Button) findViewById(R.id.b4);//Налево
Напишем обработчики нажатия этих кнопок, для отправки команд:
b0.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("0"); } }); b1.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("1"); } }); b2.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("2"); } }); b3.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("3"); } }); b4.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("4"); } });
Полный код приложения:
package com.example.rob_2_3_0; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; import com.example.rob_2_3_0.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 b0, b1, b2, b3, b4; boolean fl=false; 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 ОТСУТСТВУЕТ"); } b0 = (Button) findViewById(R.id.b0);//Стоп b1 = (Button) findViewById(R.id.b1);//Вперед b2 = (Button) findViewById(R.id.b2);//Назад b3 = (Button) findViewById(R.id.b3);//Направо b4 = (Button) findViewById(R.id.b4);//Налево b0.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("0"); } }); b1.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("1"); } }); b2.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("2"); } }); b3.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("3"); } }); b4.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("4"); } }); 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(); if (btAdapter != null){ if (btAdapter.isEnabled()){ 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 (btAdapter != null){ if (btAdapter.isEnabled()){ if (MyThred.status_OutStrem() != null) { MyThred.cancel(); } try { btSocket.close(); } catch (IOException e2) { MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + "."); } } } }//OnPause 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;} } } }
Данное приложение, позволяет управлять роботом при помощи андроида, отправляя команды по БТ на ардуино, и принимая текстовые ответы от него. Первая часть поставленной задачи выполнена.
Скетч для Arduino ШАГ 2 – Режим автономного управления роботом
Для работы с ультразвуковым сенсором, воспользуемся готовой библиотекой
ultrasonic-HC-SR04.zip
Распаковываем файлы и помещаем в каталог где расположен скетч
Подключаем библиотеку
#include "Ultrasonic.h"
Конструктор Ultrasonic принимает два параметра — номера выводов к которым подключены Trig и Echo, соответственно:
Ultrasonic ultrasonic(5, 6);
Получаем данные о расстоянии до объекта в сантиметрах:
float dist_cm = ultrasonic.Ranging(CM); //Получаем расстояние до объекта
Передаем данные на последовательный порт, для последующей передачи их через БТ модуль.
Serial.print("*"); Serial.print(dist_cm); Serial.print("#");
Символы «*» и «#» означают начало и конец передаваемого блока информации о расстоянии до объекта. Это необходимо для того чтобы четко отделять необходимые данные друг от друга, так как при их передачи часть теряется либо приходит с запозданием.
Полный скетч для загрузки в ардуино:
#include "Ultrasonic.h" //Объявляем переменные char incomingbyte; int i=0; //motors A (LEFT) int R_A_IA = 9; // A-IA int R_A_IB = 10; // A-IB //motors B (RIGHT) int L_B_IA = 11; // B-IA int L_B_IB = 12; // B-IB //Конструктор для работы с ультразвуковым сенсором Ultrasonic ultrasonic(5, 6); //Инициализация переменных void setup() { Serial.begin(38400); //RIGHT pinMode(R_A_IA,OUTPUT); digitalWrite(R_A_IA, HIGH); pinMode(R_A_IB,OUTPUT); digitalWrite(R_A_IB, HIGH); //LEFT pinMode(L_B_IA,OUTPUT); digitalWrite(L_B_IA, HIGH); pinMode(L_B_IB,OUTPUT); digitalWrite(L_B_IB, HIGH); } void go_forward(){ //LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, HIGH); //RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, HIGH); } void go_back(){ //LEFT digitalWrite(L_B_IA, HIGH); digitalWrite(L_B_IB, LOW); //RIGHT digitalWrite(R_A_IA, HIGH); digitalWrite(R_A_IB, LOW); } void go_right(){ //LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, HIGH); //RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, LOW); } void go_left(){ //LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, LOW); //RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, HIGH); } void stop_robot(){ //LEFT digitalWrite(L_B_IA, LOW); digitalWrite(L_B_IB, LOW); //RIGHT digitalWrite(R_A_IA, LOW); digitalWrite(R_A_IB, LOW); } //Основной цикл программы void loop() { if (Serial.available() > 0){ incomingbyte = Serial.read(); if (incomingbyte == '1'){ go_forward(); } if (incomingbyte == '2'){ go_back(); } if (incomingbyte == '3'){ go_right(); } if (incomingbyte == '4'){ go_left(); } if (incomingbyte=='0'){ stop_robot(); } } float dist_cm = ultrasonic.Ranging(CM); //Получаем расстояние до объекта Serial.print("*"); Serial.print(dist_cm); Serial.print("#"); }
Android приложение ШАГ 2 – Режим автономного управления роботом
Добавим в основное активити кнопку «b5» (автоуправление). Код приведен ниже:
<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/b5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Автоуправление" /> </LinearLayout> <TextView android:id="@+id/txtrobot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Поле текстовых сообщений" /> <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:layout_weight="100" android:text="Вперед" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="100" > <Button android:id="@+id/b4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Налево" /> <Button android:id="@+id/b0" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Стоп" /> <Button android:id="@+id/b3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="100" android:text="Направо" /> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/b2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="100" android:text="Назад" /> </LinearLayout> </LinearLayout>
Содержимое
Таким образом, основное activity примет вид:

Объявим переменную b5:
Button b0, b1, b2, b3, b4, b5;
И флаг позволяющий определить включен режим автоуправления или нет:
boolean fl=false;
Находим ее по ID:
b5 = (Button) findViewById(R.id.b5);//Автоуправление
Создадим обработчик ее нажатия:
b5.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.d(LOG_TAG, "НАЖАЛИ АВТОУПРАВЛЕНИЕ"); if (!fl){ Log.d(LOG_TAG, "Если флаг опущен"); fl=true; b1.setEnabled(false); b2.setEnabled(false); b3.setEnabled(false); b4.setEnabled(false); MyThred.sendData("1"); Log.d(LOG_TAG, "Отправили 1"); } } });
А также внесем изменения в обработчик кнопки «b0»(Стоп)
b0.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("0"); if (fl) { fl = false; b1.setEnabled(true); b2.setEnabled(true); b3.setEnabled(true); b4.setEnabled(true); } } });
Осталось создать алгоритм позволяющий роботу самостоятельно перемещаться по помещению и объезжать препятствия.
Обработаем полученные данные о расстоянии до объекта отправленные ардуино. Если расстояние до объекта менее 50 см. то поворачиваем направо в противном случае едим прямо:
byte[] readBuf = (byte[]) msg.obj; String strIncom = new String(readBuf, 0, msg.arg1); sb.append(strIncom);// формируем строку int beginOfLineIndex = sb.indexOf("*");//определяем символы начала строки int endOfLineIndex = sb.indexOf("#");//определяем символы конца строки //Если блок данных соответствует маске *данные# то выполняем код if ((endOfLineIndex > 0) && (beginOfLineIndex == 0)) { String sbprint = sb.substring(beginOfLineIndex+1, endOfLineIndex-3); mytext.setText("Данные от Arduino: " + sbprint); if (fl){ int dist = Integer.parseInt(sbprint); if (dist<50) { MyThred.sendData("3"); } else { MyThred.sendData("1"); } } } sb.delete(0, sb.length());
Ниже приведен полный код Activity:
package com.robot.rob_2_3; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.UUID; import com.robot.rob_2_3.R; import android.os.Bundle; import android.os.CountDownTimer; 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 static final long MILLIS_PER_SECOND = 0; private ConnectedThred MyThred = null; public TextView mytext; Button b0, b1, b2, b3, b4, b5; boolean fl=false; Handler h; private StringBuilder sb = new StringBuilder(); @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 ОТСУТСТВУЕТ"); } b0 = (Button) findViewById(R.id.b0);//Стоп b1 = (Button) findViewById(R.id.b1);//Вперед b2 = (Button) findViewById(R.id.b2);//Назад b3 = (Button) findViewById(R.id.b3);//Направо b4 = (Button) findViewById(R.id.b4);//Налево b5 = (Button) findViewById(R.id.b5);//Автоуправление b0.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("0"); if (fl) { fl = false; b1.setEnabled(true); b2.setEnabled(true); b3.setEnabled(true); b4.setEnabled(true); } } }); b1.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("1"); } }); b2.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("2"); } }); b3.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("3"); } }); b4.setOnClickListener(new OnClickListener() { public void onClick(View v) { MyThred.sendData("4"); } }); b5.setOnClickListener(new OnClickListener() { public void onClick(View v) { Log.d(LOG_TAG, "НАЖАЛИ АВТОУПРАВЛЕНИЕ"); if (!fl){ Log.d(LOG_TAG, "Если флаг опущен"); fl=true; b1.setEnabled(false); b2.setEnabled(false); b3.setEnabled(false); b4.setEnabled(false); MyThred.sendData("1"); Log.d(LOG_TAG, "Отправили 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); sb.append(strIncom);// формируем строку int beginOfLineIndex = sb.indexOf("*");//определяем символы начала строки int endOfLineIndex = sb.indexOf("#");//определяем символы конца строки //Если блок данных соотвествует маске *данные# то выполняем код if ((endOfLineIndex > 0) && (beginOfLineIndex == 0)) { // если встречаем конец строки, String sbprint = sb.substring(beginOfLineIndex+1, endOfLineIndex-3); // то извлекаем строку mytext.setText("Данные от Arduino: " + sbprint); if (fl){ int dist = Integer.parseInt(sbprint); if (dist<50) { MyThred.sendData("3"); } else { MyThred.sendData("1"); } } } sb.delete(0, sb.length()); break; } }; }; } @Override public void onResume() { super.onResume(); if (btAdapter != null){ if (btAdapter.isEnabled()){ 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 (btAdapter != null){ if (btAdapter.isEnabled()){ if (MyThred.status_OutStrem() != null) { MyThred.cancel(); } try { btSocket.close(); } catch (IOException e2) { MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + "."); } } } }//OnPause 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;} } } }
Созданное приложение для андроида в связке с представленным скетчем ардуино, позволяет, как удаленно самостоятельно управлять роботом, так и включать режим автономного управление, при котором робот передвигается в прямом направлении и если требуется, объезжает препятствия.
Результатом проделанной работы является простейший рефлексный робот. Дальнейшее применение более сложных алгоритмов на базе приведенных шаблонных приложений и скетчей позволит создавать роботов основанных на модели, на цели, на полезности, обучающихся роботов и др.
К следующей статье я сделал заказ всего одного модуля:
| Наименование | Ссылка | Цена y.e | Цена руб | Кол-во | Сумма |
| Wifi модуль | dx.com/p/hi-link-hlk-rm04-serial-port-ethernet-wi-fi-adapter-module-blue-black-214540#.UutHKD1_sd0 | 14,99 | 524,65 | 1 | 524,65 |
ИТОГ: 524,65
В комментариях к предыдущей статье, хабра пользователь commanderxo порекомендовал не изобретать велосипед, а воспользоваться стандартным протоколом Firmata (протокол для обмена данными между ардуино и сервером). К сожалению работоспособной библиотеки, для андроида в связке с БТ, я не нашел. Написать свою библиотеку у меня не хватает времени и сил, поэтому в данной статье я продолжаю изобретать велосипед. Если кто из Хабра пользователей обладает информацией о такой библиотеке просьба поделиться.
ссылка на оригинал статьи http://habrahabr.ru/post/211999/
Добавить комментарий