Overview

This section provides an example of how to develop a simple bundle that discovers and connects to a Smart device (BLE), retrieves data from it, and publishes the results to the cloud. This example uses the TI SensorTag based on CC2541 or CC2650. For more information about this device, refer to http://www.ti.com/tool/cc2541dk-sensor and http://www.ti.com/ww/en/wireless_connectivity/sensortag2015/index.html.

You will learn how to perform the following functions:

  • Prepare the embedded device to communicate with a Smart device

  • Develop a bundle retrieves data from the device

  • Optionally publish the data in the cloud

Prerequisites

This tutorial uses a Raspberry Pi Type B with a LMTechnologies LM506 Bluetooth 4.0 http://lm-technologies.com/wireless-adapters/lm506-class-1-bluetooth-4-0-usb-adapter/ dongle.

Prepare the Embedded Device

In order to communicate with Smart devices, the bluez package must be installed on the embedded device. To do so, make sure you have the necessary libraries on the Raspberry Pi and proceed as follows:

NOTE: Depending on the size of your display window, you may need to scroll across to the right in order to view the commands and code samples in their entirety.

sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev

Next, download and uncompress the package:

sudo wget https://www.kernel.org/pub/linux/bluetooth/bluez-4.101.tar.xz
sudo tar xvf bluez-4.101.tar

Change to the bluez folder; configure and install the package:

cd bluez-4.101
sudo ./configure --disable-systemd
sudo make
sudo make install

Finally, change the location of the hciconfig command:

sudo mv /usr/local/sbin/hciconfig /usr/sbin

SensorTag Communication via Command Line

Once configured, you can scan and connect with a Smart device. A TI SensorTag is used in the example that follows.

Plug in the Bluetooth dongle if needed and verify that the interface is up:

sudo hciconfig hci0

If the interface is down, enable it with the following command:

sudo hciconfig hci0 up

Perform a BLE scan with hcitool (this process may be interrupted with ctrl-c):

sudo hcitool lescan
LE Scan ...
BC:6A:29:AE:CC:96 (unknown)
BC:6A:29:AE:CC:96 SensorTag

If the SensorTag is not listed, press the button on the left side of the device to make it discoverable. Interactive communication with the device is possible using the gatttool:

sudo gatttool -b BC:6A:29:AE:CC:96 -I
[   ][BC:6A:29:AE:CC:96][LE]> connect
[CON][BC:6A:29:AE:CC:96][LE]>

In order to read the sensor values from the SensorTag, you need to write some registers on the device. For details, please refer to the CC2541 user guide: http://processors.wiki.ti.com/index.php/SensorTag_User_Guide. However, note that the reported BLE handles are not up-to-date on this page. Also refer to this updated attribute table: http://processors.wiki.ti.com/images/archive/a/a8/20130111154127!BLE_SensorTag_GATT_Server.pdf.

The example that follows shows the procedure for retrieving the temperature value from the SensorTag.

Once connected with gatttool, the IR temperature sensor is enabled to write the value 01 to the handle 0x0029:

[CON][BC:6A:29:AE:CC:96][LE]> char-write-cmd 0x0029 01

Next, the temperature value is read from the 0x0025 handle:

[CON][BC:6A:29:AE:CC:96][LE]> char-read-hnd 0x0025
[CON][BC:6A:29:AE:CC:96][LE]>
Characteristic value/descriptor: a7 fe 2c 0d

In accordance with the documentation, the retrieved raw values have to be refined in order to obtain the ambient and object temperature.

Enable notifications writing the value 0001 to the 0x0026 register:

[CON][BC:6A:29:AE:CC:96][LE]> char-write-cmd 0x0026 0100
[CON][BC:6A:29:AE:CC:96][LE]>
Notification handle = 0x0025 value: a5 fe 3c 0d
[CON][BC:6A:29:AE:CC:96][LE]>
Notification handle = 0x0025 value: 9f fe 3c 0d
[CON][BC:6A:29:AE:CC:96][LE]>
Notification handle = 0x0025 value: 9a fe 3c 0d

Stop the notifications by writing 0000 to the same register:

[CON][BC:6A:29:AE:CC:96][LE]>
Notification handle = 0x0025 value: 9e fe 3c 0d
[CON][BC:6A:29:AE:CC:96][LE]> char-write-cmd 0x0026 000
Notification handle = 0x0025 value: a3 fe 3c 0d
[CON][BC:6A:29:AE:CC:96][LE]>

BLE Bundle for TI SensorTag

The BLE bundle performs the following operations:

  • Starts a scan for smart devices (lescan)

  • Selects all the TI SensorTag in range

  • Connects to the discovered SensorTags and discovers their capabilities

  • Reads data from all the sensors onboard and writes the values in the log file

Develop the BLE Bundle

Once the required packages are installed and communication with the SensorTag via command line is established, you may start to develop the BLE bundle as follows:

NOTE: For more detailed information about bundle development (i.e., the plug-in project, classes, and MANIFEST file configuration), please refer to the Hello World Example located here.

  • Create a Plug-in Project named org.eclipse.kura.example.ble.tisensortag.

  • Create the following classes: BluetoothLe, TiSensorTag, and TiSensorTagGatt.
  • Include the following bundles in the MANIFEST.MF:
    • org.eclipse.kura.bluetooth
    • org.eclipse.kura.configuration
    • org.eclipse.kura.message
    • org.osgi.service.component
    • org.slf4j

The following files need to be implemented in order to write the source code:

OSGI-INF/metatype/com.eurotech.example.ble.tisensortag.BluetoothLe.xml File

The OSGI-INF/metatype/com.eurotech.example.ble.tisensortag.BluetoothLe.xml file describes the parameters for this bundle including the following:

  • cc2650 - defines the type of TI Sensor Tag; CC2650 is selected if the parameter is true.

  • scan_time - specifies the length of time to scan for devices in seconds.

  • period - specifies the time interval in seconds between two publishes.

  • publishTopic - supplies the topic to publish data to the cloud.

com.eurotech.example.ble.tisensortag.BluetoothLe.java File

The com.eurotech.example.ble.tisensortag.BluetoothLe.java file contains the activate and deactivate methods for this bundle. The activate method gets the BluetoothAdapter and defines a ScheduledExecutorService, which schedules the execution of the updateSensors method every second. The following code sample shows part of the activate method:

try {
	m_cloudClient = m_cloudService.newCloudClient(APP_ID);
	m_cloudClient.addCloudClientListener(this);

	// Get Bluetooth adapter and ensure it is enabled
	m_bluetoothAdapter = m_bluetoothService.getBluetoothAdapter();
	if (m_bluetoothAdapter != null) {
		s_logger.info("Bluetooth adapter address => " + m_bluetoothAdapter.getAddress());
		s_logger.info("Bluetooth adapter le enabled => " + m_bluetoothAdapter.isLeReady());

		if (!m_bluetoothAdapter.isEnabled()) {
			s_logger.info("Enabling bluetooth adapter...");
			m_bluetoothAdapter.enable();
			s_logger.info("Bluetooth adapter address => " + m_bluetoothAdapter.getAddress());
		}
		m_workerCount = 0;
		m_found = false;
		m_connected = false;
		m_startTime = 0;
		m_handle = m_worker.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				updateSensors();
			}
		}, 0, 1, TimeUnit.SECONDS);
	}
	else s_logger.warn("No Bluetooth adapter found ...");
} catch (Exception e) {
	s_logger.error("Error starting component", e);
	throw new ComponentException(e);
}

The updateSensors code starts and stops the scanning procedure. If a SensorTag is detected, the bundle tries to connect to it and reads the temperature from the on-board sensor. The updateSensors code is shown below.

void updateSensors() {

	// Scan
	if(!m_found){
		if(m_bluetoothAdapter.isScanning()){
			s_logger.debug("m_bluetoothAdapter.isScanning");
			if((System.currentTimeMillis() - m_startTime) >= (m_scantime*1000)){
				m_bluetoothAdapter.killLeScan();
			}
		}
		else{
			s_logger.info("startLeScan");
			m_bluetoothAdapter.startLeScan(this);
			m_startTime = System.currentTimeMillis();
		}
	}
	else if(m_bluetoothAdapter.isScanning()){
		m_bluetoothAdapter.killLeScan();
	}

	// connect SensorTag
	if(m_found){
		if(!m_connected){
			s_logger.info("Found, connecting...");
			m_connected = myTiSensorTag.connect();
			if(m_connected){
				myTiSensorTag.enableTemperatureSensor(m_cc2650);
				myTiSensorTag.enableTemperatureNotifications(m_cc2650);
			}
			else {
				s_logger.info("Cannot connect to TI SensorTag " + myTiSensorTag.getBluetoothDevice().getAdress() + ".");
			}
		}

		// Temperature
		if(myTiSensorTag.isTemperatureReceived()){
			myTiSensorTag.setTemperatureReceived(false);
		}
	}

}

com.eurotech.example.ble.sensortag.TiSensorTag.java File

The com.eurotech.example.ble.sensortag.TiSensorTag.java file is used to connect and disconnect to the SensorTag. It also contains the methods to configure and read data from the sensor. The connection method uses the BluetoothGatt Service as shown below:

public boolean connect() {
    m_bluetoothGatt = m_device.getBluetoothGatt();
    boolean connected = m_bluetoothGatt.connect();
    if(connected) {
        m_bluetoothGatt.setBluetoothLeNotificationListener(this);
        m_connected = true;
        return true;
    }
    else {
       	// If connect command is not executed, close gatttool
       	m_bluetoothGatt.disconnect();
      	m_connected = false;
        return false;
    }
}

A set of methods for reading from and writing to the internal register of the device are included in the file. The following code sample presents the methods to manage the temperature sensor.

// ---------------------------------------------------------------------------------------------
//
//  Temperature Sensor, reference: http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
//
// ---------------------------------------------------------------------------------------------
/*
 * Enable temperature sensor
 */
public void enableTemperatureSensor(boolean cc2650) {
	// Write "01" to 0x29 to enable temperature sensor
	if(cc2650)
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_ENABLE_2650, "01");
	else
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_ENABLE_2541, "01");
}

/*
 * Disable temperature sensor
 */
public void disableTemperatureSensor(boolean cc2650) {
	// Write "00" to 0x29 to enable temperature sensor
	if(cc2650)
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_ENABLE_2650, "00");
	else
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_ENABLE_2541, "00");
}

/*
 * Read temperature sensor
 */
public String readTemperature(String handleValue) {
	// Read value from handle 0x25
	return m_bluetoothGatt.readCharacteristicValue(handleValue);
}

/*
 * Read temperature sensor by UUID
 */
public String readTemperatureByUuid(UUID uuid) {
	return m_bluetoothGatt.readCharacteristicValueByUuid(uuid);
}

/*
 * Enable temperature notifications
 */
public void enableTemperatureNotifications(boolean cc2650) {
	//Write "01:00 to 0x26 to enable notifications
	if(cc2650)
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_NOTIFICATION_2650, "01:00");
	else
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_NOTIFICATION_2541, "01:00");
}
/*
 * Disable temperature notifications
 */
public void disableTemperatureNotifications(boolean cc2650) {
	//Write "00:00 to 0x26 to enable notifications
	if(cc2650)
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_NOTIFICATION_2650, "00:00");
	else
		m_bluetoothGatt.writeCharacteristicValue(TiSensorTagGatt.HANDLE_TEMP_SENSOR_NOTIFICATION_2541, "00:00");
}

/*
 * Calculate temperature
 */
private double calculateTemperature(double obj, double amb) {
	double Vobj2 = obj;
    Vobj2 *= 0.00000015625;

    double Tdie = (amb / 128.0) + 273.15;

    double S0 = 5.593E-14;	// Calibration factor
    double a1 = 1.75E-3;
    double a2 = -1.678E-5;
    double b0 = -2.94E-5;
    double b1 = -5.7E-7;
    double b2 = 4.63E-9;
    double c2 = 13.4;
    double Tref = 298.15;
    double S = S0*(1+a1*(Tdie - Tref)+a2*Math.pow((Tdie - Tref),2));
    double Vos = b0 + b1*(Tdie - Tref) + b2*Math.pow((Tdie - Tref),2);
    double fObj = (Vobj2 - Vos) + c2*Math.pow((Vobj2 - Vos),2);
    double tObj = Math.pow(Math.pow(Tdie,4) + (fObj/S),.25);

    return tObj - 273.15;
}

Deploy and Validate the Bundle

In order to proceed, you need to know the IP address of your embedded gateway that is on the remote target unit. Once you do, follow the mToolkit instructions for installing a single bundle to the remote target unit. When this installation completes, the bundle starts automatically. You should see a message similar to the one below from /var/log/esf.log indicating that the bundle was successfully installed and configured, and started to search for TI SensorTags.

2015-06-05 14:54:29,978 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.e.b.t.BluetoothLe - Activating BluetoothLe example...
2015-06-05 14:54:30,551 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.e.b.t.BluetoothLe - Bluetooth adapter address => null
2015-06-05 14:54:30,554 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.e.b.t.BluetoothLe - Bluetooth adapter le enabled => false
2015-06-05 14:54:30,576 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.e.b.t.BluetoothLe - Enabling bluetooth adapter...
2015-06-05 14:54:31,587 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.e.b.t.BluetoothLe - Bluetooth adapter address => 5C:F3:70:60:63:9E
2015-06-05 14:54:31,685 [pool-21-thread-1] INFO  o.e.k.e.b.t.BluetoothLe - startLeScan
2015-06-05 14:54:31,693 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.c.c.ConfigurableComponentTracker - Adding ConfigurableComponent org.eclipse.kura.example.ble.tisensortag.BluetoothLe
2015-06-05 14:54:31,700 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.c.c.ConfigurationServiceImpl - Registration of ConfigurableComponent org.eclipse.kura.example.ble.tisensortag.BluetoothLe by org.eclipse.kura.core.configuration.ConfigurationServiceImpl@1075fdf...
2015-06-05 14:54:31,738 [pool-21-thread-1] INFO  o.e.k.l.b.l.BluetoothLeScanner - Starting bluetooth le scan...
2015-06-05 14:54:31,757 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.c.c.ConfigurationServiceImpl - Registering org.eclipse.kura.example.ble.tisensortag.BluetoothLe with ocd: org.eclipse.kura.core.configuration.metatype.Tocd@17e09e5 ...
2015-06-05 14:54:31,761 [Component Resolve Thread (Bundle 6)] INFO  o.e.k.c.c.ConfigurationServiceImpl - Registration Completed for Component org.eclipse.kura.example.ble.tisensortag.BluetoothLe.
2015-06-05 14:54:52,680 [pool-21-thread-1] INFO  o.e.k.l.b.l.BluetoothLeScanner - Killing hcitool...
2015-06-05 14:55:36,814 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.u.BluetoothProcess - EEE LE Scan ...
BC:6A:29:AC:4E:23 (unknown)
BC:6A:29:AC:4E:23 SensorTag

2015-06-05 14:55:36,820 [pool-21-thread-1] INFO  o.e.k.l.b.u.BluetoothProcess - Closing streams and killing...
2015-06-05 14:55:36,827 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothLeScanner - LE Scan ...
2015-06-05 14:55:36,873 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothLeScanner - BC:6A:29:AC:4E:23 (unknown)
2015-06-05 14:55:36,888 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothLeScanner - BC:6A:29:AC:4E:23 SensorTag
2015-06-05 14:55:36,909 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothLeScanner - m_scanResult.add BC:6A:29:AC:4E:23 - SensorTag
2015-06-05 14:55:36,928 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.e.b.t.BluetoothLe - Address BC:6A:29:AC:4E:23 Name SensorTag
2015-06-05 14:55:36,931 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.e.b.t.BluetoothLe - TI SensorTag BC:6A:29:AC:4E:23 found.
2015-06-05 14:55:37,680 [pool-21-thread-1] INFO  o.e.k.e.b.t.BluetoothLe - Found, connecting...
2015-06-05 14:55:37,826 [pool-21-thread-1] INFO  o.e.k.l.b.l.BluetoothGattImpl - Sending connect message...
2015-06-05 14:55:38,425 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothGattImpl - Receiving notification: Notification handle = 0x0025 value: ab fe 70 0c

2015-06-05 14:55:38,458 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.e.b.t.TiSensorTag - Received temp value: Ambient: 24.875 Target: 20.745652488725113
2015-06-05 14:55:39,369 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothGattImpl - Receiving notification: Notification handle = 0x0025 value: b5 fe 74 0c

2015-06-05 14:55:39,373 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.e.b.t.TiSensorTag - Received temp value: Ambient: 24.90625 Target: 21.056125826758375
2015-06-05 14:55:40,367 [BluetoothProcess Input Stream Gobbler] INFO  o.e.k.l.b.l.BluetoothGattImpl - Receiving notification: Notification handle = 0x0025 value: ab fe 74 0c

Cloud-enabled BLE Bundle

Optionally, the bundle may be modified to publish the retrieved sensor values to the cloud. This part of the tutorial requires an active Everyware Cloud account.

In this section, you can enable the application bundle to communicate with the Everyware Cloud. This capability requires some modifications to the ESF bundle application. With this feature, instead of writing the sensor values on the log file, ESF publishes a message containing the values to the Everyware Cloud using the CloudClient service. Each sensor value is published on a specific topic containing the sensor name (temperature, accelerometer, humidity, etc.).

Before continuing, make sure your embedded device is already connected to the Everyware Cloud.

Modify the Code

To publish the received sensor values to the cloud, you need to modify the file com.eurotech.example.ble.sensortag.BluetoothLe.java. In the case of temperature sensor, the updateSensors will run the doPublishTemp method as shown below.

void updateSensors() {

	// Scan
	if(!m_found){
		if(m_bluetoothAdapter.isScanning()){
			s_logger.debug("m_bluetoothAdapter.isScanning");
			if((System.currentTimeMillis() - m_startTime) >= (m_scantime*1000)){
				m_bluetoothAdapter.killLeScan();
			}
		}
		else{
			s_logger.info("startLeScan");
			m_bluetoothAdapter.startLeScan(this);
			m_startTime = System.currentTimeMillis();
		}
	}
	else if(m_bluetoothAdapter.isScanning()){
		m_bluetoothAdapter.killLeScan();
	}

	// connect SensorTag
	if(m_found){
		if(!m_connected){
			s_logger.info("Found, connecting...");
			m_connected = myTiSensorTag.connect();
			if(m_connected){
				myTiSensorTag.enableTemperatureSensor(m_cc2650);
				myTiSensorTag.enableTemperatureNotifications(m_cc2650);
			}
			else {
				s_logger.info("Cannot connect to TI SensorTag " + myTiSensorTag.getBluetoothDevice().getAdress() + ".");
			}
		}

		// Temperature
		if(myTiSensorTag.isTemperatureReceived()){
			if(m_workerCount==m_pubrate)
				doPublishTemp(myTiSensorTag.getBluetoothDevice().getAdress(), myTiSensorTag.getTempAmbient(), myTiSensorTag.getTempTarget());
			myTiSensorTag.setTemperatureReceived(false);
		}
	}

	m_workerCount++;
	if(m_workerCount>m_pubrate){
		m_workerCount=0;
	}
}

The following doPublishTemp method creates a KuraPayload object and uses the CloudClient service to publish it on a specific topic:

private void doPublishTemp(String address, Object ambValue, Object targetValue) {
	if(m_topic!=null){
		KuraPayload payload = new KuraPayload();
		payload.setTimestamp(new Date());
		payload.addMetric("Ambient", ambValue);
		payload.addMetric("Target", targetValue);
		try {
			m_cloudClient.publish(m_topic+"/"+address + "/temperature", payload, 0, false);
		} catch (Exception e) {
			s_logger.error("Can't publish message, " + "temperature", e);
		}
	}
}

Once the code is updated, the bundle has to be redeployed. As with the previous version of the code, the BLE bundle searches for a SensorTag and once connected, it writes the sensor values in the log file. In addition, the bundle publishes a message for every sensor value received. You can see them in the console of your account in the Everyware Cloud.