Saturday, July 18, 2015

Android example to communicate with Bluetooth device, HC-06 Bluetooth Module

The former exercise "Test HC-06 Bluetooth Module with Android BluetoothChat" show how to communicate between Android and HC-06 Bluetooth Module, using "Android BluetoothChat example".

And the post in my another blog "Arduino-er: Connect Arduino Due with HC-06 (Bluetooth Module)", show how to receive data from HC-06, send back to Bluetooth device and PC via USB.

Here, we are going to implement our own Android app (in Android Studio) to connect and communicate to Arduino Due + HC-06 Bluetooth Module. Actually, it is copied (with modified UUID) from my old example "Bluetooth communication between Android devices", AndroidBlueTooth part.


From the video, it can be noted that the first character always missed, or overwritten. We can verify it in Arduino IDE's Serial Monitor, the data received on Arduino Due (via Bluetooth) is correct.


[Updated@2015-10-31: Lost character Fixed, refer "Android communicate with Arduino + HC-06 Bluetooth Module, part II"]

com.example.androidbtcontrol.MainActivity.java
/*
Android Example to connect to and communicate with Bluetooth
In this exercise, the target is a Arduino Due + HC-06 (Bluetooth Module)

Ref:
- Make BlueTooth connection between Android devices
http://android-er.blogspot.com/2014/12/make-bluetooth-connection-between.html
- Bluetooth communication between Android devices
http://android-er.blogspot.com/2014/12/bluetooth-communication-between-android.html
 */
package com.example.androidbtcontrol;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;


public class MainActivity extends ActionBarActivity {

    private static final int REQUEST_ENABLE_BT = 1;

    BluetoothAdapter bluetoothAdapter;

    ArrayList<BluetoothDevice> pairedDeviceArrayList;

    TextView textInfo, textStatus;
    ListView listViewPairedDevice;
    LinearLayout inputPane;
    EditText inputField;
    Button btnSend;

    ArrayAdapter<BluetoothDevice> pairedDeviceAdapter;
    private UUID myUUID;
    private final String UUID_STRING_WELL_KNOWN_SPP =
        "00001101-0000-1000-8000-00805F9B34FB";

    ThreadConnectBTdevice myThreadConnectBTdevice;
    ThreadConnected myThreadConnected;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textInfo = (TextView)findViewById(R.id.info);
        textStatus = (TextView)findViewById(R.id.status);
        listViewPairedDevice = (ListView)findViewById(R.id.pairedlist);

        inputPane = (LinearLayout)findViewById(R.id.inputpane);
        inputField = (EditText)findViewById(R.id.input);
        btnSend = (Button)findViewById(R.id.send);
        btnSend.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                if(myThreadConnected!=null){
                    byte[] bytesToSend = inputField.getText().toString().getBytes();
                    myThreadConnected.write(bytesToSend);
                }
            }});

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)){
            Toast.makeText(this,
                    "FEATURE_BLUETOOTH NOT support",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        //using the well-known SPP UUID
        myUUID = UUID.fromString(UUID_STRING_WELL_KNOWN_SPP);

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Toast.makeText(this,
                    "Bluetooth is not supported on this hardware platform",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        String stInfo = bluetoothAdapter.getName() + "\n" +
                bluetoothAdapter.getAddress();
        textInfo.setText(stInfo);
    }

    @Override
    protected void onStart() {
        super.onStart();

        //Turn ON BlueTooth if it is OFF
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
        }

        setup();
    }

    private void setup() {
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            pairedDeviceArrayList = new ArrayList<BluetoothDevice>();

            for (BluetoothDevice device : pairedDevices) {
                pairedDeviceArrayList.add(device);
            }

            pairedDeviceAdapter = new ArrayAdapter<BluetoothDevice>(this,
                    android.R.layout.simple_list_item_1, pairedDeviceArrayList);
            listViewPairedDevice.setAdapter(pairedDeviceAdapter);

            listViewPairedDevice.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                @Override
                public void onItemClick(AdapterView<?> parent, View view,
                                        int position, long id) {
                    BluetoothDevice device =
                            (BluetoothDevice) parent.getItemAtPosition(position);
                    Toast.makeText(MainActivity.this,
                            "Name: " + device.getName() + "\n"
                                    + "Address: " + device.getAddress() + "\n"
                                    + "BondState: " + device.getBondState() + "\n"
                                    + "BluetoothClass: " + device.getBluetoothClass() + "\n"
                                    + "Class: " + device.getClass(),
                            Toast.LENGTH_LONG).show();

                    textStatus.setText("start ThreadConnectBTdevice");
                    myThreadConnectBTdevice = new ThreadConnectBTdevice(device);
                    myThreadConnectBTdevice.start();
                }
            });
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if(myThreadConnectBTdevice!=null){
            myThreadConnectBTdevice.cancel();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode==REQUEST_ENABLE_BT){
            if(resultCode == Activity.RESULT_OK){
                setup();
            }else{
                Toast.makeText(this,
                        "BlueTooth NOT enabled",
                        Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    //Called in ThreadConnectBTdevice once connect successed
    //to start ThreadConnected
    private void startThreadConnected(BluetoothSocket socket){

        myThreadConnected = new ThreadConnected(socket);
        myThreadConnected.start();
    }

    /*
    ThreadConnectBTdevice:
    Background Thread to handle BlueTooth connecting
    */
    private class ThreadConnectBTdevice extends Thread {

        private BluetoothSocket bluetoothSocket = null;
        private final BluetoothDevice bluetoothDevice;


        private ThreadConnectBTdevice(BluetoothDevice device) {
            bluetoothDevice = device;

            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(myUUID);
                textStatus.setText("bluetoothSocket: \n" + bluetoothSocket);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            boolean success = false;
            try {
                bluetoothSocket.connect();
                success = true;
            } catch (IOException e) {
                e.printStackTrace();

                final String eMessage = e.getMessage();
                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        textStatus.setText("something wrong bluetoothSocket.connect(): \n" + eMessage);
                    }
                });

                try {
                    bluetoothSocket.close();
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }

            if(success){
                //connect successful
                final String msgconnected = "connect successful:\n"
                        + "BluetoothSocket: " + bluetoothSocket + "\n"
                        + "BluetoothDevice: " + bluetoothDevice;

                runOnUiThread(new Runnable(){

                    @Override
                    public void run() {
                        textStatus.setText(msgconnected);

                        listViewPairedDevice.setVisibility(View.GONE);
                        inputPane.setVisibility(View.VISIBLE);
                    }});

                startThreadConnected(bluetoothSocket);
            }else{
                //fail
            }
        }

        public void cancel() {

            Toast.makeText(getApplicationContext(),
                    "close bluetoothSocket",
                    Toast.LENGTH_LONG).show();

            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    /*
    ThreadConnected:
    Background Thread to handle Bluetooth data communication
    after connected
     */
    private class ThreadConnected extends Thread {
        private final BluetoothSocket connectedBluetoothSocket;
        private final InputStream connectedInputStream;
        private final OutputStream connectedOutputStream;

        public ThreadConnected(BluetoothSocket socket) {
            connectedBluetoothSocket = socket;
            InputStream in = null;
            OutputStream out = null;

            try {
                in = socket.getInputStream();
                out = socket.getOutputStream();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            connectedInputStream = in;
            connectedOutputStream = out;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;

            while (true) {
                try {
                    bytes = connectedInputStream.read(buffer);
                    String strReceived = new String(buffer, 0, bytes);
                    final String msgReceived = String.valueOf(bytes) +
                            " bytes received:\n"
                            + strReceived;

                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            textStatus.setText(msgReceived);
                        }});

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();

                    final String msgConnectionLost = "Connection lost:\n"
                            + e.getMessage();
                    runOnUiThread(new Runnable(){

                        @Override
                        public void run() {
                            textStatus.setText(msgConnectionLost);
                        }});
                }
            }
        }

        public void write(byte[] buffer) {
            try {
                connectedOutputStream.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void cancel() {
            try {
                connectedBluetoothSocket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}


layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/info"
        android:textStyle="bold|italic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/status"
        android:textSize="28sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <ListView
        android:id="@+id/pairedlist"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/inputpane"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone">

        <EditText
            android:id="@+id/input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button
            android:id="@+id/send"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Sent"/>

    </LinearLayout>

</LinearLayout>


Have to modify AndroidManifest.xml to uses-permission of "android.permission.BLUETOOTH".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidbtcontrol" >

    <uses-permission android:name="android.permission.BLUETOOTH"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


download filesDownload the files (Android Studio Format).

Next:
Android Bluetooth to control LED brightness on Arduino Due + HC-06

18 comments:

  1. hi,

    i am not able to download files.
    please attach project folder.

    thank you friend.

    ReplyDelete
  2. http://sites.google.com/site/androidexercise6/download/AndroidBTControl_20150718a.zip

    ReplyDelete
  3. Thank you. It works fine until I upgraded phone to Android 5.1.1 :(
    then the bluetoothSocket.connect() always failed. I have tried some other SPP code too, the same problem. Can you help?

    ReplyDelete
  4. Hi... Can you please help us out that why the first character is not getting printed every time i send the message..?..!

    Thank You..

    ReplyDelete
  5. Hello eddie kwan,

    I re-tested on Nexus 7 (2012) running Android 5.1.1, and it work as expected.

    ReplyDelete
  6. Samsung Galaxy s6 throws IOException on connect()

    ReplyDelete
  7. First thank you it works but where is the Arduino code. Please can you share Arduino code of this project
    Thanks

    ReplyDelete
  8. Hi Android Eric,

    I have a question to ask. Why i can only send 2 times of data to arduino? After that two times, then it could not work already. I wil attach my code. Can u help me to verify? Need your help urgently. Thanks in advanced. Hope to hear from you.

    package com.utar.user.trylasttime;

    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothSocket;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.Toast;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.Set;
    import java.util.UUID;

    public class PurifierMenu extends AppCompatActivity {

    //private static String address = "20:15:12:29:62:14";
    private static String address = "98:D3:31:30:61:D9";
    private BluetoothAdapter myBluetooth = BluetoothAdapter.getDefaultAdapter();
    BluetoothDevice myDevice = myBluetooth.getRemoteDevice(address);
    //private BluetoothSocket mySocket = null;
    private OutputStream outstream = null;
    private static final int REQUEST_ENABLE_BT = 1;
    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    connectingBTdevice myThreadConnectBTdevice;
    ConnectedThread myConnectedThread;
    //ThreadConnectBTdevice myThreadConnectBTdevice;
    //ThreadConnected myThreadConnected;
    private static Button onPurifier;
    private static Button offPurifier;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_purifier_menu);
    checkBTstate();
    myThreadConnectBTdevice = new connectingBTdevice(myDevice);
    myThreadConnectBTdevice.start();

    onPurifierListener();
    offPurifierListener();


    }

    public void onPurifierListener(){
    onPurifier = (Button)findViewById(R.id.onPurifier);
    onPurifier.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

    if(myThreadConnectBTdevice!=null) {
    myConnectedThread.write("1");
    Toast msg = Toast.makeText(getBaseContext(),
    "You have clicked On", Toast.LENGTH_SHORT);
    msg.show();
    }

    }
    });
    }
    public void offPurifierListener(){
    offPurifier = (Button)findViewById(R.id.offPurifier);
    offPurifier.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {

    if(myThreadConnectBTdevice!=null) {
    myConnectedThread.write("2");
    Toast msg = Toast.makeText(getBaseContext(),
    "You have clicked Off", Toast.LENGTH_SHORT);
    msg.show();
    }
    }
    });

    }

    ReplyDelete
  9. The code will be continued here.

    private void checkBTstate(){
    if (!myBluetooth.isEnabled()){
    Intent enableBtIntent = new Intent(myBluetooth.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }else {
    Toast.makeText(getApplicationContext(), "Bluetooth had been ON! Please proceed thanks!", Toast.LENGTH_LONG).show();
    }
    }

    private void startConnectedThread(BluetoothSocket socket){

    myConnectedThread = new ConnectedThread(socket);
    myConnectedThread.start();
    }

    private class connectingBTdevice extends Thread{


    private final BluetoothDevice btdevice;
    private final BluetoothSocket btSocket;
    private connectingBTdevice(BluetoothDevice myDevice){

    BluetoothSocket tmp = null;
    btdevice = myDevice;
    try{
    tmp = myDevice.createRfcommSocketToServiceRecord(MY_UUID);
    }catch(IOException e){}
    btSocket = tmp;
    }

    public void run(){

    myBluetooth.cancelDiscovery();

    try{
    btSocket.connect();
    }catch(IOException connectException){
    //Unable to connect; close the socket and get out
    try{
    btSocket.close();
    }catch (IOException closeException){}
    return;
    }

    startConnectedThread(btSocket);


    }

    public void cancel(){
    try{
    btSocket.close();
    }catch(IOException e){}

    }
    }

    private class ConnectedThread extends Thread{
    private final BluetoothSocket btSocket;
    private final InputStream myInStream;
    private final OutputStream myOutStream;

    public ConnectedThread(BluetoothSocket socket){
    btSocket = socket;
    InputStream tmpIn = null;
    OutputStream tmpOut = null;

    try{
    tmpIn = socket.getInputStream();
    tmpOut = socket.getOutputStream();
    }catch (IOException e){}

    myInStream = tmpIn;
    myOutStream = tmpOut;
    }

    public void write(String message){
    byte[] msgBuffer = message.getBytes();
    try{
    myOutStream.write(msgBuffer);
    }catch(IOException e){}
    }

    public void cancel(){
    try{
    btSocket.close();
    }catch(IOException e){}
    }

    }

    }

    ReplyDelete
  10. thank yousom much Eric you safe my life :)

    ReplyDelete
  11. Hey Eric. Great work on this tutorial. It has been so helpful, and I have been using it to help me with a group project. However, the changes I would like to make to the code is that I don't want to send to the Arduino the string that includes the typed text, and the bytes sent. I only want to send just the text without saying the bytes received. What do I edit?

    Please help...

    ReplyDelete
  12. hello MKFearon,

    The "xx bytes received:" is for information only.

    To remove it:
    Modify the code in run() of ThreadConnected class:

    final String msgReceived = String.valueOf(bytes) +
    " bytes received:\n"
    + strReceived;

    to:

    final String msgReceived = strReceived;

    ReplyDelete
  13. Thanks Eric for your quick response. I recognized that I really didn't have deal with that part and just leave it as is. I have edited your code to be a voice to text input for several catch phrases and then send it by 'Send' button via bluetooth to an RN42 Bluetooth module and Arduino Uno. But with my arduino code and app, the Arduino Serial monitor is receiving rubbish, like "yy yy pkpkpkpkjpk", each time i send the text. Below will be the MainActivity code for app showing the send button part, the speech input part, and then after will be the code for the Arduino.

    MainActivity

    btnSpeak.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
    promptSpeechInput();
    }
    });

    btnSend.setOnClickListener(new View.OnClickListener(){

    @Override
    public void onClick(View v) {
    if(myThreadConnected!=null){
    Toast.makeText(MainActivity.this,
    txtSpeechInput.getText().toString(),
    Toast.LENGTH_LONG).show();
    byte[] bytesToSend = txtSpeechInput.getText().toString().getBytes();
    myThreadConnected.write(bytesToSend);
    }
    }});


    /**
    * Receiving speech input
    * */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    ......
    ......
    case REQ_CODE_SPEECH_INPUT: {
    if (resultCode == RESULT_OK && null != data) {

    ArrayList result = data
    .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
    String preset = result.get(0);
    if ((result.get(0).contains("accident")) && (result.get(0).contains("ahead"))){
    preset = "ACCIDENT AHEAD";
    txtSpeechInput.setText(preset);
    }
    else if ((result.get(0).contains("accident")) &&
    (result.get(0).contains("left"))) {
    preset = "ACCIDENT LEFT";
    txtSpeechInput.setText(preset);
    }
    else if ((result.get(0).contains("accident")) &&
    (result.get(0).contains("right"))){
    preset = "ACCIDENT RIGHT";
    txtSpeechInput.setText(preset);
    }
    else if ((result.get(0).contains("animal")) &&
    (result.get(0).contains("crossing"))){
    preset = "ANIMAL CROSSING";
    txtSpeechInput.setText(preset);
    }
    else if ((result.get(0).contains("emergency")) &&
    (result.get(0).contains("vehicle"))){
    preset = "EMERGENCY VEH";
    txtSpeechInput.setText(preset);
    }
    else {
    txtSpeechInput.setText(" ");
    }
    }
    break;
    }

    }
    }

    Arduino Uno Code

    #include
    #define rxPin 2
    #define txPin 3

    SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin);

    void setup()
    {
    pinMode(rxPin, INPUT);
    pinMode(txPin, OUTPUT);
    delay(1000);
    Serial.begin(9600);
    mySerial.begin(9600);

    Serial.write("\nTest Start\n");
    }

    void loop()
    {
    while(mySerial.available())
    {
    char data = mySerial.read();
    Serial.write(data);
    mySerial.write(data);
    }
    }


    Please help....again? :-(

    ReplyDelete
  14. Figured out my problem. It was the arduino baud reading. Changed it from 9600 to 115200. Works fine.

    ReplyDelete
  15. El código funciona muy bien en 4.4.4, api 19

    ReplyDelete
  16. hi i have run the code but its not working means its giving message "Something worng bluetoothSocket.connect(): read failed, socket might closed or timeout,read ret:-1" plase help

    ReplyDelete