Click here to Skip to main content
15,867,308 members
Articles / Internet of Things
Article

Java for Bluetooth LE applications

18 Mar 2016CPOL4 min read 32.8K   2  
This document is a guide for creating Java IoT applications that can access remote Bluetooth Low Energy devices on IoT platforms, such as the Intel® Edison development board.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.

Overview

This document is a guide for creating Java* IoT applications that can access remote Bluetooth* Low Energy devices on IoT platforms, such as the Intel® Edison development board. Support for developing these applications is provided as part of the Intel® IoT Developer Kit, using the open source TinyB project. TinyB exposes simple C++ and Java APIs for using Bluetooth LE devices. This guide will only cover developing applications using the Java API in TinyB on the Intel® Edison development board.

Compatibility and requirements

The current Bluetooth API in TinyB has been tested with the Java 8 runtime environment (OpenJDK 8). This environment as well as TinyB are provided as part of the official Intel® IoT Developer Kit image builds for Intel® Edison boards.

On other Linux-based systems, TinyB can be used as long as BlueZ* version 5.37 or newer is installed and the bluetoothd daemon has been started with experimental features enabled (-E flag). More details can be found in the online README file.

In this guide, the TinyB application uses a Texas Instruments Sensor Tag as a Bluetooth LE device.

Documentation and application examples

The documentation for the Bluetooth LE API exposed by TinyB can be found online at the following locations:

The HelloTinyB (or hellotinyb for C++) example uses a Texas Instruments Sensor Tag, from which it reads the ambient and object temperature. The application requires the MAC address of the Sensor Tag as a first parameter to the program (XX:XX:XX:XX:XX:XX in the following example).

./examples/hellotinyb <code>XX:XX:XX:XX:XX:XX

java -cp examples/java/HelloTinyB.jar:/usr/lib/java/tinyb.jar HelloTinyB XX:XX:XX:XX:XX:XX

Writing a Bluetooth LE Java IoT application

We will use the HelloTinyB Java sample found in the TinyB repository as an example showing how to write a program that reads data from a GATT Service over Bluetooth LE. A wiki entry describing the Texas Instruments Sensor Tag device can be found here: http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide.

To start looking at the device, we first must initialize the TinyB library. A BluetoothManager object provides an entry point for using Bluetooth devices. There can be only one BluetoothManager at one time, and the reference to it is obtained through the getBluetoothManager() method.

BluetoothManager manager = BluetoothManager.getBluetoothManager();

The manager will try to initialize a BluetoothAdapter if any Bluetooth adapter is present in the system. To initialize discovery we can call startDiscovery(), which will put the default adapter in discovery mode.

boolean discoveryStarted = manager.startDiscovery();

We should expect to see the following output:

The discovery started: true
Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false 

After discovery is started, new devices will be detected. We can get a list of all devices through the manager's getDevices() method. We can look through the list of devices to find the device with the MAC address that we provided as a parameter. We continue looking until we find it, or until we have tried 15 times without success (about 1 minute).

static BluetoothDevice getDevice(String address) throws InterruptedException  {
    BluetoothManager manager = BluetoothManager.getBluetoothManager();
    BluetoothDevice sensor = null;
    for (int i = 0; (i < 15) && running; ++i) {
        List<BluetoothDevice> list = manager.getDevices();
         for (BluetoothDevice device : list) {
            printDevice(device);
            /*
             * Here we check if the address matches.
             */
            if (device.getAddress().equals(address))
                sensor = device;
        }
        if (sensor != null) {
            return sensor;
        }
        Thread.sleep(4000);
    }
    return null;
}

Afterwards, we can run the connect method on the returned device. The output should be as follows:

Found device: Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false 
Sensor with the provided address connected

Our device should expose a temperature service, which has a UUID we can find out from the data sheet. The service description of the SensorTag can be found here: http://processors.wiki.ti.com/images/a/a8/BLE_SensorTag_GATT_Server.pdf. The service we are looking for has the short UUID AA00, which we insert into the TI Base UUID instead of the XXXX: f000XXXX-0451-4000-b000-000000000000.

static BluetoothGattService getService(BluetoothDevice device, String 
 UUID) throws InterruptedException {
    System.out.println("Services exposed by device:");
    BluetoothGattService tempService = null;
    List<BluetoothGattService> bluetoothServices = null;
    do {
        bluetoothServices = device.getServices();
        for (BluetoothGattService service : bluetoothServices) {
            System.out.println("UUID: " + service.getUuid());
            if (service.getUuid().equals(UUID))
                tempService = service;
        }
        Thread.sleep(4000);
    } while (bluetoothServices != null && bluetoothServices.isEmpty() && running);
    return tempService;
}

The code above should produce the following output:

Services exposed by device:
UUID: f000aa64-0451-4000-b000-000000000000
UUID: 0000180a-0000-1000-8000-00805f9b34fb
UUID: f000ccc0-0451-4000-b000-000000000000
UUID: f000ac00-0451-4000-b000-000000000000
...
Found service f000aa00-0451-4000-b000-000000000000

First of all, we should obtain the characteristics of this service. There are three of them: the value (UUID AA01), configuration (AA02), and period (AA03). We can obtain them using the following code:

static BluetoothGattCharacteristic getCharacteristic(BluetoothGattService service, String UUID) {
    List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
    for (BluetoothGattCharacteristic characteristic : characteristics) {
        if (characteristic.getUuid().equals(UUID))
            return characteristic;
    }
    return null;
}
BluetoothGattCharacteristic tempValue = getCharacteristic(tempService, "f000aa01-0451-4000-b000-000000000000");
BluetoothGattCharacteristic tempConfig = getCharacteristic(tempService, "f000aa02-0451-4000-b000-000000000000");
BluetoothGattCharacteristic tempPeriod = getCharacteristic(tempService, "f000aa03-0451-4000-b000-000000000000");

We need to turn on the Temperature Service by writing 1 in the configuration characteristic, as mentioned in the PDF above. We could also modify the update interval by writing in the period characteristic, but the default value of 1s is good enough for our purposes.

byte[] config = { 0x01  };
tempConfig.writeValue(config);

After this configuration, we should be able to read the temperature from the device. The temperature service returns the data in an encoded format, which can be found in the wiki entry for the Sensor Tag device. Convert the raw temperature format to Celsius and print it. Conversion for object temperature depends on ambient temperature according to the wiki, but we will assume the result without conversion is good enough for our purposes.

while (running) {
    byte[] tempRaw = tempValue.readValue();
    System.out.print("Temp raw = {");
    for (byte b : tempRaw) {
        System.out.print(String.format("%02x,", b));
    }
    System.out.print("}");
        int objectTempRaw = tempRaw[0] + (tempRaw[1] << 8);
    int ambientTempRaw = tempRaw[2] + (tempRaw[3] << 8);
        float objectTempCelsius = convertCelsius(objectTempRaw);
    float ambientTempCelsius = convertCelsius(ambientTempRaw);

    System.out.println(String.format(" Temp: Object = %fC, Ambient = %fC", objectTempCelsius, ambientTempCelsius));
    Thread.sleep(1000);
}

Running this loop will print the temperature values collected from the Bluetooth LE sensor:

Temp raw = {10,0b,c8,0d,} Temp: Object = 22.125000C, Ambient = 25.562500C
Temp raw = {10,0b,c8,0d,} Temp: Object = 22.125000C, Ambient = 25.562500C
Temp raw = {04,0b,cc,0d,} Temp: Object = 22.031250C, Ambient = 25.593750C
...
Temp raw = {34,0b,cc,0d,} Temp: Object = 22.406250C, Ambient = 25.593750C

Known limitations

The API used in this example is based on TinyB v0.3, which only supports polling, but v0.4 will introduce a simplified API for discovering devices and services.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
You may know us for our processors. But we do so much more. Intel invents at the boundaries of technology to make amazing experiences possible for business and society, and for every person on Earth.

Harnessing the capability of the cloud, the ubiquity of the Internet of Things, the latest advances in memory and programmable solutions, and the promise of always-on 5G connectivity, Intel is disrupting industries and solving global challenges. Leading on policy, diversity, inclusion, education and sustainability, we create value for our stockholders, customers and society.
This is a Organisation

42 members

Comments and Discussions

 
-- There are no messages in this forum --