Недавно постучался ко мне человек и попросил помочь сделать программку которая сможет контачить к Arduino с Android девайса. Порывшись в интернете я нарыл статью в которой рассказывается как раз то что я искал, и я решил что ее нужно к себе в блог переписать потому что она очень хорошо написана, все равно я собирался разбираться с Arduino.
Далее со слов автора статьи.
Я буду использовать Bluetooth модуль HC-06, однако для других модулей HC-04, HC-05 и т.п. схема подключения такая же (за исключением светодиода). Плата Arduino Nano V3.
Для наглядности, к плате Arduino я подключил красный светодиод, к 12-пину, но можно использовать и встроенный LED (обычно 13 пин).
Скетч для Arduino следующий:
Программа работает очень просто. После запуска или сброса устройства, в последовательный порт выводится сообщение с предложением нажать 1 или 0. В зависимости от нажатой (принятой) цифры светодиод будет загораться или гаснуть.
Теперь, что касается Android. Мы рассмотрим два примера, в первом мы будем передавать данные от Android-устройства к arduino, а во втором примере мы рассмотрим двусторонний обмен данными между устройствами. Второй пример сложнее и в части понимания и по сложности кода, т.к. используются потоки (thread).
Мы будем использовать Java код, с явным указанием MAC-адреса устройства, к которому мы будем подключаться. Т.к. если делать интерфейс обнаружения Bluetooth-устройств, их выбора, подключения к ним и т.д., то код будет очень большой и для некоторых читателей труднопонимаем. Но для тех, кому интересно могут посмотреть стандартный пример Bluetooth Chat.
Узнать MAC-адрес можно к примеру в программе для Android'а: Bluetooth Terminal:
Нас интересует устройство BOLUTEK (наш модуль HC-06, подключенный к Arduino), его MAC адрес: 00:15:FF:F2:19:4C. Его и надо будет в дальнейшем прописать в программе.
Первая программа очень простая, главное окно активити будет содержать 2 кнопки: включить LED и выключить LED. При нажатии на кнопку включения LED, по Bluetooth будет передаваться «1», при нажатии на выключение LED — «0».
В файле манифеста необходимо прописать 2 строки разрешения работы с Bluetooth:
Сам код главного активити:
Данный код найден на одном из зарубежных блогов и слегка модернизирован. Как видно выше, на кнопки мы вешаем обработчики событий. При нажатии на кнопку передается строка 1 или 0 через sendData() в буфер Bluetooth адаптера. Полный проект с исходными кодами приведен ниже. Для работы программы, необходим Android не ниже версии API15, т.е. 4.0.3 и выше.
А вот здесь пришлось повозиться. Дело в том, что в Android'е для приема данных от какого-либо устройства необходимо создавать отдельный фоновый поток, чтобы у нас не зависало основное активити. Для этого мы задействуем thread и все данные будут приниматься в отдельном потоке.
На окно главного активити мы добавим новый элемент TextView, который будет служить для отображения принятых данных от Arduino. Сам java-код главного активити я постарался хорошо прокомментировать, чтобы сделать его удобочитаемым:
В данном примере для отправки данных мы используем отдельный поток Thread. Тоже самое и для приема данных — метод run(). Также обратите внимание на класс Handler, который служит для организации очереди сообщений и их вывода в главное активити. Дело в том, что в фоновом потоке нельзя напрямую выводить что-либо в главное активити, т.к. это приведет к «крашу» программы.
Класс StringBuilder используется для формирования строки из принятых данных. После, происходит поиск конца строки с символами \r\n, и если они найдены, то строка отображается на активити и обьект sb очищается, чтобы не произошло склейка с последующими принятыми данными.
Скачать исходники:
Dropbox & Github
Далее со слов автора статьи.
Arduino
Я буду использовать Bluetooth модуль HC-06, однако для других модулей HC-04, HC-05 и т.п. схема подключения такая же (за исключением светодиода). Плата Arduino Nano V3.
Для наглядности, к плате Arduino я подключил красный светодиод, к 12-пину, но можно использовать и встроенный LED (обычно 13 пин).
Скетч для Arduino следующий:
char incomingByte; // входящие данные
int LED = 12; // LED подключен к 12 пину
void setup() {
Serial.begin(9600); // инициализация порта
pinMode(LED, OUTPUT);
Serial.println("Press 1 to LED ON or 0 to LED OFF...");
}
void loop() {
if (Serial.available() > 0) { //если пришли данные
incomingByte = Serial.read(); // считываем байт
if(incomingByte == '0') {
digitalWrite(LED, LOW); // если 1, то выключаем LED
Serial.println("LED OFF. Press 1 to LED ON!"); // и выводим обратно сообщение
}
if(incomingByte == '1') {
digitalWrite(LED, HIGH); // если 0, то включаем LED
Serial.println("LED ON. Press 0 to LED OFF!");
}
}
}
Программа работает очень просто. После запуска или сброса устройства, в последовательный порт выводится сообщение с предложением нажать 1 или 0. В зависимости от нажатой (принятой) цифры светодиод будет загораться или гаснуть.
Теперь, что касается Android. Мы рассмотрим два примера, в первом мы будем передавать данные от Android-устройства к arduino, а во втором примере мы рассмотрим двусторонний обмен данными между устройствами. Второй пример сложнее и в части понимания и по сложности кода, т.к. используются потоки (thread).
Android
Мы будем использовать Java код, с явным указанием MAC-адреса устройства, к которому мы будем подключаться. Т.к. если делать интерфейс обнаружения Bluetooth-устройств, их выбора, подключения к ним и т.д., то код будет очень большой и для некоторых читателей труднопонимаем. Но для тех, кому интересно могут посмотреть стандартный пример Bluetooth Chat.
Узнать MAC-адрес можно к примеру в программе для Android'а: Bluetooth Terminal:
Нас интересует устройство BOLUTEK (наш модуль HC-06, подключенный к Arduino), его MAC адрес: 00:15:FF:F2:19:4C. Его и надо будет в дальнейшем прописать в программе.
Передаем данные в Arduino
Первая программа очень простая, главное окно активити будет содержать 2 кнопки: включить LED и выключить LED. При нажатии на кнопку включения LED, по Bluetooth будет передаваться «1», при нажатии на выключение LED — «0».
В файле манифеста необходимо прописать 2 строки разрешения работы с Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
Сам код главного активити:
package com.example.bluetooth1;
import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;
import com.example.bluetooth1.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String TAG = "bluetooth1";
Button btnOn, btnOff;
private static final int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
// SPP UUID сервиса
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// MAC-адрес Bluetooth модуля
private static String address = "00:15:FF:F2:19:4C";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = (Button) findViewById(R.id.btnOn);
btnOff = (Button) findViewById(R.id.btnOff);
btAdapter = BluetoothAdapter.getDefaultAdapter();
checkBTState();
btnOn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("1");
Toast.makeText(getBaseContext(), "Включаем LED", Toast.LENGTH_SHORT).show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("0");
Toast.makeText(getBaseContext(), "Выключаем LED", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "...onResume - попытка соединения...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Соединяемся...");
try {
btSocket.connect();
Log.d(TAG, "...Соединение установлено и готово к передачи данных...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Создание Socket...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "...In onPause()...");
if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
errorExit("Fatal Error", "In onPause() and failed to flush output stream: " + e.getMessage() + ".");
}
}
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
// Check for Bluetooth support and then check to make sure it is turned on
// Emulator doesn't support Bluetooth and will return null
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth не поддерживается");
} else {
if (btAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth включен...");
} else {
//Prompt user to turn on Bluetooth
Intent enableBtIntent = new Intent(btAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(TAG, "...Посылаем данные: " + message + "...");
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00"))
msg = msg + ".\n\nВ переменной address у вас прописан 00:00:00:00:00:00, вам необходимо прописать реальный MAC-адрес Bluetooth модуля";
msg = msg + ".\n\nПроверьте поддержку SPP UUID: " + MY_UUID.toString() + " на Bluetooth модуле, к которому вы подключаетесь.\n\n";
errorExit("Fatal Error", msg);
}
}
}
Данный код найден на одном из зарубежных блогов и слегка модернизирован. Как видно выше, на кнопки мы вешаем обработчики событий. При нажатии на кнопку передается строка 1 или 0 через sendData() в буфер Bluetooth адаптера. Полный проект с исходными кодами приведен ниже. Для работы программы, необходим Android не ниже версии API15, т.е. 4.0.3 и выше.
Прием и передача данных к Arduino
А вот здесь пришлось повозиться. Дело в том, что в Android'е для приема данных от какого-либо устройства необходимо создавать отдельный фоновый поток, чтобы у нас не зависало основное активити. Для этого мы задействуем thread и все данные будут приниматься в отдельном потоке.
На окно главного активити мы добавим новый элемент TextView, который будет служить для отображения принятых данных от Arduino. Сам java-код главного активити я постарался хорошо прокомментировать, чтобы сделать его удобочитаемым:
package com.example.bluetooth2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import com.example.bluetooth2.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
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;
public class MainActivity extends Activity {
private static final String TAG = "bluetooth2";
Button btnOn, btnOff;
TextView txtArduino;
Handler h;
private static final int REQUEST_ENABLE_BT = 1;
final int RECIEVE_MESSAGE = 1; // Статус для Handler
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder sb = new StringBuilder();
private ConnectedThread mConnectedThread;
// SPP UUID сервиса
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// MAC-адрес Bluetooth модуля
private static String address = "00:15:FF:F2:19:4C";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = (Button) findViewById(R.id.btnOn); // кнопка включения
btnOff = (Button) findViewById(R.id.btnOff); // кнопка выключения
txtArduino = (TextView) findViewById(R.id.txtArduino); // для вывода текста, полученного от Arduino
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case RECIEVE_MESSAGE: // если приняли сообщение в Handler
byte[] readBuf = (byte[]) msg.obj;
String strIncom = new String(readBuf, 0, msg.arg1);
sb.append(strIncom); // формируем строку
int endOfLineIndex = sb.indexOf("\r\n"); // определяем символы конца строки
if (endOfLineIndex > 0) { // если встречаем конец строки,
String sbprint = sb.substring(0, endOfLineIndex); // то извлекаем строку
sb.delete(0, sb.length()); // и очищаем sb
txtArduino.setText("Ответ от Arduino: " + sbprint); // обновляем TextView
btnOff.setEnabled(true);
btnOn.setEnabled(true);
}
//Log.d(TAG, "...Строка:"+ sb.toString() + "Байт:" + msg.arg1 + "...");
break;
}
};
};
btAdapter = BluetoothAdapter.getDefaultAdapter(); // получаем локальный Bluetooth адаптер
checkBTState();
btnOn.setOnClickListener(new OnClickListener() { // определяем обработчик при нажатии на кнопку
public void onClick(View v) {
btnOn.setEnabled(false);
mConnectedThread.write("1"); // Отправляем через Bluetooth цифру 1
//Toast.makeText(getBaseContext(), "Включаем LED", Toast.LENGTH_SHORT).show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
btnOff.setEnabled(false);
mConnectedThread.write("0"); // Отправляем через Bluetooth цифру 0
//Toast.makeText(getBaseContext(), "Выключаем LED", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "...onResume - попытка соединения...");
// Set up a pointer to the remote node using it's address.
BluetoothDevice device = btAdapter.getRemoteDevice(address);
// Two things are needed to make a connection:
// A MAC address, which we got above.
// A Service ID or UUID. In this case we are using the
// UUID for SPP.
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e.getMessage() + ".");
}
// Discovery is resource intensive. Make sure it isn't going on
// when you attempt to connect and pass your message.
btAdapter.cancelDiscovery();
// Establish the connection. This will block until it connects.
Log.d(TAG, "...Соединяемся...");
try {
btSocket.connect();
Log.d(TAG, "...Соединение установлено и готово к передачи данных...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
// Create a data stream so we can talk to server.
Log.d(TAG, "...Создание Socket...");
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "...In onPause()...");
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
// Check for Bluetooth support and then check to make sure it is turned on
// Emulator doesn't support Bluetooth and will return null
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth не поддерживается");
} else {
if (btAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth включен...");
} else {
//Prompt user to turn on Bluetooth
Intent enableBtIntent = new Intent(btAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer); // Получаем кол-во байт и само собщение в байтовый массив "buffer"
h.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget(); // Отправляем в очередь сообщений Handler
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(String message) {
Log.d(TAG, "...Данные для отправки: " + message + "...");
byte[] msgBuffer = message.getBytes();
try {
mmOutStream.write(msgBuffer);
} catch (IOException e) {
Log.d(TAG, "...Ошибка отправки данных: " + e.getMessage() + "...");
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
}
В данном примере для отправки данных мы используем отдельный поток Thread. Тоже самое и для приема данных — метод run(). Также обратите внимание на класс Handler, который служит для организации очереди сообщений и их вывода в главное активити. Дело в том, что в фоновом потоке нельзя напрямую выводить что-либо в главное активити, т.к. это приведет к «крашу» программы.
Класс StringBuilder используется для формирования строки из принятых данных. После, происходит поиск конца строки с символами \r\n, и если они найдены, то строка отображается на активити и обьект sb очищается, чтобы не произошло склейка с последующими принятыми данными.
Скачать исходники:
Dropbox & Github
Комментариев нет:
Отправить комментарий