1 package com.bluekitchen; 2 3 import com.bluekitchen.btstack.BD_ADDR; 4 import com.bluekitchen.btstack.BT_UUID; 5 import com.bluekitchen.btstack.BTstack; 6 import com.bluekitchen.btstack.GATTCharacteristic; 7 import com.bluekitchen.btstack.GATTService; 8 import com.bluekitchen.btstack.Packet; 9 import com.bluekitchen.btstack.PacketHandler; 10 import com.bluekitchen.btstack.Util; 11 import com.bluekitchen.btstack.event.BTstackEventState; 12 import com.bluekitchen.btstack.event.GAPEventAdvertisingReport; 13 import com.bluekitchen.btstack.event.GATTEventCharacteristicQueryResult; 14 import com.bluekitchen.btstack.event.GATTEventCharacteristicValueQueryResult; 15 import com.bluekitchen.btstack.event.GATTEventNotification; 16 import com.bluekitchen.btstack.event.GATTEventQueryComplete; 17 import com.bluekitchen.btstack.event.GATTEventServiceQueryResult; 18 import com.bluekitchen.btstack.event.HCIEventDisconnectionComplete; 19 import com.bluekitchen.btstack.event.HCIEventLEConnectionComplete; 20 21 public class GATTClientTest implements PacketHandler { 22 23 private enum STATE { 24 w4_btstack_working, w4_scan_result, w4_connected, w4_services_complete, w4_characteristic_complete, w4_characteristic_read 25 , w4_characteristic_write, w4_acc_service_result, w4_acc_enable_characteristic_result, w4_write_acc_enable_result, w4_acc_client_config_characteristic_result, w4_acc_client_config_result, 26 w4_acc_data, w4_connected_acc, battery_data 27 }; 28 29 private BTstack btstack; 30 private STATE state; 31 private int testAddrType; 32 private BD_ADDR testAddr; 33 private int testHandle; 34 private GATTService testService; 35 private GATTCharacteristic testCharacteristic; 36 private int service_count = 0; 37 private int characteristic_count = 0; 38 private int connectionHandle; 39 private int counter = 0; 40 41 private byte[] acc_service_uuid = new byte[] {(byte)0xf0, 0, (byte)0xaa, (byte)0x10, 4, (byte)0x51, (byte)0x40, 0, (byte)0xb0, 0, 0, 0, 0, 0, 0, 0}; 42 private byte[] acc_chr_client_config_uuid = new byte[] {(byte)0xf0, 0, (byte)0xaa, (byte)0x11, 4, (byte)0x51, (byte)0x40, 0, (byte)0xb0, 0, 0, 0, 0, 0, 0, 0}; 43 private byte[] acc_chr_enable_uuid = new byte[] {(byte)0xf0, 0, (byte)0xaa, (byte)0x12, 4, (byte)0x51, (byte)0x40, 0, (byte)0xb0, 0, 0, 0, 0, 0, 0, 0}; 44 private byte[] acc_enable = new byte[] {1}; 45 private byte acc_notification = 1; 46 private GATTService accService; 47 private GATTCharacteristic enableCharacteristic; 48 private GATTCharacteristic configCharacteristic; 49 50 private GATTService batteryService; 51 private GATTCharacteristic batteryLevelCharacteristic; 52 private byte[] battery_level_chr_uuid = new byte[] {0, 0, (byte)0x2a, (byte)0x19, 0, 0, (byte)0x10, 0, (byte)0x80, 0, 0, (byte)0x80, (byte)0x5f, (byte)0x9b, (byte)0x34, (byte)0xfb}; 53 GATTEventCharacteristicValueQueryResult battery; 54 55 private BT_UUID uuid128(byte[] att_uuid) { 56 byte [] uuid = new byte[16]; 57 Util.flipX(att_uuid, uuid); 58 return new BT_UUID(uuid); 59 } 60 61 public void handlePacket(Packet packet){ 62 if (packet instanceof HCIEventDisconnectionComplete){ 63 System.out.println(String.format("Received dissconnect, restart scannning.")); 64 state = STATE.w4_scan_result; 65 btstack.GAPLEScanStart(); 66 return; 67 } 68 69 if (packet instanceof GATTEventQueryComplete){ 70 GATTEventQueryComplete event = (GATTEventQueryComplete) packet; 71 System.out.println(testAddr + " battery data"); 72 if (event.getATTStatus() != 0){ 73 System.out.println("Battery data could not be read.\nRestart scanning."); 74 state = STATE.w4_scan_result; 75 btstack.GAPLEScanStart(); 76 return; 77 } 78 } 79 80 switch (state){ 81 case w4_btstack_working: 82 if (packet instanceof BTstackEventState){ 83 BTstackEventState event = (BTstackEventState) packet; 84 if (event.getState() == 2) { 85 System.out.println("BTstack working, start scanning."); 86 state = STATE.w4_scan_result; 87 btstack.GAPLEScanStart(); 88 } 89 } 90 break; 91 case w4_scan_result: 92 if (packet instanceof GAPEventAdvertisingReport){ 93 // Advertisement received. Connect to the found BT address. 94 GAPEventAdvertisingReport report = (GAPEventAdvertisingReport) packet; 95 testAddrType = report.getAddressType(); 96 testAddr = report.getAddress(); 97 System.out.println(String.format("Adv: type %d, addr %s\ndata: %s \n Stop scan, initiate connect.", testAddrType, testAddr, Util.asHexdump(report.getData()))); 98 btstack.GAPLEScanStop(); 99 state = STATE.w4_connected; 100 btstack.GAPLEConnect(testAddrType, testAddr); 101 } 102 break; 103 case w4_connected: 104 if (packet instanceof HCIEventLEConnectionComplete){ 105 HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet; 106 if (event.getStatus() != 0) { 107 System.out.println(testAddr + String.format(" - connection failed, status %d.\nRestart scanning.", event.getStatus())); 108 state = STATE.w4_scan_result; 109 btstack.GAPLEScanStart(); 110 break; 111 } 112 113 // Query battery service. 114 state = STATE.w4_services_complete; 115 connectionHandle = event.getConnectionHandle(); 116 System.out.println(testAddr + String.format(" - connected %x.\nQuery battery service.", connectionHandle)); 117 btstack.GATTDiscoverPrimaryServicesByUUID16(connectionHandle, 0x180f); 118 } 119 break; 120 case w4_services_complete: 121 if (packet instanceof GATTEventServiceQueryResult){ 122 // Store battery service. Wait for GATTEventQueryComplete event to send next GATT command. 123 GATTEventServiceQueryResult event = (GATTEventServiceQueryResult) packet; 124 System.out.println(testAddr + String.format(" - battery service %s", event.getService().getUUID())); 125 batteryService = event.getService(); 126 break; 127 } 128 if (packet instanceof GATTEventQueryComplete){ 129 // Check if battery service is found. 130 if (batteryService == null) { 131 System.out.println(testAddr + " - no battery service. \nRestart scanning."); 132 state = STATE.w4_scan_result; 133 btstack.GAPLEScanStart(); 134 break; 135 } 136 System.out.println(testAddr + " - query battery level."); 137 state = STATE.w4_characteristic_complete; 138 btstack.GATTDiscoverCharacteristicsForServiceByUUID128(connectionHandle, batteryService, uuid128(this.battery_level_chr_uuid)); 139 } 140 break; 141 case w4_characteristic_complete: 142 if (packet instanceof GATTEventCharacteristicQueryResult){ 143 // Store battery level characteristic. Wait for GATTEventQueryComplete event to send next GATT command. 144 GATTEventCharacteristicQueryResult event = (GATTEventCharacteristicQueryResult) packet; 145 batteryLevelCharacteristic = event.getCharacteristic(); 146 System.out.println(testAddr + " - battery level found."); 147 break; 148 } 149 150 if (!(packet instanceof GATTEventQueryComplete)) break; 151 if (batteryLevelCharacteristic == null) { 152 System.out.println("No battery level characteristic found"); 153 break; 154 } 155 System.out.println(testAddr + " - polling battery."); 156 counter = 0; 157 state = STATE.battery_data; 158 new Thread(new Runnable(){ 159 @Override 160 public void run() { 161 try { 162 while(state == STATE.battery_data){ 163 Thread.sleep(5000); 164 btstack.GATTReadValueOfCharacteristic(connectionHandle, batteryLevelCharacteristic); 165 } 166 } catch (InterruptedException e) {} 167 } 168 }).start(); 169 break; 170 case battery_data: 171 if (packet instanceof GATTEventCharacteristicValueQueryResult){ 172 GATTEventCharacteristicValueQueryResult battery = (GATTEventCharacteristicValueQueryResult) packet; 173 174 if (battery.getValueLength() != 1) break; 175 byte[] data = battery.getValue(); 176 counter = counter + 1; 177 System.out.println(String.format("Counter %d, battery level: %d", counter, data[0]) + "%"); 178 break; 179 180 } 181 break; 182 default: 183 break; 184 } 185 } 186 187 public void handlePacketAcc(Packet packet){ 188 189 // System.out.println(packet.toString()); 190 if (packet instanceof HCIEventDisconnectionComplete){ 191 HCIEventDisconnectionComplete event = (HCIEventDisconnectionComplete) packet; 192 testHandle = event.getConnectionHandle(); 193 System.out.println(String.format("Received disconnect, status %d, handle %x", event.getStatus(), testHandle)); 194 return; 195 } 196 197 switch (state){ 198 case w4_btstack_working: 199 if (packet instanceof BTstackEventState){ 200 BTstackEventState event = (BTstackEventState) packet; 201 if (event.getState() == 2) { 202 203 System.out.println("GAPLEScanStart()"); 204 state = STATE.w4_scan_result; 205 btstack.GAPLEScanStart(); 206 } 207 } 208 break; 209 case w4_scan_result: 210 if (packet instanceof GAPEventAdvertisingReport){ 211 GAPEventAdvertisingReport report = (GAPEventAdvertisingReport) packet; 212 testAddrType = report.getAddressType(); 213 testAddr = report.getAddress(); 214 System.out.println(String.format("Adv: type %d, addr %s", testAddrType, testAddr)); 215 System.out.println(String.format("Data: %s", Util.asHexdump(report.getData()))); 216 System.out.println("GAPLEScanStop()"); 217 btstack.GAPLEScanStop(); 218 System.out.println("GAPLEConnect(...)"); 219 state = STATE.w4_connected_acc; 220 btstack.GAPLEConnect(testAddrType, testAddr); 221 } 222 break; 223 224 case w4_connected: 225 if (packet instanceof HCIEventLEConnectionComplete){ 226 HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet; 227 testHandle = event.getConnectionHandle(); 228 System.out.println(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle)); 229 state = STATE.w4_services_complete; 230 System.out.println("GATTDiscoverPrimaryServices(...)"); 231 btstack.GATTDiscoverPrimaryServices(testHandle); 232 } 233 break; 234 case w4_services_complete: 235 if (packet instanceof GATTEventServiceQueryResult){ 236 GATTEventServiceQueryResult event = (GATTEventServiceQueryResult) packet; 237 if (testService == null){ 238 System.out.println(String.format("First service UUID %s", event.getService().getUUID())); 239 testService = event.getService(); 240 } 241 System.out.println("Service: " + event.getService()); 242 service_count++; 243 } 244 if (packet instanceof GATTEventQueryComplete){ 245 System.out.println(String.format("Service query complete, total %d services", service_count)); 246 state = STATE.w4_characteristic_complete; 247 btstack.GATTDiscoverCharacteristicsForService(testHandle, testService); 248 } 249 break; 250 251 case w4_characteristic_complete: 252 if (packet instanceof GATTEventCharacteristicQueryResult){ 253 GATTEventCharacteristicQueryResult event = (GATTEventCharacteristicQueryResult) packet; 254 if (testCharacteristic == null){ 255 System.out.println(String.format("First characteristic UUID %s", event.getCharacteristic().getUUID())); 256 testCharacteristic = event.getCharacteristic(); 257 } 258 System.out.println("Characteristic: " + event.getCharacteristic()); 259 characteristic_count++; 260 } 261 if (packet instanceof GATTEventQueryComplete){ 262 System.out.println(String.format("Characteristic query complete, total %d characteristics", characteristic_count)); 263 state = STATE.w4_characteristic_read; 264 btstack.GATTReadValueOfCharacteristic(testHandle, testCharacteristic); 265 } 266 break; 267 268 case w4_characteristic_read: 269 if (packet instanceof GATTEventCharacteristicValueQueryResult){ 270 System.out.println("Read complete"); 271 System.out.println( packet.toString()); 272 state = STATE.w4_characteristic_write; 273 byte [] data = { 'B', 'T', 's', 't', 'a', 'c', 'k'}; 274 btstack.GATTWriteValueOfCharacteristic(testHandle, testCharacteristic, data.length, data); 275 } 276 break; 277 case w4_characteristic_write: 278 if (packet instanceof GATTEventQueryComplete){ 279 System.out.println("Write complete, search for ACC service"); 280 state = STATE.w4_acc_service_result; 281 btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(this.acc_service_uuid)); // not working 282 } 283 break; 284 285 case w4_connected_acc: 286 if (packet instanceof HCIEventLEConnectionComplete){ 287 HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet; 288 testHandle = event.getConnectionHandle(); 289 System.out.println(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle)); 290 System.out.println("Search for ACC service"); 291 state = STATE.w4_acc_service_result; 292 byte [] uuid = new byte[16]; 293 Util.flipX(this.acc_service_uuid, uuid); // works 294 btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(uuid)); 295 } 296 break; 297 298 case w4_acc_service_result: 299 System.out.println(String.format("w4_acc_service_result state")); 300 if (packet instanceof GATTEventServiceQueryResult){ 301 GATTEventServiceQueryResult event = (GATTEventServiceQueryResult) packet; 302 System.out.println(String.format("ACC service found %s", event.getService().getUUID())); 303 accService = event.getService(); 304 break; 305 } 306 if (packet instanceof GATTEventQueryComplete){ 307 if (accService == null) { 308 System.out.println("No acc service found"); 309 break; 310 } 311 System.out.println("ACC Service found, searching for acc enable characteristic"); 312 state = STATE.w4_acc_enable_characteristic_result; 313 byte [] uuid = new byte[16]; 314 Util.flipX(this.acc_chr_enable_uuid, uuid); 315 btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid)); 316 } 317 break; 318 319 case w4_acc_enable_characteristic_result: 320 if (packet instanceof GATTEventCharacteristicQueryResult){ 321 GATTEventCharacteristicQueryResult event = (GATTEventCharacteristicQueryResult) packet; 322 enableCharacteristic = event.getCharacteristic(); 323 System.out.println("Enable ACC Characteristic found "); 324 } 325 if (packet instanceof GATTEventQueryComplete){ 326 if (enableCharacteristic == null) { 327 System.out.println("No acc enable chr found"); 328 break; 329 } 330 System.out.println("Write enable acc characteristic"); 331 state = STATE.w4_write_acc_enable_result; 332 btstack.GATTWriteValueOfCharacteristic(testHandle, enableCharacteristic, 1, this.acc_enable); 333 } 334 break; 335 case w4_write_acc_enable_result: 336 if (packet instanceof GATTEventQueryComplete){ 337 System.out.println("Acc enabled,searching for acc client config characteristic"); 338 byte [] uuid = new byte[16]; 339 Util.flipX(this.acc_chr_client_config_uuid, uuid); 340 btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid)); 341 state = STATE.w4_acc_client_config_characteristic_result; 342 } 343 break; 344 345 case w4_acc_client_config_characteristic_result: 346 if (packet instanceof GATTEventCharacteristicQueryResult){ 347 GATTEventCharacteristicQueryResult event = (GATTEventCharacteristicQueryResult) packet; 348 configCharacteristic = event.getCharacteristic(); 349 System.out.println("ACC Client Config Characteristic found"); 350 } 351 if (packet instanceof GATTEventQueryComplete){ 352 if (configCharacteristic == null) { 353 System.out.println("No acc config chr found"); 354 break; 355 } 356 System.out.println("Write ACC Client Config Characteristic"); 357 state = STATE.w4_acc_data; 358 btstack.GATTWriteClientCharacteristicConfiguration(testHandle, configCharacteristic, this.acc_notification); 359 } 360 break; 361 362 case w4_acc_data: 363 if (packet instanceof GATTEventQueryComplete){ 364 System.out.println("Acc configured for notification"); 365 break; 366 } 367 368 if (packet instanceof GATTEventNotification){ 369 System.out.println("Acc Value"); 370 System.out.println(packet.toString()); 371 btstack.GAPDisconnect(testHandle); 372 } 373 374 default: 375 break; 376 } 377 } 378 379 void test(){ 380 381 System.out.println("LE Test Application"); 382 383 // connect to BTstack Daemon via default port on localhost 384 btstack = new BTstack(); 385 btstack.setTcpPort(BTstack.DEFAULT_TCP_PORT); 386 btstack.registerPacketHandler(this); 387 boolean ok = btstack.connect(); 388 if (!ok) { 389 System.out.println("Failed to connect to BTstack Server"); 390 return; 391 } 392 393 System.out.println("BTstackSetPowerMode(1)"); 394 395 state = STATE.w4_btstack_working; 396 btstack.BTstackSetPowerMode(1); 397 } 398 399 public static void main(String args[]){ 400 new GATTClientTest().test(); 401 } 402 } 403