[Перевод] Работа с Bluetooth LE из Java-приложений

Сегодня расскажем о том, как, пользуясь Java, создавать приложения для IoT, которые могут работать с удалёнными Bluetooth Low Energy-устройствами. Разработку таких приложений, благодаря проекту с открытым исходным кодом TinyB, поддерживает Intel IoT Development Kit. TinyB предоставляет разработчику простые API для C++ и Java, которые позволяют работать с BLE-устройствами. Здесь рассмотрим API TinyB для Java, а эксперименты будем проводить на Intel Edison.
375720baaef744dfb4dccf4c4d388653.jpg


О совместимости и предварительных требованиях


Текущая версия Bluetooth API в TinyB протестирована в среде исполнения Java 8 (OpenJDK 8). Это окружение, как и TinyB, поставляется как часть официальной сборки образа Intel IoT Development Kit для плат Intel Edison.

На Linux-системах TinyB можно использовать при установленном BlueZ (версия 5.37. или выше). При этом демон bluetoothd запускается с включенными экспериментальными функциями (флаг –E). Подробнее об этом можете почитать в файле README. В нашем примере в роли Bluetooth LE-устройства выступает SensorTag от Texas Instruments. Подробности об устройстве можно найти здесь.

Документация и примеры приложений


Вот два варианта документации для Bluetooth API которое предоставляет TinyB. Здесь — материалы для тех, кто пользуется C++, а здесь — для Java-разработчиков.

В примере HelloTinyB, который написан на Java (или в hellotinyb для C++) используется вышеупомянутый SensorTag. С него осуществляется считывание показателей об окружающей температуре и температуре объекта. Для правильной работы приложению требуется MAC-адрес SensorTag. Его надо передать в качестве первого параметра при запуске программы:

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

IoT-приложение на Java для работы с Bluetooth LE


Здесь мы воспользуемся примером HelloTinyB, который написан на Java. Его можно найти в репозитории TinyB. Мы покажем, как написать приложение, которое читает данные из службы GATT через Bluetooth LE.

Для того, чтобы начать работать с SensorTag, нужно инициализировать библиотеку TinyB. Объект BluetoothManager предоставляет точку входа для использования Bluetooth-устройств. В программе может быть лишь один экземпляр этого объекта-менеджера. Ссылку на него можно получить с помощью метода getBluetoothManager ():

BluetoothManager manager = BluetoothManager.getBluetoothManager();

Менеджер попытается инициализировать объект BluetoothAdapter если в системе присутствует Bluetooth-адаптер. Для того, чтобы запустить поиск других устройств, нужно вызвать метод startDiscovery (), который переведёт адаптер, используемый по умолчанию, в режим поиска:
boolean discoveryStarted = manager.startDiscovery();

После выполнения этой команды можно ждать следующего отладочного вывода:
The discovery started: true
Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false

После запуска поиска обнаружено новое устройство. Список всех найденных устройств можно получить с помощью метода менеджера getDevices (). Этот список нужно просмотреть для того, чтобы найти устройство с MAC-адресом, заданным в качестве параметра при запуске программы. Поиск продолжается либо до тех пор, пока нужное устройство не будет найдено, либо — пока не будет предпринято 15 попыток его обнаружения (занимает это около минуты).
static BluetoothDevice getDevice(String address) throws InterruptedException {
	BluetoothManager manager = BluetoothManager.getBluetoothManager();
	BluetoothDevice sensor = null;
	for (int i = 0; (i < 15) && running; ++i) {
		List list = manager.getDevices();
		for (BluetoothDevice device : list) {
			printDevice(device);
			/*
			* Здесь проверяем, совпадает ли адрес с заданным.
			*/
			if (device.getAddress().equals(address))
			sensor = device;
		}
		if (sensor != null) {
			return sensor;
		}
		Thread.sleep(4000);
	}
	return null;
}

После того, как нужное Bluetooth-устройство обнаружено, можно запустить процесс подключения к нему с помощью метода connect объекта, который ему соответствует. Вот как должен выглядеть тестовый вывод на данном этапе работы:
Found device: Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false
Sensor with the provided address connected

Устройство, к которому мы подключаемся, должно давать доступ к службе определения температуры, UUID которой можно найти в документации. Служба, которая нам нужна, имеет короткий UUID AA00. Его нужно вставить в основной UUID TI вместо 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 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;
}

В результате работы вышеприведённого кода должно быть выведено следующее:
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

В первую очередь нам нужно получить характеристики службы. Таких характеристик три: значение (UUID AA01), конфигурация (AA02) и период: (AA03). Узнаем их, воспользовавшись следующим кодом:
static BluetoothGattCharacteristic getCharacteristic(BluetoothGattService service, String UUID) {
	List 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");

Теперь нужно включить службу определения температуры, записав »1» в характеристику конфигурации. Подробности об этом есть в вышеупомянутой документации. Можно изменить и интервал обновления показателей, записав нужное значение в характеристику периода, но значение по умолчанию,»1», нам подходит.
byte[] config = { 0x01 }; 
tempConfig.writeValue(config);

После настройки можно приступать к чтению с устройства сведений о температуре. Служба температуры возвращает данные в закодированном формате. Особенности работы с этим форматом данных можно найти в документации к SensorTag. Мы собираемся преобразовать полученные данные в градусы Цельсия и вывести их в консоль. Температура объекта, в соответствии с документацией, зависит от температуры окружающей среды. Здесь мы будем считать, что результаты без дополнительных преобразований нас устраивают.
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);
}

В процессе выполнения этого цикла будут выводиться сведения о температуре, полученные с Bluetooth LE-датчика:
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

Итоги


Мы рассказали о том, как писать на Java приложения, которые умеют работать с Bluetooth LE-устройствами. Надо отметить, что API, использованное в рассмотренном примере, основано на TinyB v0.3. Эта версия библиотеки поддерживает лишь работу в режиме опроса устройств, но в версии 0.4. представлено упрощённое API для обнаружения устройств и служб.

Надеемся, то, что вы сегодня узнали, поможет вам в разработке собственных IoT-проектов.

Комментарии (1)

  • 5 июля 2016 в 16:30

    0

    Здравствуйте, можно ли использовать библиотеку TinyB (java) не только работая с SensorTag, но и с Bluetooth адаптером встроенным в ноутбук или же Bluetooth адаптером подключаемым через usb?

© Habrahabr.ru