Реклама ⓘ
Главная » Arduino
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Похожие статьи:


Реклама ⓘ

Android и Arduino. Обмен данными

В предыдущих двух частях мы рассмотрели вопрос передачи информации от Android-устройства в плату Arduino, и обратную задачу: передача информации от Arduino платы на устройство с Android. Настало время для объединения этих двух методик, чтобы получить полноценный двухсторонний обмен информацией между Android и Arduino.

Для этого, мы соберем простенький проект с использованием ультразвукового дальномера и пьезо-буззера.

Ультразвуковой дальномер

Алгоритм работы

На экране устройства с Android отображается активити с кнопкой "Измерить", при нажатии на которую, на плату Arduino приходит соответствующая команда. Плата Arduino обрабатывает команду и начинает цикл измерений, после чего вычисляется средняя дистанция до препятствия в сантиметрах. Данное расстояние до обьекта, передается обратно в Android-устройство, где отображается в виде текста, а также на ползунке (ProgressBar).
В Android также происходит обработка данных: если расстояние до обьекта меньше 20 см, то происходит передача управляющего сигнала на Arduino для включения буззера. Это естественно можно было бы сделать и в коде Arduino, но для наглядности я возложил эту задачу на плечи Android устройства. В общем получился небольшой парктроник.

Итак, для начала необходимо определится с управляющими командами. Они должны быть одинаково определены и в Arduino и в Android устройстве. Я выбрал следующие числа:
1 - команда разрешения передачи
2 - команда запрета передачи
3 - команда включения буззера

С первыми двумя командами получилось немного запутано, т.к. я не смог заставить Android корректно принимать один посыл с данными (пробовал и в цикле передавать и по времени, но Android упорно не хочет принимать данные, подозреваю, что это связанно с ADB и при использовании Accessory Mode таких проблем быть не должно). Поэтому когда Arduino принял команду 1 (разрешение передачи) он производит замер расстояния и включает беспрерывную передачу данных в цикле. Как только Android принял данные, он передает к Arduino команду 2, чтобы тот остановил передачу данных.

Программа для Arduino

Скетч для Arduino:

#include 
#include 

// Adb connection.
Connection * connection;             // Adb connection.

#define COMMAND_SEND_TRUE  1   // команда разрешения передачи
#define COMMAND_SEND_FALSE 2   // команда запрета передачи
#define COMMAND_PLAY_BEEP  3   // команда включения буззера

const int numOfReadings = 10;        // кол-во замеров (элементов массива)
int readings[numOfReadings];         // значения измерений в массиве
int arrayIndex = 0;                  // индекс элемента в массиве
int total = 0;                       // всего значений
int averageDistance = 0;             // средняя дистанция

// настройка пинов и переменных для УЗ датчика
int echoPin = 2;                     // DYP_ME007 ECHO pin
int initPin = 3;                     // DYP_ME007 TRIG pin
int BeeperPin = 8;                   // pin буззера
unsigned long pulseTime = 0;         // длительность пульса в микросекундах
unsigned long distance = 0;          // расстояние в (см)

boolean SendToAndroid = false;

void setup() {
  pinMode(initPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(BeeperPin, OUTPUT);  // Буззер

  // формируем массив
  for (int thisReading = 0; thisReading < numOfReadings; thisReading++) {
    readings[thisReading] = 0;
  }
  
  Serial.begin(115200);
  
  // Инициализация подсистемы ADB.  
  ADB::init();

  // Open an ADB stream to the phone's shell. Auto-reconnect. Use any unused port number eg:4568
  connection = ADB::addConnection("tcp:4568", true, adbEventHandler);  
  
} 

void loop() {
  if(SendToAndroid == true) makeDimension();
  ADB::poll();    // Poll the ADB subsystem.
}

void adbEventHandler(Connection * connection, adb_eventType event, uint16_t length, uint8_t * data)
{
  if (event == ADB_CONNECTION_RECEIVE)   // Если приняли данные
  {
    Serial.print("data:"); // Вывод в Serial Monitor для отладки
    Serial.println(data[0],DEC);
    if((data[0]) == COMMAND_SEND_TRUE) SendToAndroid = true;   // Флаг, что надо вкл. передачу данных
    else if ((data[0]) == COMMAND_SEND_FALSE) SendToAndroid = false;  //Флаг, что данные приняты и откл. передачу данных
    else if ((data[0]) == COMMAND_PLAY_BEEP) playBeep(); 
  }
  else if (event == ADB_CONNECTION_OPEN) Serial.println("ADB connection open");
  else if (event == ADB_CONNECTION_CLOSE) Serial.println("ADB connection close");
  else {
    Serial.println(event);
  }
}

void makeDimension() {
  for (int i = 0; i < numOfReadings; i++) { 
    digitalWrite(initPin, HIGH);              // посылаем импульс длительностью 10мс
    delayMicroseconds(10); 
    digitalWrite(initPin, LOW);
    
    pulseTime = pulseIn(echoPin, HIGH);             // Считываем длительность пришедшего импульса
    distance = pulseTime/58;                        // Дистанция = (длит. импульса / 58) см
    total= total - readings[arrayIndex];
    readings[arrayIndex] = distance;
    total= total + readings[arrayIndex];
    arrayIndex = arrayIndex + 1;
    // После того, как достигли последнего элемента, начинаем сначала
    if (arrayIndex >= numOfReadings)  {
      arrayIndex = 0;
    }
    //Serial.println(distance, DEC);
  }

  averageDistance = total / numOfReadings;      // вычисляем среднюю дистанцию

  //Serial.println(averageDistance, DEC);
  connection->write(2,(uint8_t*)&averageDistance);  // Отсылаем 2 байта
  delay(10);
}

void playBeep() {
  for (int j = 0; j < 10; j++) { 
    analogWrite(BeeperPin, 20); 
    delay(50);
    analogWrite(BeeperPin, 0);
    delay(150); 
  }
}

В самом начале мы определяем 3 константы - это команды для передачи сообщений между устройствами: COMMAND_SEND_TRUE = 1, COMMAND_SEND_FALSE = 2, COMMAND_PLAY_BEEP = 3

Обработчик adbEventHandler() вызывается каждый раз при принятии данных и при наступлении других событий от ADB (открытие и закрытие соединения).

Функция makeDimension() производит 10 замеров расстояний, а затем вычисляет по ним среднее значение, которое через команду connection->write >() 2-мя байтами отправляется в Android устройство.

С функцией playBeep() все просто - она предназначена для проигрывания 10-ти коротких звуков через буззер.

Программа для Android

Наше окно активити будет состоять из следующих ключевых элементов:
кнопка (Button) - для посылки команды измерения расстояния
текстовое поле (TextView) - для отображения полученного расстояния
прогресс-бар (ProgressBar) - для визуального отображения расстояния (максимум - 500 см)
иконка соединения (ImageView) - отображается при активном соединении с Android устройством.

Активити

XML файл данного активити см. в прикрепленных файлах

Файл для главного Activity содержит следующий код:

package com.example.arduino54;

import java.io.IOException;

import org.microbridge.server.Server;
import org.microbridge.server.AbstractServerListener;

import com.example.arduino54.R;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Button;

public class MainActivity extends Activity {
    
    private int Distance = 0;
    public final String APP_NAME = "arduino54";
    
    public final byte COMMAND_SEND_TRUE = 1;    // Команда разрешения передачи
    public final byte COMMAND_SEND_FALSE = 2;   // Команда запрета передачи
    public final byte COMMAND_PLAY_BEEP = 3;   	// Команда включения буззера
    
    public final int SYS_COMMAND_DATA = 0;   		// Внутренняя команда: передача данных
    public final int SYS_COMMAND_CONNECTED = 1;		// Внутренняя команда: соединение установлено
    public final int SYS_COMMAND_DISCONNECTED = 2;	// Внутренняя команда: соединение потеряно
    
    Server server = null;
    ImageView connectedImage;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
               
        // Создаем TCP сервер (на основе сервера MicroBridge LightWeight)
        try
        {
        	server = new Server(4568); //Этот же порт необходимо использовать и на ADK-плате
        	server.start();            
        } catch (IOException e)
        {
        	Log.e(APP_NAME, "Unable to start TCP server", e);
        	System.exit(-1);
        }
        
        connectedImage = (ImageView) findViewById(R.id.imageConnected);
        connectedImage.setAlpha(20);
         
        Button Button1 = (Button)findViewById(R.id.button1);
        Button1.setOnClickListener(new View.OnClickListener() {
        	public void onClick(View v) {
        		try
        		{
        			server.send(new byte[] {(byte) COMMAND_SEND_TRUE});	//Посылаем данные
        			//Log.d(APP_NAME, "data_send:"+bSend);
        		} catch (IOException e)
        		{
        			Log.e(APP_NAME, "Problem sending TCP message", e);
        		}	
        	}
		});
        
          
        server.addListener(new AbstractServerListener() {

            @Override
            public void onReceive(org.microbridge.server.Client client, byte[] data)
            {
				Log.d(APP_NAME, "data0:"+data[0]+"; data1:"+data[1]);
				if (data.length<2) Log.e(APP_NAME, "Размер данных менее 2-х байт:"+data.length);
				else {
					try
					{
						server.send(new byte[] {(byte) COMMAND_SEND_FALSE});	//Посылаем данные
					} catch (IOException e)
					{
						Log.e(APP_NAME, "Problem sending TCP message", e);
					}	
				}

				Distance = ((data[1] << 8) | (data[0] & 0xFF));	// Формируем слово из 2-х байт
    
                //Any update to UI can not be carried out in a non UI thread like the one used
                //for Server. Hence runOnUIThread is used.
                runOnUiThread(new Runnable() {
                    //@Override
                    public void run() {
                        new UpdateData().execute(Distance,SYS_COMMAND_DATA);
                    }
                });
            }
            
            //@Override
            public void onClientConnect(org.microbridge.server.Server server, org.microbridge.server.Client client){
            	Log.d(APP_NAME, "ClientConnected");
            	runOnUiThread(new Runnable() {
                    public void run() {
                        new UpdateData().execute(0,SYS_COMMAND_CONNECTED);
                    }
                });
            }
            
            public void onClientDisconnect(org.microbridge.server.Server server, org.microbridge.server.Client client){
            	Log.d(APP_NAME, "ClientDisconnected");
            	runOnUiThread(new Runnable() {
                    public void run() {
                        new UpdateData().execute(0,SYS_COMMAND_DISCONNECTED);
                    }
                });
            }   
            
        });     
    }
    
    @Override
    protected void onDestroy (){
    	super.onDestroy();
    	server.stop();
    }
    
    class UpdateData extends AsyncTask< Integer, Integer, Integer[]> {
        // Called to initiate the background activity
        @Override
        protected Integer[] doInBackground(Integer... ArdState) {
            if((ArdState[0] < 20) && (ArdState[0] != 0)){	//Если расстояние меньше 20см
            	try
				{
					server.send(new byte[] {(byte) COMMAND_PLAY_BEEP});
				} catch (IOException e)
				{
					Log.e(APP_NAME, "Problem sending TCP message", e);
				}	
            }
        	return (ArdState);  //Возвращаем в onPostExecute()
        }
        
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // Not used in this case
        }
        
        @Override
        protected void onPostExecute(Integer... result) {      	
        	Log.d(APP_NAME, "onPostExecute[0]:"+result[0]);
        	Log.d(APP_NAME, "onPostExecute[1]:"+result[1]);
        	

        	if(result[1] == 1){
        		connectedImage.setAlpha(255);
        	}
        	else if(result[1] == 2){
        		connectedImage.setAlpha(20);
        	}
        	                     
            TextView txt_Distance_Arduino = (TextView) findViewById(R.id.textDistance);
            txt_Distance_Arduino.setText(String.valueOf(result[0]+" см"));    // Выводим на activity дистанцию
                      
            ProgressBar mProgressBar = (ProgressBar)findViewById(R.id.progressBar1);
            mProgressBar.setProgress(result[0]);
        }
    }
}

Здесь мы для класса server определяем метод server.addListener(new AbstractServerListener() {}), а также: onReceive(), onClientConnect() и onClientDisconnect() который вызывается при получении данных от сервера MicroBridge, при соединении и разьединении.

На кнопке Button1 мы вешаем обработчик события нажатия setOnClickListener(). При нажатии на кнопку вызывается данный метод и посылает на плату Arduino команду COMMAND_SEND_TRUE, по которой Arduino производит измерение расстояния и передачу значения расстояния.

В методе onReceive(), как только мы приняли данные, то мы сразу же отсылаем обратно команду COMMAND_SEND_FALSE, для того, чтобы Arduino выключил передачу пакетов. Об этом алгоритме я писал выше.

Обратите внимание, что для передачи данных отдельному потоку, мы используем внутренние системные команды SYS_COMMAND_DATA, SYS_COMMAND_CONNECTED и SYS_COMMAND_DISCONNECTED . Команды передаются 2-м элементом массива, а в первом элементе содержится измеренное расстояние, полученное от Arduino.

При срабатывании события onClientConnect(), создается новый поток в который передается массив с командой SYS_COMMAND_CONNECTED (в нашем случае 0), и в методе onPostExecute() путем установки значения Alpha в максимальное 255, происходит отображения иконки соединения. При поступлении команды SYS_COMMAND_DISCONNECTED устанавливается Alpha в значение 20, иконка становится блеклой и ее почти не видно, это означает что соединение не установлено. Прозрачность Alpha устанавливается методом setAlpha(int).

Когда поток принимает данные из метода onReceive, то в методе doInBackground() происходит сравнение условия, и если расстояние не рано нулю и меньше 20 см, то методом server.send() посылается команда COMMAND_PLAY_BEEP для включения буззера на плате Arduino.

В методе onPostExecute() происходит вывод UI элементов для отображения численного значения расстояния и полоски на прогрессбаре.

Фото макетной платы

В прикрепленном файле вы можете скачать проекты для Arduino и Android, а также все необходимые библиотеки

Прикрепленные файлы:

Теги:

Колтыков А.В. Опубликована: 2012 г. 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (10) | Я собрал (0) | Подписаться

0
Radan #
Неплохо будет написать программу управления светодиодами - аля бегущие огни или ещё лучше цветомузыка. Тема найдёт своего читателя!
Ответить
0
Колтыков А.В. #
Бегущие огни не вопрос сделаем, как раз вчера на eBay заказал RGB-светодиодную ленту, может на ней что-нибудь и придумаю.
А вот с цветомузыкой надо будет подумать, не уверен, что хватит быстродействия ADB, тут бы работа в режиме ОАО лучше подошла бы, но все равно попробую.
Ответить
0
Andra #
А с этой платой можно использовать программу Standard Android ADK Demo Kit?
Ответить
0
Колтыков А. #
Можно, только тут встает вопрос совместимости вашего Андроид девайса с протоколом AOA. Об этом я писал в первой статье
Ответить
0
Андрей #
А вот примеры с использованием интересных внутренностей Android-телефона были бы актуальны. Считать данные GPS с телефона, отправить измеренный параметр Ардуинкой по электр.почте телефона либо по WiFi.
Ответить
0
Колтыков А. #
Дело в том, что у меня планшет без GPS, 3G и даже Bluetooth. Хочу купить новый, где все это есть, как будет, так все сразу постараюсь описать. Пишите, что конкретно вы хотели бы видеть. К примеру передачу данных с GPS в Arduino не могу придумать практическое применение...
Ответить
0
Андрей #
GPS был только примером, а вот с WiFi просторы шире :). В смысле: отправка оповещений и данных сенсоров по почте либо выкладка на сайт. А если WiFi нет открытого в зоне телефона, то тоже самое через GSM/GPRS.
Я тоже в ближайшие месяцы буду копать в этом направлении, чтобы убедиться, что (как где-то писали) телефон со всем его функционалом может быть отличным и недорогим дополнением к Ардуинке. Сейчас в китайских мобилах куча всего и за недорого, что грех не попробовать :)
Ответить
0
Колтыков А. #
Понял. На следующей недельке начну проект по изготовлению робота Android <-> Bluetooth <-> Arduino (либо если прийдет на Stellaris LM4F120 LaunchPad). Потом возможно раскошелюсь на Wi-Fi модуль и опишу тоже самое применительно к Wi-Fi.
Ответить
0
Yury #
Хотелось бы увидеть, как можно по команде с Ардуино вызвать программу, вывести в ней нужные данные с Ардуино, и потом, опять же по команде с Ардуино закрыть ее, переключившись на программу которая была запущена до этого.
Хочу реализовать визуализацию парктроника в авто. Парктроники штатные, Ардуино слушает шину авто (БМВ, шина iBus).
Ответить
0
Валерий #
Хотелось бы развить тему и рассмотреть вопросы обмена информацией между андроид и ардуино через блютуз. Цель: использовать экран планшета для контроля и управления устройствами на ардуино. Преимущества:
1. Отпадает необходимость в установке индикатора на каждом устройстве.
2. Появляется возможность удаленно управлять различными устройствами на ардуино и контролировать их работу (например в системе умного дома или системе полива на даче).
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Arduino UNO
Arduino UNO
Модуль измерения тока на ACS712 (30А) Pickit 2 - USB-программатор PIC-микроконтроллеров
вверх