Пользователь
0,0
рейтинг
9 января 2014 в 08:52

РОБОТ на базе: android, arduino, bluetooth. Начало из песочницы

Создание робота, даже простенького с ограниченным функционалом, довольно интересная и увлекательная задача. В последнее время любительская робототехника переживает настоящий бум, ей начали увлекаться даже те люди, которые от электроники очень далеки (к ним отношусь и я). Прошло то время, когда нужно было сидеть ночами с паяльником, или травить платы. Все стало гораздо проще, нужно лишь купить Arduino, комплект проводков, сенсоры, датчики, моторчики и вперед к сборке своего первого робота. Таким образом, фокус разработчиков — любителей сместился с электроники и механической части к программированию.
Данная серия статей будет содержать информацию для быстрого старта по созданию своего первого робота, от покупки необходимых деталей до его запуска в наш мир.

Отмечу, что статьи я пишу по ходу самостоятельного изучения материала и создания своего первого робота.

Предполагается, что человек читающий статью уже имеет опыт в:
  • Установке и настройке среды разработки для android устройств.
  • Написании первой программы Hello World для android устройства.
  • Установке драйверов и среды разработки для Arduino.
  • Написании первой программы (включать, выключать) светодиоды на Arduino.


Сокращения

БТ – Bluetooth;
Андроид – устройство с ОС android, к примеру, смартфон;
Скетч – приложение для Arduino;
Ардуино – Arduino Nano.

Общая схема робота

Пусть робот имеет следующую общую схему (рис.1). Андроид, является «головным мозгом», в котором обрабатываются все данные и проводятся вычисления. На основании результатов вычислении по БТ каналу передаются команды, принимаемые БТ модулем и в дальнейшем поступающие по последовательному порту в Arduino. Arduino обрабатывает поступившие команды и выполняет их при помощи «мышц» (Моторы светодиоды и др.). Кроме того в Arduino поступают данные от «органов чувств» (сенсоры, датчики и др.), которые он приводит в удобный вид и отправляет при помощи БТ модуля в «головной мозг». И так все повторяет до бесконечности.
image
Рисунок 1
Обоснование именно такой схемы робота следующее:
-БТ модуль и Arduino Nano имеют низкую стоимость, а также маленький объем и вес.
-Андроид, не дорогое и доступное устройство, уже имеется у огромного количества людей, в том числе и у меня.
-Андроид имеет свои сенсоры, датчики и экран для вывода информации
-Простота написания кода для приложений как для андроида так и для ардуино.
Для упрощения задачи построения робота, предлагаю воспользоваться методом «от простого к сложному», т.е. как и при изучении языка программирования создадим первую программу «Hello word». Конечно, это будет не одна программа, а как минимум две (для ардуино и андроида). В каждой последующей статье функционал робота будет увеличиваться.

Постановка задачи

Собрать простенькое устройство, у которого имеются:
-2 кнопки (b1, b2).
-1 светодиод (Led).
И выполняется функционал:
-главное activity содержит 2 кнопки «Отпарвить 0» и «Отправить 1», при нажатии на которые от андроида к ардуино по БТ каналу передаются данные, соответствнно «0» и «1», ардуино их обработвает и зажигает либо тушит светодиод.
-данные о нажатии или отпускании кнопок от ардуино передаются в андроид, на экране главного activity выводится информация об этом.

Закуп необходимых деталей и узлов.

1) Android устройство – смартфон LG P500 (версия Android 2.3.3), либо любое другое устройство на базе Android версией 2.3.3 и выше. Цену не указываю, так как отдельно не покупал, а использую свой смартфон.
2) Arduino NanoV3.0 ( dx.com/ru/p/nano-v3-0-avr-atmega328-p-20au-module-board-usb-cable-for-arduino-118037 ) — 11,32$
3) Модуль Bluetooth ( dx.com/ru/p/bluetooth-board-module-4-pin-121326 ) — 10,57$
4) Макетные провода ( dx.com/ru/p/30cm-breadboard-wires-for-electronic-diy-40-cable-pack-80207 ) — 2,51$
5) Кнопки — 2шт., светодиод – 1 шт. – 50 рублей
Итого: 855,20 рублей.

Приступим к работе


Arduino

Соберем из имеющихся деталей схему (рис. 2)
image
Рисунок 2
Питание на arduino подается по USB кабелю, который подключается к компьютеру, по нему же загружается и программное обеспечение (скетч) выполняемое на нем. Необходимо отметить, что загружать скетч в arduino можно только тогда, когда отключено питание от Bluetooth модуля (Вывод (17) 3V3), в противном случае возникает ошибка.
Подробности установки среды разработки Arduino и драйверов можно найти на официальном сайте: arduino.ru/Guide/Windows
Ниже приведен скетч который необходимо загрузить в ардуино:
//Объявляем переменные
int led = 12; // Светодиод
int b1 = 11; // Кнопка
int b2 = 10; // Кнопка
int value_1,value_2 = 0; // Переменные необходимые для устранения дребезга контак
char incomingbyte; // переменная для приема данных

//Инициализация переменных
void setup() {
  Serial.begin(38400);
  pinMode(led,OUTPUT);
  digitalWrite(led, HIGH);
  pinMode(b1,INPUT);
  digitalWrite(b1, HIGH);  
  pinMode(b2,INPUT);
  digitalWrite(b2, HIGH);  
}
//Обрабатываем нажатие кнопок и устраняем дребезг контактов
void contact_bounce(int buttton){
  value_1 = digitalRead(buttton);
  if (!value_1){
    delay(80);
    value_2 = digitalRead(buttton);
    if (!value_2){
       Serial.print("Press button b"); 
       Serial.println(buttton); 
    }
  }
}
//Основной цикл программы
void loop() {
  if (Serial.available() > 0){
    incomingbyte = Serial.read();
      if (incomingbyte == '1'){
        digitalWrite(led,HIGH);
        Serial.println("LED ON");
      }
      if (incomingbyte=='0'){
        digitalWrite(led,LOW);
        Serial.println("LED OFF");
      }
  }
  contact_bounce(b1);
  contact_bounce(b2);
}

Объявляем переменные, напротив каждой стоит комментарий.
Инициируем последовательное соединение и задаем скорость передачи данных в бит/c (бод). Мой БТ модуль работает на скорости 38400, но может быть скорость 9600 (скорость БТ модуля можно задавать при помощи AT команд). Устанавливаем режим работы заданного входа/выхода(pin) как входа или как выхода. Подаем HIGH значение на входы и выходы.
Функция void contact_bounce(int buttton) обрабатывает нажатие кнопок и позволяет устранить дребезг контактов, который возникает при соприкосновении или расхождении контактов в механических переключающих устройствах, таких, как кнопка, происходит многократное замыкание и размыкание.
В основном цикле Loop слушаем последовательный порт, и если на него пришли данные мы их обрабатываем. Кроме того вызываем функцию обработки нажатия кнопок и устранения дребезга контактов.
Загрузив скетч в ардуино, можем проверить его работоспособность – запустив монитор порта. При нажатии кнопок в окне монитора будет выводиться надпись «Press button b». Проверить работу светодиода удастся только после написания приложения для андроида.
В данной статье для ардуино будет использоваться только этот скетч.Приступим к разработки приложения для андроида.

Android

Для удобства отладки приложения андроида, рекомендую использовать не «Android virtual device», а реальный смартфон с ОС Android версии от 2.3.3 подключенный через USB кабель к компьютеру в режиме «Отладки». Существует огромное количество статей как это сделать.

Android ШАГ 1

Создаем новый проект «Android application project»
Для работы с БТ необходимо выставить права на использование его нашим приложением. Для этого заходим в манифест, выбираем закладку Permissions, нажимаем add, далее Uses permission, и устанавливаем следующие права: android.permission.BLUETOOTH, android.permission.BLUETOOTH_ADMIN
Теперь оформим основное activity, в res/layout/activity_main.xml поместим код:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
        <Button
            android:id="@+id/b1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Отправить "0"" />
        <Button
            android:id="@+id/b2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Отправить "1"" />
    </LinearLayout>
    <TextView
        android:id="@+id/txtrobot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Поле текстовых сообщений" />
</LinearLayout>

Таким образом, основное activity примет вид:
image
Рисунок 3
Текстовое поле «txtrobot», будет отображать всю необходимую нам информацию. Кнопки b1 и b2, будут отправлять команды в arduino.
Теперь переходим в src/../MainActivity.java здесь и будет располагаться наш основной код. Подключим пакет Api для Bluetooth:
import android.bluetooth.*;

Перед тем как использовать БТ необходимо убедится, что в нашем андроиде он присутствует. Создадим экземпляр класса BluetoothAdapter (отвечающий за работу с установленным в андроиде БТ модулем):
btAdapter = BluetoothAdapter.getDefaultAdapter();  

Если андроид не имеет БТ то будет возвращено null:
TextView mytext = (TextView) findViewById(R.id.txtrobot);
if (btAdapter != null){
        	mytext.setText("Bluetooth присутствует");			
}else
{
        	mytext.setText("Bluetooth отсутствует");
}


В данном виде программу уже можно запустить. На экране андроида вы должны увидеть надпись: «Bluetooth присутствует».
Теперь необходимо убедиться, что БТ включен, либо предложить его включить. Добавим константу:
private static final int REQUEST_ENABLE_BT = 1;

и код:
if (btAdapter.isEnabled()){
 	mytext.setText("Bluetooth включен. Все отлично.");			
}else
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
       	startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}


Запустив измененное приложение на андроиде, Вам будет выдан «Запрос разрешения на включения Bluetooth», подтвердив его, тем самым Вы активируете БТ.
Полный код приложения:
package com.robot.bluetest;

import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
	private static final int REQUEST_ENABLE_BT = 0;
	public BluetoothAdapter btAdapter;
	public TextView mytext;
	
@Override
protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
				
		btAdapter = BluetoothAdapter.getDefaultAdapter();
		mytext = (TextView) findViewById(R.id.txtrobot);     
    	
        if (btAdapter != null){
        	if (btAdapter.isEnabled()){
        		mytext.setText("Bluetooth включен. Все отлично.");			
        	}else
        	{
        		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        	}
        	
        }else
        {
        	mytext.setText("Bluetooth отсутствует");
        }
	}
}



Android ШАГ 2

Для дальнейших экспериментов, необходимо «спарить» наш андроид и БТ модуль, для этого на андроиде в настройках БТ, выполним поиск и подключимся к БТ модулю, пароль «1234».
Если вдруг удаленный БТ модуль не будет нормально работать, просто отключите от него все провода (VCC, GND,RX,TX), тем самым сделав жесткую перезагрузку, и снова подключите их — это должно помочь.
Теперь попробуем программно подключится к удаленному БТ модулю: основной код подключения разместим в onResume. onResume – это одно из состояний нашего Activity, а именно, Activity видно на экране, оно находится в фокусе, пользователь может с ним взаимодействовать. Ниже приведен основной код Activity:
package com.robot.bluetoothrob2;

import java.io.IOException;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
	private static final int REQUEST_ENABLE_BT = 0;
	final String LOG_TAG = "myLogs";
	public BluetoothAdapter btAdapter;
	private BluetoothSocket btSocket = null;
	// MAC-адрес Bluetooth модуля
	private static String MacAdress = "20:11:02:47:01:60";
	public TextView mytext;
	
@Override
protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
				
		btAdapter = BluetoothAdapter.getDefaultAdapter();
		mytext = (TextView) findViewById(R.id.txtrobot);     
    	
        if (btAdapter != null){
        	if (btAdapter.isEnabled()){
        		mytext.setText("Bluetooth включен. Все отлично.");			
        	}else
        	{
        		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        	}
        	
        }else
        {
        	MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
        }
	}
	
	@Override
	public void onResume() {
	    super.onResume();
	  
	    Log.d(LOG_TAG, "***Пытаемся соединиться***");
	    // Получаем удаленное устройство по его MAC адресу
	    BluetoothDevice device = btAdapter.getRemoteDevice(MacAdress);
	    mytext.setText("***Получили device = " + device.getName() + "***");	
	  }
	
	private void MyError(String title, String message){
		    Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
		    finish();
	}
}

Доступ к удаленному БТ модулю получаем по его МАС адресу btAdapter.getRemoteDevice(MacAdress). Узнать MAC-адрес БТ модуля можно, при помощи программы для андроида: Bluetooth Terminal.
Что бы убедится, что доступ к БТ модулю получен, используем метод getName(), который позволяет получить имя удаленного БТ модуля, и выводим результат на экран андроида.
Также в этом примере, была добавлена возможность ведения лога, который по ходу выполнения программы можно просматривать, и обнаруживать существующие ошибки. Кроме того создана функция MyError, которая вызывается в том случае если нужно аварийно завершить приложение.
Запустив приложение, на экране андроида в текстовом поле «txtrobot» отобразится имя удаленного БТ модуля.

Android ШАГ 3

Доступ к удаленному БТ модулю получен, следующий наш шаг передать данные от андроида к нему. Для этого в onResume(), создадим сокет:
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);

где UUID (Universally Unique Identifier) — это стандарт идентификации, используемый в создании программного обеспечения. Добавим в определение константу UUID:
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

Чтобы не тормозить соединение отменим поиск других БТ устройств:
btAdapter.cancelDiscovery();	

Попытаемся подсоединиться:
btSocket.connect();

При не удаче закроем сокет:
btSocket.close();

Так как методы отправки и получения данных являются блокирующими, их следует выполнять в отдельном потоке, что бы предотвратить зависание основного приложения. Для этого создадим класс:
	 //Класс отдельного потока для передачи данных  
	 private class ConnectedThred extends Thread{
		 private final BluetoothSocket copyBtSocket;
		 private final OutputStream OutStrem;
		 
		 public ConnectedThred(BluetoothSocket socket){
			 copyBtSocket = socket;
			 OutputStream tmpOut = null;
			 try{
				 tmpOut = socket.getOutputStream();
			 } catch (IOException e){}
			 
			 OutStrem = tmpOut;
		 }
		 
		 public void sendData(String message) {
			    byte[] msgBuffer = message.getBytes();
			    Log.d(LOG_TAG, "***Отправляем данные: " + message + "***"  );
			  
			    try {
			    	OutStrem.write(msgBuffer);
			    } catch (IOException e) {}
		}
		 
		 public void cancel(){
			 try {
				 copyBtSocket.close();
			 }catch(IOException e){}			 
		 }
		 
		 public Object status_OutStrem(){
			 if (OutStrem == null){return null;		
			 }else{return OutStrem;}
		 }
	 }  

В конструкторе public ConnectedThred(BluetoothSocket socket) создается объект управляющий передачей данных через сокет:
tmpOut = socket.getOutputStream();

Для отправки данных из главного activity вызывается метод sendData(String message) с параметром текстового сообщения, которое преобразуется к типу byte. Метод cancel() позволяет закрыть сокет.
Напишем, обработчики нажатия кнопок b1 и b2, содержащие вызов функции sendData(String message) и сделаем запись об этом в логе. Полный код приложения приведен ниже:
package com.robot.bluetoothrob2;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;

import com.robot.bluetoothrob2.R;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
	private static final int REQUEST_ENABLE_BT = 1;
	final String LOG_TAG = "myLogs";
	private BluetoothAdapter btAdapter = null;
	private BluetoothSocket btSocket = null;
	private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
	private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private ConnectedThred MyThred = null;
	public TextView mytext;
	Button b1, b2;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
				
		btAdapter = BluetoothAdapter.getDefaultAdapter();
		mytext = (TextView) findViewById(R.id.txtrobot);     
    	
        if (btAdapter != null){
        	if (btAdapter.isEnabled()){
        		mytext.setText("Bluetooth включен. Все отлично.");			
        	}else
        	{
        		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        	}
        	
        }else
        {
        	MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
        }
        
        b1 = (Button) findViewById(R.id.b1);
        b2 = (Button) findViewById(R.id.b2);
               
        b1.setOnClickListener(new OnClickListener() {
          public void onClick(View v) {
           MyThred.sendData("0");
           mytext.setText("Отправлены данные: 0");
          }
        });
      
        b2.setOnClickListener(new OnClickListener() {
          public void onClick(View v) {
           MyThred.sendData("1");
           mytext.setText("Отправлены данные: 1");
          }
        });
        
	}
	
	@Override
	public void onResume() {
	    super.onResume();
	  
	    BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
	    Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());
	      
	    try {
	        btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
	        Log.d(LOG_TAG, "...Создали сокет...");
	      } catch (IOException e) {
	        MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
	      }	 
	    
	    btAdapter.cancelDiscovery();	    
	    Log.d(LOG_TAG, "***Отменили поиск других устройств***");
	    
	    Log.d(LOG_TAG, "***Соединяемся...***");
	    try {
	      btSocket.connect();
	      Log.d(LOG_TAG, "***Соединение успешно установлено***");
	    } catch (IOException e) {
	      try {
	        btSocket.close();
	      } catch (IOException e2) {
	        MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
	      }
	    }
	   	  
	    MyThred = new ConnectedThred(btSocket);	    
	  }
	
	  @Override
	  public void onPause() {
	    super.onPause();
	  
	    Log.d(LOG_TAG, "...In onPause()...");
	  
	    if (MyThred.status_OutStrem() != null) {
	        MyThred.cancel();
	    }
	  
	    try     {
	      btSocket.close();
	    } catch (IOException e2) {
	    	MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
	    }
	  }	
	
	private void MyError(String title, String message){
		    Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
		    finish();
	}

	  
	 //Отдельный поток для передачи данных  
	 private class ConnectedThred extends Thread{
		 private final BluetoothSocket copyBtSocket;
		 private final OutputStream OutStrem;
		 
		 public ConnectedThred(BluetoothSocket socket){
			 copyBtSocket = socket;
			 OutputStream tmpOut = null;
			 try{
				 tmpOut = socket.getOutputStream();
			 } catch (IOException e){}
			 
			 OutStrem = tmpOut;
		 }
		 
		 public void sendData(String message) {
			    byte[] msgBuffer = message.getBytes();
			    Log.d(LOG_TAG, "***Отправляем данные: " + message + "***"  );
			  
			    try {
			    	OutStrem.write(msgBuffer);
			    } catch (IOException e) {}
		}
		 
		 public void cancel(){
			 try {
				 copyBtSocket.close();
			 }catch(IOException e){}			 
		 }
		 
		 public Object status_OutStrem(){
			 if (OutStrem == null){return null;		
			 }else{return OutStrem;}
		 }
	 } 
}


Написанное нами приложение, позволяет передавать по БТ от андроида к ардуино данные – «0» и «1», которые в свою очередь для ардуино являются командами «1» — зажечь светодиод, а «0» — потушить. Таким образом, с учетом общей схемы робота, мы научились отдавать команды от «головного мозга» к «мышцам». Осталось дело за малым, научить андроид принимать данные от «органов чувств».

Android ШАГ 4

Получение данных также как и отправку, необходимо выполнять в отдельном потоке, чтобы избежать зависание главного activity. Принятые данные от БТ модуля мы будем выводить на экран главного activity в текстовом поле – MyText. Но возникает трудность — работа с view-компонентами доступна только из основного потока. А новые потоки, которые мы создаем, не имеют доступа к элементам экрана. Для решения данной проблемы воспользуемся механизмом Handler.
Handler — это механизм, который позволяет работать с очередью сообщений. Он привязан к конкретному потоку (thread) и работает с его очередью. Handler умеет помещать сообщения в очередь. При этом он ставит самого себя в качестве получателя этого сообщения. И когда приходит время, система достает сообщение из очереди и отправляет его адресату (т.е. в Handler) на обработку.
Объявим Handler:
Handler h;

Создадим свой Handler:
  h = new Handler() {
            public void handleMessage(android.os.Message msg) {
              switch (msg.what) {
              case ArduinoData:
	        	  byte[] readBuf = (byte[]) msg.obj;
	              String strIncom = new String(readBuf, 0, msg.arg1);                                         
	              mytext.setText("Данные от Arduino: " + strIncom);           
	              break;
              }
            };
          };

в нем реализуем метод обработки сообщений handleMessage. Мы извлекаем из сообщения атрибут what, obj и аргументы типа int. Преобразуем полученное сообщение в строку и выводим его в текстовое поле главного activity: mytext.setText(«Данные от Arduino: » + strIncom);
В потоке для передачи данных добавим функцию запуска этого потока, и разместим там цикл с функцией чтения данных:
public void run()
		 {
			 byte[] buffer = new byte[1024];
			 int bytes;
			 
			 while(true){
				 try{
					 bytes = InStrem.read(buffer);
					 h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
				 }catch(IOException e){break;} 
				 
			 } 			 
 }


Полный код приложения приведен ниже:
package com.robot.bluetoothrob2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.UUID;

import com.robot.bluetoothrob2.R;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.bluetooth.*;
import android.content.Intent;

public class MainActivity extends Activity {
	private static final int REQUEST_ENABLE_BT = 1;
	final int ArduinoData = 1;        
	final String LOG_TAG = "myLogs";
	private BluetoothAdapter btAdapter = null;
	private BluetoothSocket btSocket = null;
	private static String MacAddress = "20:11:02:47:01:60"; // MAC-адрес БТ модуля
	private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private ConnectedThred MyThred = null;
	public TextView mytext;
	Button b1, b2;
	Handler h;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
				
		btAdapter = BluetoothAdapter.getDefaultAdapter();
		mytext = (TextView) findViewById(R.id.txtrobot);     
    	
        if (btAdapter != null){
        	if (btAdapter.isEnabled()){
        		mytext.setText("Bluetooth включен. Все отлично.");			
        	}else
        	{
        		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        	}
        	
        }else
        {
        	MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");
        }
        
        b1 = (Button) findViewById(R.id.b1);
        b2 = (Button) findViewById(R.id.b2);
               
        b1.setOnClickListener(new OnClickListener() {
          public void onClick(View v) {
           MyThred.sendData("0");
           //mytext.setText("Отправлены данные: 0");
          }
        });
      
        b2.setOnClickListener(new OnClickListener() {
          public void onClick(View v) {
           MyThred.sendData("1");
          // mytext.setText("Отправлены данные: 1");
          }
        });
        
        h = new Handler() {
            public void handleMessage(android.os.Message msg) {
              switch (msg.what) {
              case ArduinoData:
	        	  byte[] readBuf = (byte[]) msg.obj;
	              String strIncom = new String(readBuf, 0, msg.arg1);                                         
	              mytext.setText("Данные от Arduino: " + strIncom);           
	              break;
              }
            };
          };
        
	}
	
	@Override
	public void onResume() {
	    super.onResume();
	  
	    BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);
	    Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());
	      
	    try {
	        btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
	        Log.d(LOG_TAG, "...Создали сокет...");
	      } catch (IOException e) {
	        MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");
	      }	 
	    
	    btAdapter.cancelDiscovery();	    
	    Log.d(LOG_TAG, "***Отменили поиск других устройств***");
	    
	    Log.d(LOG_TAG, "***Соединяемся...***");
	    try {
	      btSocket.connect();
	      Log.d(LOG_TAG, "***Соединение успешно установлено***");
	    } catch (IOException e) {
	      try {
	        btSocket.close();
	      } catch (IOException e2) {
	        MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");
	      }
	    }
	   	  
	    MyThred = new ConnectedThred(btSocket);	    
	    MyThred.start();
	  }
	
	  @Override
	  public void onPause() {
	    super.onPause();
	  
	    Log.d(LOG_TAG, "...In onPause()...");
	  
	    if (MyThred.status_OutStrem() != null) {
	        MyThred.cancel();
	    }
	  
	    try     {
	      btSocket.close();
	    } catch (IOException e2) {
	    	MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");
	    }
	  }	
	
	private void MyError(String title, String message){
		    Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
		    finish();
	}

	  
	 //Отдельный поток для передачи данных  
	 private class ConnectedThred extends Thread{
		 private final BluetoothSocket copyBtSocket;
		 private final OutputStream OutStrem;
		 private final InputStream InStrem;
		 
		 public ConnectedThred(BluetoothSocket socket){
			 copyBtSocket = socket;
			 OutputStream tmpOut = null;
			 InputStream tmpIn = null;
			 try{
				 tmpOut = socket.getOutputStream();
				 tmpIn = socket.getInputStream();
			 } catch (IOException e){}
			 
			 OutStrem = tmpOut;
			 InStrem = tmpIn;
		 }
		 
		 public void run()
		 {
			 byte[] buffer = new byte[1024];
			 int bytes;
			 
			 while(true){
				 try{
					 bytes = InStrem.read(buffer);
					 h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();
				 }catch(IOException e){break;} 
				 
			 } 
			 
		 }
		 
		 public void sendData(String message) {
			    byte[] msgBuffer = message.getBytes();
			    Log.d(LOG_TAG, "***Отправляем данные: " + message + "***"  );
			  
			    try {
			    	OutStrem.write(msgBuffer);
			    } catch (IOException e) {}
		}
		 
		 public void cancel(){
			 try {
				 copyBtSocket.close();
			 }catch(IOException e){}			 
		 }
		 
		 public Object status_OutStrem(){
			 if (OutStrem == null){return null;		
			 }else{return OutStrem;}
		 }
	 } 
}


В шаге 4, представлено приложение, которое позволяет, как передавать по БТ от андроида к ардуино команды, так и получать от ардуино данные. Таким образом, мы научились отдавать команды и принимать данные, а следовательно выполнили поставленную задачу.
Кроме того данное приложение является шаблоном, на его базе можно создавать более сложные приложения, которые в свою очередь смогут обрабатывать данные например от ультразвукового сенсора, а также отдавать команды моторчикам, для передвижения робота.
К следующей статье я сделал заказ новых деталей и модулей. Ниже приведен список и цены:
Наименовние Ссылка Цена y.e Цена руб Кол-во Сумма
Макет dx.com/p/solderless-breadboard-with-400-tie-point-white-121534 3,1 102,3 2 204,6
Ультразвуковой сенсор dx.com/p/ultrasonic-sensor-distance-measuring-module-138563 3,9 128,7 3 386,1
Драйвер для мотора 2 шт. dx.com/ru/p/hg7881-two-channel-motor-driver-board-blue-green-black-2-5-12v-215795 2,8 92,4 2 184,8
Платформа dx.com/ru/p/zl-4-smart-car-chassis-kit-for-arduino-black-yellow-152992 28,2 930,6 1 930,6
Проводки соединения dx.com/p/breadboard-jumper-wire-cord-kit-for-arduino-diy-140-piece-pack-138220 6,9 227,7 1 227,7
Питание dx.com/ru/p/dc-power-converter-module-for-electronic-diy-219232 2,3 75,9 1 75,9

ИТОГ: 1933,8
Михаил @sychidze
карма
18,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (33)

  • +2
    Впечатляет объем публикации. А ведь это только начало сбора робота :)

    p.s. Может сделать робота, собирающего роботов? :-)
    • +7
      Робота пишущего статьи о сборке роботов? :-)
      • +1
        Это следующий этап :)
        • +1
          Да, и пусть роботы, которых будет собирать этот робот, включают в себя веб-сервер, на котором запущены сайты, на которых эти роботы публикуют свои статьи о сборке роботов.
    • 0
      Не понимаю, что все так к объему статей придираются? Особенно не люблю, когда материал, который занимает страниц 5 А4 формата, разбивают на несколько постов. За примерами далеко ходить не надо. Вон вчерась один опубликовал перевод: «он очень объемный, так что будет две части» (с). А на деле читать особо нечего.

      Конкретно к вам претензий естесственно нет, просто подвернулся коммент в тему под накипевшее :)
      Сама статья нормального объема, основную часть которой кстати занимает код, который можно было спрятать под спойлер и статья сразу бы стала чище.

      И раз уж начал писать коммент: спасибо автору топика за то, что поделился опытом :)
      • 0
        Мне кажется, что перевод иногда логично разбивать на части, хотя бы потому что перевод — это утомительный и долгий процесс. Просто для себя, особенно, если автор не занимается переводами профессионально. Хотя читать порой действительно не очень удобно.
  • 0
    Интересно, добавлю в избранное чтобы почитать по-внимательнее на досуге.

    Если будет продолжение, в конце этой статьи сделайте ссылки на следующие части.

    Кстати, а вместо BlueTooth можно ли Wi-Fi использовать?
    Ещё в качестве спинного мозга хотелось бы использовать что-то по-сильнее Arduino, например старый нетбук с Linux. Можно ли управлять серво из Linux?
    • 0
      -Вместо BlueTooth можно использовать Wi-Fi модуль, к примеру вот такой
      dx.com/ru/p/vrm04-multifunction-uart-serial-port-to-ethernet-wi-fi-converting-adapter-module-w-antenna-blue-215334
      но данный модуль стоит дороже, сам объемнее и весит больше по сравнению с БТ модулем.
      -из Linux управлять можно, даже более того на базе Linux существует ROS(robot operation system — www.ros.org ), главное определиться по какому порту управлять серво моторами, и все равно придется использовать либо микроконтроллер либо другую микросхему, которая преобразует команду от нетбука в сигналы подаваемые на драйвера моторов.
      • 0
        Посмотрите в сторону BeagleBone, а также Raspberry Pi и подобных плат — полноценный компьютер с портами GPIO и многими другими, и размером как Arduino Uno.
        • +1
          Я тоже потихоньку делаю робота для детей и рассматривал вариант с Rapberry Pi. Малинка намного быстрее чем Arduino, однако GPIO не буферизовано и сгорает от малейшей ошибки, отсутсвуют ШИМ-выходы (управление моторами, серво) и аналоговые входы (дешёвые дальномеры). На ардуино легко писать работающий в реальном времени софт, использовать прерывания (например для счёта импульсов с энкодеров на колёсах).

          В результате остановился на гибридной схеме, когда Arduino занимается сенсорами и моторами и обрабатывает простейшие рефлексы. Например если дальномеры регистрируют пропасть, то нужно сразу же остановить моторы чтоб не упасть со стола. Raspberry играет роль головного мозга — к ней подключена камера с микрофоном (вынуто из дешёвой веб-камеры) и простейший аудиоусилитель с динамиком. На Raspberry крутится веб-сервер который занимается логикой и передаёт в ардуинный «спинной мозг» комманды вроде «развернись вправо на 90 градусов». Микрокомпьютеры соединяются по последовательному порту (выведен на GPIO) через преобразователь логических уровней 3,3V <-> 5V.

          image

          Пока у меня готова только механика, плюс оттестирована работа Arduino с датчиками, так что статья на хабре с полным кодом включая веб-сервер появится скорей всего уже весной. Фотографии можно посмотреть здесь.
          • 0
            Я тоже потихоньку делаю робота для детей и рассматривал вариант с Rapberry Pi. Малинка намного быстрее чем Arduino, однако GPIO не буферизовано и сгорает от малейшей ошибки, отсутсвуют ШИМ-выходы (управление моторами, серво) и аналоговые входы (дешёвые дальномеры). На ардуино легко писать работающий в реальном времени софт, использовать прерывания (например для счёта импульсов с энкодеров на колёсах).


            Поэтому я и написал сначала про BeagleBone: стоит почти столько же, но больше GPIO, есть ШИМ-выходы, АЦП и другое. Прерывания кстати тоже можно обрабатывать нормально, при большом желании даже некоторые realtime-фичи можно сделать (но не думаю, что понадобится — всё-таки производительность намного больше ардуины). Мне сейчас едут детали для робота, в качестве мозга как раз эта плата планируется :)
        • 0
          Согласен с тем, что стоит все-таки рассмотреть альтернативы. Например, вариант, основанный на Raspberry Pi: habrahabr.ru/company/grambo_pi/blog/203124/

          В этом случае отпадает необходимость в Bluetooth и программировать можно непосредственно как сам Raspberry Pi, так и контроллер.
    • +1
      Уважаемые, а за что меня «минусуют»? Я же задал вопрос по теме.
    • 0
      Нашёл вот интересные контроллеры:
      http://www.pololu.com/product/1356
      к компьютеру цепляются по USB и позволяют управлять серво-приводами. Программировать можно на Java, что должно обеспечить кросс-платформенность.
      • 0
        del
  • +2
    Из того, что бросилось в глаза: вы неправильно делаете debounce. Смысл процедуры состоит в том, чтобы убедиться, что контакт стабилен в течение некого промежутка времени, а вы проверяете в начале и в конце 80мс промежутка. А надо так:

    const int debounceDelay = 10;  // milliseconds to wait until stable
    
    boolean debounce(int pin) { // Used to distinguish between phantom keypresses and real ones.
      boolean state;
      boolean previousState;
      previousState = digitalRead(pin); // We store switch state,
      for (int counter=0; counter < debounceDelay; counter++) {
        delay(1);                       // wait for 1 millisecond,
        state = digitalRead(pin);       // read the pin,
        if (state != previousState) {
          counter = 0;                  // reset the counter if the state changes,
          previousState = state;        // and save the current state,
        }
      }
      return state;                     // here when the switch state has been stable longer than the debounce period.
    } 
    • +1
      Ваш код безусловно выглядит красивее, но дребезг контактов длится несколько микросекунд, поэтому приведенный мною код также работоспособен, так как состояние нажатия кнопки проверяется через 80 миллисекунд, к этому времени дребезг уже отсутствует.
    • 0
      Да ладно, вполне работоспособный код, только время надо увеличить. Будет почти что НЧ-фильтр, для частот ниже 1/2Т, согласно дедушке Котельникову :)
      Вот например www.ni.com/example/31251/en/
      А ваш код вносит задержку в программу целых 10 мс на обработку каждого нажатия, а за это время можно как минимумв космос улететь 10 тысяч инструкций выполнить.
      • 0
        я скопипастил из личного проекта — там речь идёт об управлении вентилями подачи водопроводной воды на 220 вольт, так что с 10ms — это я конкретно перестраховывался, пяти замеров подряд хватит с головой. В любом случае, несколько последовательных замеров лучше, чем только два.
        • 0
          Я к тому, что delay это непозволительная роскошь для системы реального времени, даже за миллисекунду задержки можно было бы пару двухбайтовых знаковых чисел перемножить.
      • 0
        А нет, я наврал, у автора тоже делэй на 80 мс. А надо просто опрашивать состояние входа по таймеру (время сэмплирования), если, например, два раза подряд «1» то кнопка нажата, если 4 раза — кнопка долго нажата. А в свободное от опроса кнопок время другой код выполнять — реалтаймовая система.
  • +2
    Я вижу вы разрабатываете собственный протокол для общения с Arduino. Может имеет смысл использовать стандартный протокол Firmata, он как раз предназначен для коммуникации умных устройств со «спинным мозгом» на Arduino.
    • 0
      Спасибо за информацию, к следующим статьям ознакомлюсь и применю.
  • 0
    Когда создавал систему управления подсветкой — habrahabr.ru/post/165861/, то писал так же методы отправки-получения данных в/из Ардуино через синезуб. Столкнулся с тем, что часть данных в отправляемых пакетах терялась — из-за чего даже пришлось вводить валидаторы на полученные данные.
    Вы с этим столкнулись?
    • 0
      При отправке от андроида к ардуино, все отлично, сколько раз проверял, все данные доходили.
      При обратной связи — от ардуино к андроиду, часть данных теряется(первые 1-2 символа). Проблема имеется еще не начинал ее решать.
  • 0
    Не много не по теме робототехники, но добавлю свои 2 копейки. Все же советую сразу избавиться от BT и перейти на WiFi. Пусть дороже и тяжелее. Но более стабильная связь.

    По работе реализовывал протокол общения iPod'ов с внешним BT устройством. В тепличных условиях все работало стабильно. Но как только вышли из теплицы, то выползли проблемы. Начали тестировать у себя и оказалось что как только в зоне связи появляется еще несколько BT устройств (телефоны, гарнитуры и т.п.), то передаваемые данные просто пропадают. Т.е. я фиксирую отправку данных, а до внешнего устройства они не доходят.

    Пришлось разработчиков просить реализовать WiFi. Проблемы со связью ушли.
  • 0
    Что за сайт такой dx.com/, первый раз встречаю. Как быстро доставляют? Почему там, а не на ebay? Цены на dx чуть выше, но незначительно.
    • 0
      Это китайский сайт. Я живу в г.Благовещенске напротив китайского г.Хэйхэ, поэтому имею возможность заказывать товар с ТаоБао, dx либо с других китайских сайтов, доставлять их в Хэйхэ(5-7 дней), а от г.Хэйхэ до г.Благовещенска — 1-2 дня. Таким образом скорость доставки очень высокая.
      • 0
        Вам надо бизнес налаживать, с Благи до Москвы даже почта россии за 7 дней доставит, итого две недели. С ebay мне по два месяца доставляют, на данный момент так и не доставили некоторые товары заказанные 4-го октября! Товары ушли в архив и теперь даже претензию некому предъявить. По адекватным ценам я б лучше с Благи заказывал.
        • 0
          Из китая EMS за ~7 дней доставил… но ценник… грустный, да. А человеку, чтобы массово ввозить придётся же кучу бумажек оформлять… в общем ценник до EMS-ного поднимется… и зачем тогда?
        • +1
          Спасибо за совет. У меня уже имеется сайт, по доставке товаров с таобао, но торговля, мне не по душе, больше интересует ит и робототехника
  • +1
    Читая подобные статьи по робототехнике и учитывая свой опыт, все чаще приходит одна мысль.
    Подобные проекты полезны в основном для самого экспериментатора, так как он приобретает опыт, как в разработке, так и в точном изложении своих действий. За это и поддерживаю автора.
    А мысль заключается в том, что подобные посты уже теряют свою новизну и привлекательность, так как по сути ничего нового для сообщества не дают. ИМХО нужны отдельные законченные функциональные блоки для предобработки сенсорной информации и механические блоки-приводы (например механическая рука и интерфейсвзаимодействия с ней, система команд). Наверняка подобные проекты есть, но на хабре я их не видел.
    Вот тогда, объединив «мозг» и «руку», робот будет более похож на себя и проект будет обладать новизной.
    • 0
      Понятное дело, что если автор тратит своё время и деньги, то он расчитывает извлечь из этого пользу, например в виде обучения. Робот хорош тем, что определяет понятную, интересную, видимую цель, задаёт критерии для оценки хорошо ли сделана работа. Если просто поставить себе расплывчатую задачу «а вот было-бы неплохо научиться программировать под андроид», то есть риск при первых же трудностях пойти по пути наименьшего сопротивления, а то и просто забросить дело на полупути. А вот робота можно пощупать, не стыдно показать друзьям, при том что реальное железо не прощает ошибок и заставляет таки научиться правильно использовать имеющиеся библиотеки или написать новые.

      Что интересно, этот робот по архитектуре почти не отличается от описанного в соседней статье. Автор этой статьи сконцентрировался пока на программировании «головного мозга», в другой статье фокус на «железе», рассматривается вопрос подавления помех по питанию. Вместе статьи гармонично дополняют друг друга, при этом думается что андроидный код с минимальными доработками сможет управлять и тем роботом. Получается универсальная архитектура «сеносоры<->ардуино<->последовательный порт (bluetooth)<->управляющая программа», где один автор может быть силён в программировании, другой в электронике, может ещё кто-то построит по этой схеме дорогого, но очень красивого робота. А хаброчитатель сможет выбрать из этого разнообразия то, что подходит лично ему.

      Жду продолжения серии статей.

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