РОБОТ на базе: android, arduino, bluetooth. Рефлексный. Часть 2

от автора

В прошлой статье РОБОТ на базе: android, arduino, bluetooth. Начало была предложена общая схема робота и представлена технология передачи и приема данных между андроидом и ардуино. А в ее завершении приведен список заказанных деталей и модулей. Детали получены (рис.1), комментарии учтены, приступим к созданию первого робота – рефлексного робота.

Рисунок 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 – Удаленное управление роботом

Загрузим скетч в ардуино, не забыв перед этим отключить питания от БТ модуля (в противном случае загрузить его не удастся):

Скетч для 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 поместим код:

основное activity ШАГ 1

<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");         	}         }); 

Полный код приложения:

Код приложения Шаг 1

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("#"); 

Символы «*» и «#» означают начало и конец передаваемого блока информации о расстоянии до объекта. Это необходимо для того чтобы четко отделять необходимые данные друг от друга, так как при их передачи часть теряется либо приходит с запозданием.
Полный скетч для загрузки в ардуино:

Скетч для Arduino ШАГ 2

 #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» (автоуправление). Код приведен ниже:

основное activity ШАГ 2

 <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:

Код приложения Шаг 2

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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *