1 package com.bluekitchen; 2 3 import com.bluekitchen.btstack.BD_ADDR; 4 import com.bluekitchen.btstack.BTstack; 5 import com.bluekitchen.btstack.Packet; 6 import com.bluekitchen.btstack.PacketHandler; 7 import com.bluekitchen.btstack.RFCOMMDataPacket; 8 import com.bluekitchen.btstack.Util; 9 import com.bluekitchen.btstack.event.*; 10 11 /** 12 * Simple demonstration of the Java binding by streaming data over SPP. 13 * 14 * This is the same example as the: example/spp_streamer_client.c example. 15 * 16 * It can be run against the 'spp_streamer' example. 17 * 18 * To run, two dongles are needed, then: 19 * 20 * 1. In a first terminal run: 21 * 22 * cd port/libusb 23 * ./spp_streamer 24 * 25 * 2. In a second terminal, run the daemon: 26 * 27 * cd port/daemon 28 * ./src/BTdaemon --tcp 29 * 30 * 3. In a third terminal, run the java application: 31 * 32 * cd platform/daemon/binding/java 33 * ant run 34 */ 35 public class SPPStreamerClient implements PacketHandler { 36 37 private final static int NUM_ROWS = 25; 38 private final static int NUM_COLS = 40; 39 public static final int TEST_COD = 0x1234; 40 41 private enum STATE { 42 BTSTACK_WORKING, INQUIRY_RESULT, SDP_QUERY_RESULT, CONNECTED, SENDING 43 } 44 45 private BTstack btstack; 46 private STATE state; 47 48 private BD_ADDR remote; 49 50 private int outgoing_channel_nr = -1; 51 private int rfcommChannelID = 0; 52 53 private final static int REPORT_INTERVAL_MS = 3000; 54 private long test_data_transferred; 55 private long test_data_start; 56 private byte[] sppTestData; 57 58 59 private void createSppTestData(final int mtu) { 60 byte[] temp = new byte[NUM_ROWS * NUM_COLS]; 61 int x, y; 62 for (y = 0; y < NUM_ROWS; y++) { 63 for (x = 0; x < NUM_COLS - 2; x++) { 64 temp[y * NUM_COLS + x] = (byte) ('0' + (x % 10)); 65 } 66 temp[y * NUM_COLS + NUM_COLS - 2] = '\n'; 67 temp[y * NUM_COLS + NUM_COLS - 1] = '\r'; 68 } 69 70 // cut to MTU 71 sppTestData = new byte[Math.min(mtu, temp.length)]; 72 System.arraycopy(temp, 0, sppTestData, 0, sppTestData.length); 73 } 74 75 void testReset() { 76 test_data_start = System.currentTimeMillis(); 77 test_data_transferred = 0; 78 } 79 80 private void calculateSpeedAndLog(int bytesSent){ 81 test_data_transferred += bytesSent; 82 // evaluate 83 long now = System.currentTimeMillis(); 84 long timePassed = now - test_data_start; 85 if (timePassed < REPORT_INTERVAL_MS) return; 86 87 // print speed 88 long bytesPerSecond = test_data_transferred * 1000 / timePassed; 89 System.out.printf("%d bytes -> %d.%03d kB/s\n", (int) test_data_transferred, (int) bytesPerSecond / 1000, bytesPerSecond % 1000); 90 91 // restart 92 test_data_start = now; 93 test_data_transferred = 0; 94 } 95 96 private void startSDPQuery() { 97 state = STATE.SDP_QUERY_RESULT; 98 int sppUUID = 0x1101; 99 byte[] serviceSearchPattern = Util.serviceSearchPatternForUUID16(sppUUID); 100 btstack.SDPClientQueryRFCOMMServices(remote, serviceSearchPattern); 101 } 102 103 public void handlePacket(Packet packet) { 104 if (packet instanceof HCIEventDisconnectionComplete) { 105 final HCIEventDisconnectionComplete event = (HCIEventDisconnectionComplete) packet; 106 System.out.printf("Received disconnect, status %d, handle %x%n", event.getStatus(), event.getConnectionHandle()); 107 btstack.disconnect(); 108 return; 109 } 110 111 switch (state) { 112 case BTSTACK_WORKING: 113 if (packet instanceof BTstackEventState) { 114 final BTstackEventState event = (BTstackEventState) packet; 115 if (event.getState() == 2) { 116 System.out.println("BTstack working. Start inquiry.."); 117 state = STATE.INQUIRY_RESULT; 118 btstack.GAPInquiryStart(5); 119 } 120 } 121 break; 122 123 case INQUIRY_RESULT: 124 if (packet instanceof GAPEventInquiryResult) { 125 final GAPEventInquiryResult event = ((GAPEventInquiryResult) packet); 126 System.out.printf("Found device with COD: %d with address: %s\n", event.getClassOfDevice(), event.getBdAddr()); 127 if (event.getClassOfDevice() == TEST_COD) { 128 remote = event.getBdAddr(); 129 btstack.GAPInquiryStop(); 130 System.out.println("Start SDP query on: " + remote); 131 startSDPQuery(); 132 } 133 } 134 if (packet instanceof GAPEventInquiryComplete) { 135 if (remote == null) { 136 System.out.println("No device with COD: %d found -> scan again."); 137 btstack.GAPInquiryStart(5); 138 } 139 } 140 break; 141 142 case SDP_QUERY_RESULT: 143 if (packet instanceof SDPEventQueryRFCOMMService) { 144 final SDPEventQueryRFCOMMService service = (SDPEventQueryRFCOMMService) packet; 145 System.out.println("Found RFCOMM channel " + service.getName() + ", channel nr: " + service.getRFCOMMChannel()); 146 outgoing_channel_nr = service.getRFCOMMChannel(); 147 } 148 if (packet instanceof SDPEventQueryComplete) { 149 SDPEventQueryComplete complete = (SDPEventQueryComplete) packet; 150 if (complete.getStatus() != 0) { 151 System.out.printf("SDP Query failed with status 0x%02x, retry SDP query.%n", complete.getStatus()); 152 startSDPQuery(); 153 break; 154 } 155 if (outgoing_channel_nr >= 0) { 156 state = STATE.CONNECTED; 157 System.out.println("Connect to channel nr " + outgoing_channel_nr); 158 btstack.RFCOMMCreateChannel(remote, outgoing_channel_nr); 159 } 160 } 161 break; 162 163 case CONNECTED: 164 if (packet instanceof RFCOMMEventChannelOpened) { 165 RFCOMMEventChannelOpened e = (RFCOMMEventChannelOpened) packet; 166 System.out.println("RFCOMMEventChannelOpened with status " + e.getStatus()); 167 if (e.getStatus() != 0) { 168 System.out.println("RFCOMM channel open failed, status " + e.getStatus()); 169 } else { 170 state = STATE.SENDING; 171 rfcommChannelID = e.getRFCOMMCid(); 172 System.out.printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %d, max frame size %d%n", rfcommChannelID, e.getMaxFrameSize()); 173 174 createSppTestData(e.getMaxFrameSize()); 175 testReset(); 176 177 btstack.RFCOMMRequestCanSendNow(e.getRFCOMMCid()); 178 } 179 } 180 break; 181 182 case SENDING: 183 if (packet instanceof RFCOMMEventCanSendNow) { 184 btstack.RFCOMMSendData(rfcommChannelID, sppTestData); 185 calculateSpeedAndLog(sppTestData.length); 186 btstack.RFCOMMRequestCanSendNow(rfcommChannelID); 187 } 188 189 if (packet instanceof RFCOMMDataPacket) { 190 // duplex 191 calculateSpeedAndLog(sppTestData.length); 192 } 193 default: 194 break; 195 } 196 } 197 198 void stream() { 199 System.out.println("SPP Streamer Client"); 200 201 // connect to BTstack Daemon via default port on localhost 202 // start: src/BTdaemon --tcp 203 204 btstack = new BTstack(); 205 btstack.setTcpPort(BTstack.DEFAULT_TCP_PORT); 206 btstack.registerPacketHandler(this); 207 boolean ok = btstack.connect(); 208 if (!ok) { 209 System.out.println("Failed to connect to BTstack Server"); 210 return; 211 } 212 213 System.out.println("BTstackSetPowerMode(1)"); 214 215 state = STATE.BTSTACK_WORKING; 216 btstack.BTstackSetPowerMode(1); 217 } 218 219 public static void main(String[] args) { 220 new SPPStreamerClient().stream(); 221 } 222 } 223 224