This article demonstrates how to communicate with a custom BLE peripheral using an Android device. This application can be adapted to add BLE capabilities to other devices by connecting a nRF capable device to them.

Overview

This is part of a series of articles on the nRF51. The nRF51 is a system-on-chip with a Cortex M0 and a BLE radio chip all in one. This article demonstrates how to communicate to a custom peripheral from Android.

Previous articles:
BLE using nRF51: ARM-GCC Build Environment

How to Communicate with a Custom BLE using an Android App

Requirements

  • Device that has the nRF51
    • Used in article: nRF-Dongle
    • Programmed with the application from the article on custom BLE peripheral
  • Android device capable of BLE
  • Java JDK 7
    • Used in article: 7u79
  • Android Studio
    • Use in article: 1.3.2

Installation

Java JDK 7

Download the JDK for your operating system and follow the on-screen instructions to install.

Android Studio

Download the installer for your operating system and follow the on-screen instructions to install.

 

Create a new project

Open Android Studio and you should be greeted with the following screen.

Select Import an Android code sample. On the next screen select the sample Bluetooth Le Gatt under Connectivity. This project will set us up with a framework to build off of for our application. The sample application has the ability to scan for devices, connect, and display information about services and characteristics.

Name the project what you'd like. I kept it the same name as the default.

Click Finish. The project will now load.

 

Working with a project

The IDE may seem overwhelming at first, it has many features. 

Let's go through a few of the key features that we need to create a custom application based on this example.

File Browser

The file browser has many categories and displays each file in the projects. The manifests are xml files that setup the project and tell the OS what behaviors and features the application will require. The java folder contains the actual code for the application. The res folder contains the resources, such as the graphical layouts, menus, and values used throughout the application. Values can be strings, arrays, colors and much more. They are used throughout the application code instead of hard coding values.

Layout View

The layout view is automatically opened if the layout file is opened. The layout can be modifed directly by changing to the text view by clicking on the bottom in the bottom left. Remember this because it will be much easier to copy the code in the next section to create a layout.

Java View

If you open one of the java files, a standard text editor will open.

 

Creating a layout with buttons

We're going to add two buttons to the application that will allow us to read and write the custom characteristics that were created. To do this, we have to create a new layout file by right clicking the layout folder > New > Layout resource. A new window will open to setup some options, leave them as the defaults. Set the name to "button_control". You should now see a new file called "button_control.xml" under the layout folder.

Click the "Text" button on the bottom left to view the XML file itself. Paste the following code into the file button_control.xml.

 

                    <?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:orientation="vertical" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent"> 

 <Button 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:text="Write Char" 
  android:id="@+id/button2" 
  android:onClick="onClickWrite" /> 

 <Button 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:text="Read Char" 
  android:id="@+id/button" 
  android:onClick="onClickRead" /> 

 <TextView 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:text="@string/no_data" 
  android:id="@+id/data_value" /> 
</LinearLayout>
                  

You should see two buttons on the example phone and some text that says "No data". This text field will be used to display the characteristic data.

 

Add read and write function to BluetoothLeService

In order for the buttons to do anything, we have to modify the BluetoothLeService file to be able to read and write the custom characteristics. Add the following two functions after the function titled getSupportedServices() around line 314. These functions have hard-coded UUID's that were created in the BLE peripheral project.

 

                    public void readCustomCharacteristic() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString("00001110-0000-1000-8000-00805f9b34fb"));
        if(mCustomService == null){
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
        /*get the read characteristic from the service*/
        BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString("00000002-0000-1000-8000-00805f9b34fb"));
        if(mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false){
            Log.w(TAG, "Failed to read characteristic");
        }
    }

    public void writeCustomCharacteristic(int value) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString("00001110-0000-1000-8000-00805f9b34fb"));
        if(mCustomService == null){
            Log.w(TAG, "Custom BLE Service not found");
            return;
        }
        /*get the read characteristic from the service*/
        BluetoothGattCharacteristic mWriteCharacteristic = mCustomService.getCharacteristic(UUID.fromString("00000001-0000-1000-8000-00805f9b34fb"));
        mWriteCharacteristic.setValue(value,android.bluetooth.BluetoothGattCharacteristic.FORMAT_UINT8,0);
        if(mBluetoothGatt.writeCharacteristic(mWriteCharacteristic) == false){
            Log.w(TAG, "Failed to write characteristic");
        }
    }
                  

Add read and write function to DeviceControlActivity

The BluetoothLeService is just the underlying class that abtracts some of the BLE Android functionality. In order to actually do anything from the UI, some modifications have to be done to the file DeviceControlActivity. Add the following two functions to the end of the class just after makeGattUpdateIntentFilter(). These functions just call the two functions that were added to the class from before. The onClickWrite() function writes the value 0xAA to the characteristic, but this could be modified to any value you wish. The read function triggers a read request to the operating system. When the read is complete the data is transfered through an intent which is read from mGattUpdateReceiver with the action ACTION_DATA_AVAILABLE. Intents and actions are a feature of Android and are beyond the scope of this article.
.

 

                    public void onClickWrite(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(0xAA);
        }
    }

    public void onClickRead(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.readCustomCharacteristic();
        }
    }
                  

Modification to DeviceControlActivity

The class as written will try to access some UI features that don't exist in the layout we created. To ensure the application doesn't crash, some lines have to be commented out. We also have to start the layout that we created.


Function mGattUpdateReceiver

Comment out line 109: 
//displayGattServices(mBluetoothLeService.getSupportedGattServices());
 

Function clearUI

Comment out line 151: 
//mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);


Function onCreate

Comment out the UI references. Keep the reference to mDataField. Change the setContentView function to load the button_control layout.

 

                    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.button_control);

        final Intent intent = getIntent();
        mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
        mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

        // Sets up UI references.
        /*
        ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
        mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
        mGattServicesList.setOnChildClickListener(servicesListClickListner);
    mConnectionState = (TextView) findViewById(R.id.connection_state);
    */
        mDataField = (TextView) findViewById(R.id.data_value);

        getActionBar().setTitle(mDeviceName);
        getActionBar().setDisplayHomeAsUpEnabled(true);
        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
    }
                  

Function updateConnectionState

Remove line 235:
//mConnectionState.setText(resourceId);
 

 

Testing the App

First, make sure the peripheral is turned on and advertising. Download the application by connecting your phone to the PC and clicking the green arrow. A window will pop-up to choose the device to run on. If you don't see your device, you may need to install ADB drivers and set your device to USB debuggable. This is different for every device.

The application will open on your phone. It will automatically begin scanning. Select the "Custom BLE" peripheral. Now you can read and write data by pressing the buttons. When you write, you can see the data on the UART console. When you read, you can see data on the phone increasing by a value of 1 every second. The data is displayed as ASCII text and the raw byte values.

Here I pressed the button read char and can see the data values of 0x6A which is 'j' in ASCII.

 

Conclusion

This article demonstrated how the BLE example in Android Studio can be modified for whatever UUID you have. This application can be adapted to add BLE capabilities to other devices by connecting a nRF capable device to them. This could be through GPIO, SPI, UART, or I2C. Future articles will show some examples of doing this. Have fun!

 

  Download Code  


Comments

30 Comments


  • Stuart Allsopp 2016-01-27

    Great little tutorial just what I’ve been looking for. I’m still struggling to understand the UUID protocol.

  • Philipp Henhapl 2016-02-04

    Hello,
    I have a problem with the “write” and “read”-function. I can easily connect to my intel edison but if i want to write something there is no reaction at the application. There is no keyboard as well. Does anybody know how to fix this problem?
    I am working on a Sony Xperia Z1.
    The device i want to commiunicate with is an intel edsion.
    I hope anyone can help me!
    Thank you

  • quique123 2016-04-10

    I’m confused about the read chars button?  It read what you wrote back from vterm-hmsoft module to the android phone?

  • quique123 2016-04-11

    I was able to connect to the module but when I click on either button I get this error in the logcat and nothing in the terminal or the android device screen: com.example.android.bluetoothlegatt W/BluetoothLeService: Custom BLE Service not found.  Prior to this I DO get Connected to GATT server, the right device is listed, services discovered and characteristics as well.

    • quique123 2016-04-11

      Sorry, I realized I didnt replace the UUID strings.  So I used the original BLEGATT app to get the services and characteristics for my module.  After that I replaced them into my android app.  I used the first available service for both read and write functions in the BluetoothServiceLE.java.  I used the first 2 characteristics for the respective first service.  One characteristic for read and the next one for write.  I can read but I cant write anything, I get Failed to Write Characteristic.  Plus I dont get the jjj or 0xAA on the android app screen, I just get the bt device identifier when clicking on read, but nothing except the Failed Write logcat log when I tap on the write button.

  • quique123 2016-04-11

    I wish I could go back and erase my previous posts grin.  I was able to get the write to work bychanging the write characteristic to the second option.  Ok so that works but all I get is this in the logcat:

    04-11 16:55:57.820 15504-15504/com.example.android.bluetoothlegatt D/BluetoothGatt: readCharacteristic() - uuid: 00002a00-0000-1000-8000-00805f9b34fb
    04-11 16:55:57.879 15504-15517/com.example.android.bluetoothlegatt D/BluetoothGatt: onCharacteristicRead() - Device=74:DA:EA:B2:87:AE UUID=00002a00-0000-1000-8000-00805f9b34fb Status=0
    04-11 16:56:15.767 15504-15504/com.example.android.bluetoothlegatt D/BluetoothGatt: writeCharacteristic() - uuid: 00002a02-0000-1000-8000-00805f9b34fb
    04-11 16:56:15.840 15504-15518/com.example.android.bluetoothlegatt D/BluetoothGatt: onCharacteristicWrite() - Device=74:DA:EA:B2:87:AE UUID=00002a02-0000-1000-8000-00805f9b34fb Status=128

    but I dont understand how I can send to the bt module with this.

    Could you help me make the final connection?

  • LimeLiight 2016-04-15

    Hi,
    I have a cc2650STK. Im trying to connect my device but it isnt shown . Help me please

  • beguroto 2016-04-20

    Hello, thank you very much: great tutorial! Could please help me? I have a proble with read function: i can write from android app to ARDUINO but I can not read information sent from ARDUINO to ANDROID APP. I changed the 3 UUID that you wrote inside the GATT SERVICE and teh GATT CHARACTERISTICS:
    mCustomService
    mReadCharacteristic
    mWriteCharacteristic
    I have this message from ANDROID: “Failed to write characteristic”. I do not change nothing inside the class SampleGattAttribute. I follow step by step you tutorial: I do not understand what I wrong.
    Thank you in advance.

    • beguroto 2016-04-20

      I solved the problem adding the below code inside the function readCustomCharacteristic() into the class BluetoothLeService :
            //BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString(“00001110-0000-1000-8000-00805f9b34fb”));
            BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString(UUIDcfg.CLIENT_UUID_PRIMARY_SERVICE));

            if(mCustomService == null){
      Log.w(TAG, "Custom BLE Service not found");
      return;
      }

            //To receive notification in Android is mandatory to set the client characteristic configuration descriptor 0x2902
            BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString(UUIDcfg.CLIENT_UUID_READ_SERVICE));
            UUID uuid = UUID.fromString(“00002902-0000-1000-8000-00805f9b34fb”);
            BluetoothGattDescriptor descriptor = mReadCharacteristic.getDescriptor(uuid);
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
            mBluetoothGatt.writeDescriptor(descriptor);
            //To receive notification in Android is mandatory to set characteristic notification to true
            mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);

            /*get the read characteristic from the service*/
            //BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString(“00000002-0000-1000-8000-00805f9b34fb”));
            mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString(UUIDcfg.CLIENT_UUID_READ_SERVICE));

            if(mBluetoothGatt.readCharacteristic(mReadCharacteristic) == false){
      Log.w(TAG, "Failed to read characteristic");
      }

      Now, running the APP, after click on READ button, I am able to read each notification sent from my arduino sketch.

      I hope this can help.

       

      • Aly El-Kerdany 2016-04-21

        I can read/write, but I don’t understand how to receive notification, could you please explain more clearly how??

  • koulou 2016-05-01

    Hi how we can communicate application bluetooth chat with a BLE custom

  • yufengvac 2016-05-05

    Hello,how can I write a binary file to the Custom BLE?

  • Fábio Verde 2016-07-18

    Hi,
    i run the code and i have no errors but when i try to read or write nothing appens. Seems like the buttons dosent exist.
    How can i fix this? urgent.

    Thanks.

  • Prasad VDV 2016-07-24

    Thanks for the code,

    But I can’t write the characteristics to the device.
    if (mBluetoothGatt.writeCharacteristic(mWriteCharacteristic)) {
    Log.w(TAG, " Write characteristic");
    } else {
    Log.w(TAG, "Failed to write characteristic");
    }
    //I will getting in to else part always….

  • kinka 2016-09-19

    Hi, I’m trying to connect via BLE 4.2 from my android device (android-6.0) to my EDK-NINA-B112 (a Development kit for the NINA-B112, which uses a nRF52832 chip). When I use the code of the tutorial, I get to the “scanning screen”, but I do not find any bluetooth devices at all. If I try using apps on the Google play store, I do however find my bluetooth device, and I can easily connect and send messages from and to the device.

    Is it a problem on the code site not supporting some of these components I just have been describing? Or might it be a problem between the android studio compiling and debugging on my phone?
    I can debug the code, put breakpoints in the scanning function, and it goes into the scanning function as it should, but it does not find any devices. I have also tried just running the app one the phone without debugging ofc.

    • kinka 2016-09-21

      I found the problem. The problem was that the app was not granted permission for “Position finding”. (Which is required for android 6+ devices)

      • Danilo Pazian Paulo 2016-10-06

        How did you do this?

      • Danilo Pazian Paulo 2016-10-06

        I have the same problem!

      • kinka 2016-10-20

        You change this on your mobile device. Go to settings->apps->(APP NAME)->permissions

      • Marco Menino 2016-10-28

        I try your solution @kinka, but is not possible editing permissions in this app

    • Marco Menino 2016-10-28

      I have add this in manifest,  <uses-permission android:name=“android.permission.ACCESS_FINE_LOCATION” >
        <uses-permission android:name=“android.permission.ACCESS_COARSE_LOCATION” >

      and active in definitions.

  • jensj 2016-10-25

    Very nice example!
    I just started to learn to program my Android phone so please be gentle wink
    How do I modify the example, so my phone sends one byte when I press the Write-button, and another when I release it again ?

    Jens

  • Julian Scholl 2016-10-27

    Very nice exmaple. I can connect but i failed to wirte data to my Genuino 101. I revice the Message:
    10-27 14:30:48.070 12369-12369/com.example.android.bluetoothlegatt W/BluetoothLeService: Custom BLE Service not found
    in my log.
    Can somebody help me with this ?

    • Marco Menino 2016-10-28

      Yes i have the same problem in the connection action. I ha ve nRF51-DK by Nordic

  • Julian Scholl 2016-10-27

    Very nice exmaple. I can connect but i failed to wirte data to my Genuino 101. I revice the Message:
    10-27 14:30:48.070 12369-12369/com.example.android.bluetoothlegatt W/BluetoothLeService: Custom BLE Service not found
    in my log.
    Can somebody help me with this ?

    • B.J. Nolletti 2016-11-01

      Did you change the UUIDs in the custom service and characteristic to the UUIDs for your BLE device?

      • PedroDa 2016-11-29

        Where I Have to change that? I can’t find out where it is

  • manjunath21994 2016-11-24

    i have followed the same procedures but im getting this error after connecting to my ble modudle java.lang.RuntimeException: Error receiving broadcast Intent { act=com.example.bluetooth.le.ACTION_GATT_CONNECTED flg=0x10 }. can anyone suggest me the solution.

  • saiprem 2016-11-28

    Thanks for the help.
    I am able to read some characteristics but some of them I am not able to.
    I am working with TI cc2541 development kit.
    Can anyone tell me why am I not able to access them.