РОБОТ на базе: 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 (протокол для обмена данными между ардуино и сервером). К сожалению работоспособной библиотеки, для андроида в связке с БТ, я не нашел. Написать свою библиотеку у меня не хватает времени и сил, поэтому в данной статье я продолжаю изобретать велосипед. Если кто из Хабра пользователей обладает информацией о такой библиотеке просьба поделиться.
    Поделиться публикацией
    Никаких подозрительных скриптов, только релевантные баннеры. Не релевантные? Пиши на: adv@tmtm.ru с темой «Полундра»

    Зачем оно вам?
    Реклама
    Комментарии 12
    • +1
      Для информации: именно этот драйвер двигателя (точнее их там два) умеет работать с ШИМ.
      Думаю регулировка скорости — пригодится )
      • 0
        Спасибо. Отличная новость я и не знал. Обязательно прочту и учту.
      • 0
        И еще: модуль Hi-Link хорош, но есть нюанс:

        полноценно работать с этим модулем можно при наличии платы расширения dx.com/ru/p/vrm04-uart-serial-port-to-ethernet-wi-fi-test-board-module-blue-black-silver-252730#.UviOCbRq2ME, что позволит влить прошивку openwrt

        • 0
          Эмм… По поводу вёрстки Activity: зачем вам LinearLayout, внутри которого только один элемент — кнопка? Он лишний. И ещё, тот LinearLayout, в котором 3 кнопки, должен иметь атрибут android:orientation=horizontal. По крайней мере рекомендуют всегда указывать ориентацию LinearLayout
        • 0
          Спасибо! С каждой такой статьей хочется отложить комп.игры/просмотр аниме и взяться за сборку робота =)
          Не подскажите, какой лучше набор взять для совсем новичка: такой или такой? И можно ли будет потом добавить Wi-Fi модуль и камеру?
          • 0
            Скучно же! Я новичёк, но вот сижу и заказываю по частям…
            • +1
              Поддерживаю совет пользователя M03G. Мне тоже кажется, что лучше заказать отдельно по частям. Купив такой набор, ты к нему и будешь привязан, будет примерная сборка по схеме. Лучше попробуй подумать, что ты хочешь увидеть в своём роботе. По читать статьи, по выбирать разнообразные детали. И заказывай по отдельности. Так у тебя получится уникальный робот, не похожий на другие.
              А по поводу Wi-Fi модуля и камеры — к arduino сможешь подключить. Вот например — Arduino WiFi Shield, как раз для Arduino UNO, как в наборах, из которых ты выбираешь. Так же и для камеры есть модули.
            • 0
              Спасибо за статьи! Я как раз получил все необходимые компоненты и думаю начать собирать своего робота. Искал описание драйвера HG7881 и набрел на эту статью, как нельзя кстати. Ввиду отсутствия у меня смартфона с Андроидом, буду пока делать управление с компа. Ну а дальше думаю прикрутить что-то на сами шасси, что бы мозг жил вместе со всем остальным. Смотрел в сторону Cubietruck в качестве мозга.
              • 0
                Почитал про Cubietruck — очень внушительно смотрится, мощности предостаточно будет для управления роботом. Также посмотрите в сторону ROS, может что интересное найдете.
              • 0
                Столкнулся с проблемой. Не получается что бы работали все четыре двигателя. Пока работают два или три — все нормально, но как только подключаю четвертый — все перестает работать. Доайвер такой же HG7881.
                В программе использую ШИМ ( analog_write(250) ) вместо HIGH для подачи сигнала на драйвер, может в этом проблема? Пробовал запитывать драйвер и от +5V ардуины и от блока с аккумами, не помогает. В чем может быть причина?

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.