Передача данных по Bluetooth между Android и Arduino

Передача данных по Bluetooth между Android и Arduino

В статье Arduino и Bluetooth был рассмотрен один из способов передачи информации между Android-устройством и ПК по Bluetooth-соединению. Там же, в двух словах было упомянуто и Android-устройство, но для принятия и передачи данных использовался Android Bluetooth терминал. Однако, для реальных устройств необходима полноценная программа (не будем же мы управлять тем же роботом из терминала…), написанная для Android’а. В данной статье хотелось бы затронуть тему программного обеспечения для работы с Bluetooth, с применением языка Java и среды разработки Eclipse. Установка и настройка Eclipse хорошо описана в этой статье: Android и Arduino. Программное обеспечение.

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. В зависимости от нажатой (принятой) цифры светодиод будет загораться или гаснуть. В общем программа абсолютно такая же как и в статье: Arduino и Bluetooth.

Теперь, что касается Android. Мы рассмотрим два примера, в первом мы будем передавать данные от Android-устройства к arduino, а во втором примере мы рассмотрим двусторонний обмен данными между устройствами. Второй пример сложнее и в части понимания и по сложности кода, т.к. используются потоки (thread).

Мы будем использовать Java код, с явным указанием MAC-адреса устройства, к которому мы будем подключаться. Т.к. если делать интерфейс обнаружения Bluetooth-устройств, их выбора, подключения к ним и т.д., то код будет очень большой и для некоторых читателей труднопонимаем. Но для тех, кому интересно могут посмотреть стандартный пример Bluetooth Chat.

Узнать MAC-адрес можно к примеру в программе для Android’а: Bluetooth Terminal:

Нас интересует устройство BOLUTEK (наш модуль HC-06, подключенный к Arduino), его MAC адрес: 00:15:FF:F2:19:4C. Его и надо будет в дальнейшем прописать в программе.

Android – передаем данные в 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 и выше.

Android – прием и передача данных к 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 очищается, чтобы не произошло склейка с последующими принятыми данными.

Видео работы:

Скачать скомпилированные файлы для Android: bluetooth1.apk и bluetooth2.apk

Скачать исходники проекта

Автор: taliban


Категория: Arduino
Метки:

Написать коментарий

*
= 3 + 1

Добавить изображение

Последние статьи