用于蓝牙 LE 应用程序的 Java
本文档是关于如何为物联网平台(例如 Intel® Edison 开发板)创建可以访问远程低功耗蓝牙设备的 Java 物联网应用程序的指南。
获取新的 Intel® IoT Developer Kit,这是一个完整的硬件和软件解决方案,让开发人员能够使用 Intel® Galileo 和 Intel® Edison 板创建令人兴奋的新解决方案。请访问 Intel® 物联网开发者中心。
概述
本文档是关于如何为物联网平台(例如 Intel® Edison 开发板)创建可以访问远程低功耗蓝牙*设备的 Java* 物联网应用程序的指南。Intel® IoT Developer Kit 提供了开发这些应用程序的支持,使用了开源的 TinyB 项目。TinyB 公开了简单的 C++ 和 Java API 来使用低功耗蓝牙设备。本指南仅涵盖在 Intel® Edison 开发板上使用 TinyB 中的 Java API 开发应用程序。
兼容性和要求
TinyB 中当前的低功耗蓝牙 API 已在 Java 8 运行时环境 (OpenJDK 8) 中进行了测试。该环境以及 TinyB 都包含在 Intel® Edison 板的官方 Intel® IoT Developer Kit 映像构建中。
在其他基于 Linux 的系统上,只要安装了 BlueZ* 5.37 或更高版本,并且 bluetoothd
守护进程已通过启用实验性功能(-E
标志)启动,就可以使用 TinyB。有关更多详细信息,请参阅在线 README 文件。
在本指南中,TinyB 应用程序使用 德州仪器传感器标签 作为低功耗蓝牙设备。
文档和应用程序示例
TinyB 公开的低功耗蓝牙 API 的文档可在以下在线位置找到:
HelloTinyB(或 C++ 版的 hellotinyb)示例使用 德州仪器传感器标签,从中读取环境温度和对象温度。该应用程序需要传感器标签的 MAC 地址作为程序的第一个参数(以下示例中的 XX:XX:XX:XX:XX:XX
)。
./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
编写低功耗蓝牙 Java 物联网应用程序
我们将使用 TinyB 存储库中的 HelloTinyB Java 示例 作为示例,展示如何编写一个程序,该程序可以通过低功耗蓝牙读取 GATT 服务的数据。有关德州仪器传感器标签设备的维基页面可以在此处找到:http://processors.wiki.ti.com/index.php/CC2650_SensorTag_User's_Guide。
要开始查看设备,我们首先必须初始化 TinyB 库。BluetoothManager 对象提供了使用蓝牙设备的一个入口点。一次只能有一个 BluetoothManager,并通过 getBluetoothManager()
方法获取其引用。
BluetoothManager manager = BluetoothManager.getBluetoothManager();
如果系统中存在蓝牙适配器,管理器将尝试初始化一个 BluetoothAdapter。要初始化发现,我们可以调用 startDiscovery()
,这将把默认适配器置于发现模式。
boolean discoveryStarted = manager.startDiscovery();
我们应该会看到以下输出
The discovery started: true Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false
发现开始后,将检测到新设备。我们可以通过管理器的 getDevices()
方法获取所有设备列表。我们可以浏览设备列表以查找具有我们作为参数提供的 MAC 地址的设备。我们会一直查找,直到找到它,或者在尝试 15 次(约 1 分钟)后仍未成功。
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; }
之后,我们可以在返回的设备上调用 connect 方法。输出应如下所示:
Found device: Address = C4:BE:84:72:2B:09 Name = CC2650 SensorTag Connected = false Sensor with the provided address connected
我们的设备应该公开一个温度服务,该服务具有我们可以从数据表中找到的 UUID。传感器标签的服务描述可以在这里找到:http://processors.wiki.ti.com/images/a/a8/BLE_SensorTag_GATT_Server.pdf。我们要查找的服务具有短 UUID AA00,我们将其插入 TI Base UUID 的 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; }
上述代码应产生以下输出:
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<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");
如上所述的 PDF 文件所提到的,我们需要通过向配置特性写入 1 来开启温度服务。我们也可以通过向周期特性写入值来修改更新间隔,但 1 秒的默认值对我们来说已经足够了。
byte[] config = { 0x01 }; tempConfig.writeValue(config);
在此配置之后,我们应该能够从设备读取温度。温度服务以编码格式返回数据,可在传感器标签设备的维基页面中找到。将原始温度格式转换为摄氏度并打印。对象温度的转换取决于维基中提到的环境温度,但我们假设未经转换的结果对我们来说已足够。
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); }
运行此循环将打印从低功耗蓝牙传感器收集的温度值。
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
已知限制
本示例中使用的 API 基于 TinyB v0.3,它只支持轮询,但 v0.4 将引入一个简化的 API 来发现设备和服务。