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 import com.bluekitchen.btstack.event.SMEventJustWorksRequest; 21 22 import java.nio.charset.StandardCharsets; 23 24 public class LEStreamerClient implements PacketHandler { 25 26 private enum STATE { 27 w4_btstack_working, w4_scan_result, w4_connected, w4_services_complete, w4_characteristic_complete, 28 w4_write_client_config_characteristic_complete, active 29 }; 30 31 private BTstack btstack; 32 private STATE state; 33 private int testAddrType; 34 private BD_ADDR testAddr = new BD_ADDR("00:1A:7D:DA:71:01"); 35 private int connectionHandle; 36 37 private BT_UUID testServiceUUID = new BT_UUID("0000FF10-0000-1000-8000-00805F9B34FB"); 38 private BT_UUID testCharacteristicUUID = new BT_UUID("0000FF11-0000-1000-8000-00805F9B34FB"); 39 private byte testNotification = 1; 40 41 private GATTService testService; 42 private GATTCharacteristic testCharacteristic; 43 44 public void handlePacket(Packet packet){ 45 46 System.out.println("Event " + packet); 47 48 if (packet instanceof SMEventJustWorksRequest){ 49 SMEventJustWorksRequest event = (SMEventJustWorksRequest) packet; 50 System.out.println("Received Just Works pairing request from " + event.getAddress() + " -> auto-accept"); 51 btstack.SMJustWorksConfirm(event.getHandle()); 52 return; 53 } 54 55 if (packet instanceof HCIEventDisconnectionComplete){ 56 System.out.println("Received dissconnect, restart scannning."); 57 state = STATE.w4_scan_result; 58 btstack.GAPLEScanStart(); 59 return; 60 } 61 62 switch (state){ 63 case w4_btstack_working: 64 if (packet instanceof BTstackEventState){ 65 BTstackEventState event = (BTstackEventState) packet; 66 if (event.getState() == 2) { 67 System.out.println("BTstack working, start scanning."); 68 state = STATE.w4_scan_result; 69 btstack.GAPLEScanStart(); 70 } 71 } 72 break; 73 case w4_scan_result: 74 if (packet instanceof GAPEventAdvertisingReport){ 75 // Advertisement received. Connect to the found BT address. 76 GAPEventAdvertisingReport report = (GAPEventAdvertisingReport) packet; 77 System.out.println(String.format("Adv: type %d, addr %s\ndata: %s\n", report.getAddressType(), report.getAddress(), Util.asHexdump(report.getData()))); 78 // hack to find 'LE Streamer' 79 if (new String(report.getData(), StandardCharsets.UTF_8).indexOf("LE Streamer") > 0){ 80 testAddrType = report.getAddressType(); 81 testAddr = report.getAddress(); 82 System.out.println(String.format("LE Streamer found, connect to %s\n", testAddr)); 83 btstack.GAPLEScanStop(); 84 state = STATE.w4_connected; 85 btstack.GAPLEConnect(testAddrType, testAddr); 86 } 87 } 88 break; 89 case w4_connected: 90 if (packet instanceof HCIEventLEConnectionComplete){ 91 HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet; 92 if (event.getStatus() != 0) { 93 System.out.println(testAddr + String.format(" - connection failed, status %d.\nRestart scanning.", event.getStatus())); 94 state = STATE.w4_scan_result; 95 btstack.GAPLEScanStart(); 96 break; 97 } 98 99 // Query test service. 100 state = STATE.w4_services_complete; 101 connectionHandle = event.getConnectionHandle(); 102 System.out.println(testAddr + String.format(" - connected %x.\nQuery streamer service.", connectionHandle)); 103 btstack.GATTDiscoverPrimaryServicesByUUID128(connectionHandle, testServiceUUID); 104 } 105 break; 106 case w4_services_complete: 107 if (packet instanceof GATTEventServiceQueryResult){ 108 // Store streamer service. Wait for GATTEventQueryComplete event to send next GATT command. 109 GATTEventServiceQueryResult event = (GATTEventServiceQueryResult) packet; 110 System.out.println(testAddr + String.format(" - streamer service %s", event.getService().getUUID())); 111 testService = event.getService(); 112 break; 113 } 114 if (packet instanceof GATTEventQueryComplete){ 115 // Check if streamer service is found. 116 if (testService == null) { 117 System.out.println(testAddr + " - no streamer service. \nRestart scanning."); 118 state = STATE.w4_scan_result; 119 btstack.GAPLEScanStart(); 120 break; 121 } 122 System.out.println(testAddr + " - query streamer characteristic."); 123 state = STATE.w4_characteristic_complete; 124 btstack.GATTDiscoverCharacteristicsForServiceByUUID128(connectionHandle, testService, testCharacteristicUUID); 125 } 126 break; 127 case w4_characteristic_complete: 128 if (packet instanceof GATTEventCharacteristicQueryResult){ 129 // Store streamer characteristic. Wait for GATTEventQueryComplete event to send next GATT command. 130 GATTEventCharacteristicQueryResult event = (GATTEventCharacteristicQueryResult) packet; 131 testCharacteristic = event.getCharacteristic(); 132 System.out.println(testAddr + " - streamer characteristic found."); 133 break; 134 } 135 136 if (!(packet instanceof GATTEventQueryComplete)) break; 137 138 if (testCharacteristic == null) { 139 System.out.println("No streamer characteristic found"); 140 break; 141 } 142 System.out.println(testAddr + " - enable notifications."); 143 state = STATE.w4_write_client_config_characteristic_complete; 144 btstack.GATTWriteClientCharacteristicConfiguration(connectionHandle, testCharacteristic, this.testNotification); 145 break; 146 147 case w4_write_client_config_characteristic_complete: 148 if (packet instanceof GATTEventQueryComplete){ 149 System.out.println(testAddr + " - notification enabled."); 150 state = STATE.active; 151 } 152 break; 153 154 case active: 155 if (packet instanceof GATTEventNotification){ 156 GATTEventNotification event = (GATTEventNotification) packet; 157 System.out.println("Data:"); 158 Util.hexdump(event.getValue()); 159 } 160 break; 161 162 default: 163 break; 164 } 165 } 166 167 void test(){ 168 169 System.out.println("LE Streamer Client"); 170 171 // connect to BTstack Daemon via default port on localhost 172 btstack = new BTstack(); 173 btstack.setTcpPort(BTstack.DEFAULT_TCP_PORT); 174 btstack.registerPacketHandler(this); 175 boolean ok = btstack.connect(); 176 if (!ok) { 177 System.out.println("Failed to connect to BTstack Server"); 178 return; 179 } 180 181 System.out.println("BTstackSetPowerMode(1)"); 182 183 // btstack.SMSetAuthenticationRequirements(8 | 4); 184 // btstack.SMSetIoCapabilities(4); 185 186 state = STATE.w4_btstack_working; 187 btstack.BTstackSetPowerMode(1); 188 } 189 190 public static void main(String args[]){ 191 new LEStreamerClient().test(); 192 } 193 } 194