Java Client Library for BTstack Daemon

This commit is contained in:
matthias.ringwald 2014-06-04 07:27:43 +00:00
parent c51407e08f
commit b9d53f0c6c
17 changed files with 1858 additions and 0 deletions

View File

@ -0,0 +1,103 @@
package com.bluekitchen.btstack;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
public class SocketConnectionUnix extends SocketConnection {
private LocalSocket socket;
private String unixSocketName = "/data/btstack/BTstack";
private InputStream in;
private OutputStream out;
private byte inHeader[] = new byte[6];
private byte inPayload[] = new byte[2000];
public SocketConnectionUnix(){
socket = null;
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#connect()
*/
@Override
public boolean connect() {
try {
socket = new LocalSocket();
LocalSocketAddress socketAddress = new LocalSocketAddress(unixSocketName, LocalSocketAddress.Namespace.FILESYSTEM);
socket.connect(socketAddress);
in = socket.getInputStream();
out = socket.getOutputStream();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#sendPacket(com.bluekitchen.btstack.Packet)
*/
@Override
public boolean sendPacket(Packet packet) {
if (out == null) return false;
try {
System.out.println("Send "); Util.hexdump(packet.getBuffer(), packet.getPayloadLen());
out.write(headerForPacket(packet));
out.write(packet.getBuffer());
out.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#receivePacket()
*/
@Override
public Packet receivePacket() {
if (in == null) return null;
int bytes_read = Util.readExactly(in, inHeader, 0, 6);
if (bytes_read != 6) return null;
int packetType = Util.readBt16(inHeader, 0);
int channel = Util.readBt16(inHeader, 2);
int len = Util.readBt16(inHeader, 4);
Util.readExactly(in, inPayload, 0, len);
Packet packet = new Packet(packetType, channel ,inPayload, len);
return packet;
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#disconnect()
*/
@Override
public void disconnect() {
if (socket != null){
try {
socket.close();
} catch (IOException e) {
}
}
}
private byte[] headerForPacket(Packet packet) {
byte header[] = new byte[6];
Util.storeBt16(header, 0, packet.getPacketType());
Util.storeBt16(header, 2, packet.getChannel());
Util.storeBt16(header, 4, packet.getBuffer().length);
return header;
}
}

View File

@ -0,0 +1,296 @@
package com.bluekitchen.lescan;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.TextView;
import com.bluekitchen.btstack.BD_ADDR;
import com.bluekitchen.btstack.BT_UUID;
import com.bluekitchen.btstack.BTstack;
import com.bluekitchen.btstack.GATTCharacteristic;
import com.bluekitchen.btstack.GATTService;
import com.bluekitchen.btstack.Packet;
import com.bluekitchen.btstack.PacketHandler;
import com.bluekitchen.btstack.Util;
import com.bluekitchen.btstack.event.BTstackEventState;
import com.bluekitchen.btstack.event.GAPLEAdvertisingReport;
import com.bluekitchen.btstack.event.GATTCharacteristicQueryResult;
import com.bluekitchen.btstack.event.GATTCharacteristicValueQueryResult;
import com.bluekitchen.btstack.event.GATTNotification;
import com.bluekitchen.btstack.event.GATTQueryComplete;
import com.bluekitchen.btstack.event.GATTServiceQueryResult;
import com.bluekitchen.btstack.event.HCIEventDisconnectionComplete;
import com.bluekitchen.btstack.event.HCIEventLEConnectionComplete;
public class MainActivity extends Activity implements PacketHandler {
private static final String BTSTACK_TAG = "BTstack";
private enum STATE {
w4_btstack_working, w4_scan_result, w4_connected, w4_services_complete, w4_characteristic_complete, w4_characteristic_read
, 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,
w4_acc_data, w4_connected_acc
};
private TextView tv;
private BTstack btstack;
private STATE state;
private int testAddrType;
private BD_ADDR testAddr;
private int testHandle;
private GATTService testService;
private GATTCharacteristic testCharacteristic;
private int service_count = 0;
private int characteristic_count = 0;
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};
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};
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};
private byte[] acc_enable = new byte[] {1};
private byte acc_notification = 1;
private GATTService accService;
private GATTCharacteristic enableCharacteristic;
private GATTCharacteristic configCharacteristic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = new TextView(this);
setContentView(tv);
test();
}
void addMessage(final String message){
Log.d(BTSTACK_TAG, message);
runOnUiThread(new Runnable(){
public void run(){
tv.append("\n");
tv.append(message);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void handlePacket(Packet packet){
// System.out.println(packet.toString());
if (packet instanceof HCIEventDisconnectionComplete){
HCIEventDisconnectionComplete event = (HCIEventDisconnectionComplete) packet;
testHandle = event.getConnectionHandle();
addMessage(String.format("Received disconnect, status %d, handle %x", event.getStatus(), testHandle));
return;
}
switch (state){
case w4_btstack_working:
if (packet instanceof BTstackEventState){
BTstackEventState event = (BTstackEventState) packet;
if (event.getState() == 2) {
addMessage("GAPLEScanStart()");
state = STATE.w4_scan_result;
btstack.GAPLEScanStart();
}
}
break;
case w4_scan_result:
if (packet instanceof GAPLEAdvertisingReport){
GAPLEAdvertisingReport report = (GAPLEAdvertisingReport) packet;
testAddrType = report.getAddressType();
testAddr = report.getAddress();
addMessage(String.format("Adv: type %d, addr %s", testAddrType, testAddr));
addMessage(String.format("Data: %s", Util.asHexdump(report.getData())));
addMessage("GAPLEScanStop()");
btstack.GAPLEScanStop();
addMessage("GAPLEConnect(...)");
state = STATE.w4_connected_acc;
btstack.GAPLEConnect(testAddrType, testAddr);
}
break;
case w4_connected:
if (packet instanceof HCIEventLEConnectionComplete){
HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet;
testHandle = event.getConnectionHandle();
addMessage(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle));
state = STATE.w4_services_complete;
addMessage("GATTDiscoverPrimaryServices(...)");
btstack.GATTDiscoverPrimaryServices(testHandle);
}
break;
case w4_services_complete:
if (packet instanceof GATTServiceQueryResult){
GATTServiceQueryResult event = (GATTServiceQueryResult) packet;
if (testService == null){
addMessage(String.format("First service UUID %s", event.getService().getUUID()));
testService = event.getService();
}
Log.d(BTSTACK_TAG, "Service: " + event.getService());
service_count++;
}
if (packet instanceof GATTQueryComplete){
addMessage(String.format("Service query complete, total %d services", service_count));
state = STATE.w4_characteristic_complete;
btstack.GATTDiscoverCharacteristicsForService(testHandle, testService);
}
break;
case w4_characteristic_complete:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
if (testCharacteristic == null){
addMessage(String.format("First characteristic UUID %s", event.getCharacteristic().getUUID()));
testCharacteristic = event.getCharacteristic();
}
Log.d(BTSTACK_TAG, "Characteristic: " + event.getCharacteristic());
characteristic_count++;
}
if (packet instanceof GATTQueryComplete){
addMessage(String.format("Characteristic query complete, total %d characteristics", characteristic_count));
state = STATE.w4_characteristic_read;
btstack.GATTReadValueOfCharacteristic(testHandle, testCharacteristic);
}
break;
case w4_characteristic_read:
if (packet instanceof GATTCharacteristicValueQueryResult){
addMessage("Read complete");
Log.d(BTSTACK_TAG, packet.toString());
state = STATE.w4_characteristic_write;
byte [] data = { 'B', 'T', 's', 't', 'a', 'c', 'k'};
btstack.GATTWriteValueOfCharacteristic(testHandle, testCharacteristic, data.length, data);
}
break;
case w4_characteristic_write:
if (packet instanceof GATTQueryComplete){
addMessage("Write complete, search for ACC service");
state = STATE.w4_acc_service_result;
btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(this.acc_service_uuid)); // not working
}
break;
case w4_connected_acc:
if (packet instanceof HCIEventLEConnectionComplete){
HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet;
testHandle = event.getConnectionHandle();
addMessage(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle));
addMessage("Search for ACC service");
state = STATE.w4_acc_service_result;
byte [] uuid = new byte[16];
Util.flipX(this.acc_service_uuid, uuid); // works
btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(uuid));
}
break;
case w4_acc_service_result:
addMessage(String.format("w4_acc_service_result state"));
if (packet instanceof GATTServiceQueryResult){
GATTServiceQueryResult event = (GATTServiceQueryResult) packet;
addMessage(String.format("ACC service found %s", event.getService().getUUID()));
accService = event.getService();
break;
}
if (packet instanceof GATTQueryComplete){
if (accService == null) {
addMessage("No acc service found");
break;
}
addMessage("ACC Service found, searching for acc enable characteristic");
state = STATE.w4_acc_enable_characteristic_result;
byte [] uuid = new byte[16];
Util.flipX(this.acc_chr_enable_uuid, uuid);
btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid));
}
break;
case w4_acc_enable_characteristic_result:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
enableCharacteristic = event.getCharacteristic();
addMessage("Enable ACC Characteristic found ");
}
if (packet instanceof GATTQueryComplete){
if (enableCharacteristic == null) {
addMessage("No acc enable chr found");
break;
}
addMessage("Write enable acc characteristic");
state = STATE.w4_write_acc_enable_result;
btstack.GATTWriteValueOfCharacteristic(testHandle, enableCharacteristic, 1, this.acc_enable);
}
break;
case w4_write_acc_enable_result:
if (packet instanceof GATTQueryComplete){
addMessage("Acc enabled,searching for acc client config characteristic");
byte [] uuid = new byte[16];
Util.flipX(this.acc_chr_client_config_uuid, uuid);
btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid));
state = STATE.w4_acc_client_config_characteristic_result;
}
break;
case w4_acc_client_config_characteristic_result:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
configCharacteristic = event.getCharacteristic();
addMessage("ACC Client Config Characteristic found");
}
if (packet instanceof GATTQueryComplete){
if (configCharacteristic == null) {
addMessage("No acc config chr found");
break;
}
addMessage("Write ACC Client Config Characteristic");
state = STATE.w4_acc_data;
btstack.GATTWriteClientCharacteristicConfiguration(testHandle, configCharacteristic, this.acc_notification);
}
break;
case w4_acc_data:
if (packet instanceof GATTQueryComplete){
addMessage("Acc configured for notification");
break;
}
if (packet instanceof GATTNotification){
addMessage("Acc Value");
Log.d(BTSTACK_TAG, packet.toString());
btstack.GAPDisconnect(testHandle);
}
default:
break;
}
}
void test(){
addMessage("LE Test Application");
btstack = new BTstack();
btstack.registerPacketHandler(this);
boolean ok = btstack.connect();
if (!ok) {
addMessage("Failed to connect to BTstack Server");
return;
}
addMessage("BTstackSetPowerMode(1)");
state = STATE.w4_btstack_working;
btstack.BTstackSetPowerMode(1);
}
}

53
java/build.xml Normal file
View File

@ -0,0 +1,53 @@
<project name="BTstack" basedir="." default="main">
<property name="src.dir" value="src"/>
<property name="gen.dir" value="gen"/>
<property name="example.dir" value="example"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="test-class" value="com.bluekitchen.GATTClientTest"/>
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${gen.dir}"/>
</target>
<target name="generate">
<mkdir dir="${gen.dir}"/>
<exec executable="./generate.py"/>
</target>
<target name="compile" depends="generate">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}">
<src path="${src.dir}" />
<src path="${gen.dir}" />
<src path="${example.dir}" />
</javac>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="test-class" value="${test-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java fork="true" classname="${test-class}">
<classpath>
<path location="${jar.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
</project>

View File

@ -0,0 +1,264 @@
package com.bluekitchen;
import com.bluekitchen.btstack.BD_ADDR;
import com.bluekitchen.btstack.BT_UUID;
import com.bluekitchen.btstack.BTstack;
import com.bluekitchen.btstack.GATTCharacteristic;
import com.bluekitchen.btstack.GATTService;
import com.bluekitchen.btstack.Packet;
import com.bluekitchen.btstack.PacketHandler;
import com.bluekitchen.btstack.Util;
import com.bluekitchen.btstack.event.BTstackEventState;
import com.bluekitchen.btstack.event.GAPLEAdvertisingReport;
import com.bluekitchen.btstack.event.GATTCharacteristicQueryResult;
import com.bluekitchen.btstack.event.GATTCharacteristicValueQueryResult;
import com.bluekitchen.btstack.event.GATTNotification;
import com.bluekitchen.btstack.event.GATTQueryComplete;
import com.bluekitchen.btstack.event.GATTServiceQueryResult;
import com.bluekitchen.btstack.event.HCIEventDisconnectionComplete;
import com.bluekitchen.btstack.event.HCIEventLEConnectionComplete;
public class GATTClientTest implements PacketHandler {
private enum STATE {
w4_btstack_working, w4_scan_result, w4_connected, w4_services_complete, w4_characteristic_complete, w4_characteristic_read
, 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,
w4_acc_data, w4_connected_acc
};
private BTstack btstack;
private STATE state;
private int testAddrType;
private BD_ADDR testAddr;
private int testHandle;
private GATTService testService;
private GATTCharacteristic testCharacteristic;
private int service_count = 0;
private int characteristic_count = 0;
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};
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};
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};
private byte[] acc_enable = new byte[] {1};
private byte acc_notification = 1;
private GATTService accService;
private GATTCharacteristic enableCharacteristic;
private GATTCharacteristic configCharacteristic;
public void handlePacket(Packet packet){
// System.out.println(packet.toString());
if (packet instanceof HCIEventDisconnectionComplete){
HCIEventDisconnectionComplete event = (HCIEventDisconnectionComplete) packet;
testHandle = event.getConnectionHandle();
System.out.println(String.format("Received disconnect, status %d, handle %x", event.getStatus(), testHandle));
return;
}
switch (state){
case w4_btstack_working:
if (packet instanceof BTstackEventState){
BTstackEventState event = (BTstackEventState) packet;
if (event.getState() == 2) {
System.out.println("GAPLEScanStart()");
state = STATE.w4_scan_result;
btstack.GAPLEScanStart();
}
}
break;
case w4_scan_result:
if (packet instanceof GAPLEAdvertisingReport){
GAPLEAdvertisingReport report = (GAPLEAdvertisingReport) packet;
testAddrType = report.getAddressType();
testAddr = report.getAddress();
System.out.println(String.format("Adv: type %d, addr %s", testAddrType, testAddr));
System.out.println(String.format("Data: %s", Util.asHexdump(report.getData())));
System.out.println("GAPLEScanStop()");
btstack.GAPLEScanStop();
System.out.println("GAPLEConnect(...)");
state = STATE.w4_connected_acc;
btstack.GAPLEConnect(testAddrType, testAddr);
}
break;
case w4_connected:
if (packet instanceof HCIEventLEConnectionComplete){
HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet;
testHandle = event.getConnectionHandle();
System.out.println(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle));
state = STATE.w4_services_complete;
System.out.println("GATTDiscoverPrimaryServices(...)");
btstack.GATTDiscoverPrimaryServices(testHandle);
}
break;
case w4_services_complete:
if (packet instanceof GATTServiceQueryResult){
GATTServiceQueryResult event = (GATTServiceQueryResult) packet;
if (testService == null){
System.out.println(String.format("First service UUID %s", event.getService().getUUID()));
testService = event.getService();
}
System.out.println("Service: " + event.getService());
service_count++;
}
if (packet instanceof GATTQueryComplete){
System.out.println(String.format("Service query complete, total %d services", service_count));
state = STATE.w4_characteristic_complete;
btstack.GATTDiscoverCharacteristicsForService(testHandle, testService);
}
break;
case w4_characteristic_complete:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
if (testCharacteristic == null){
System.out.println(String.format("First characteristic UUID %s", event.getCharacteristic().getUUID()));
testCharacteristic = event.getCharacteristic();
}
System.out.println("Characteristic: " + event.getCharacteristic());
characteristic_count++;
}
if (packet instanceof GATTQueryComplete){
System.out.println(String.format("Characteristic query complete, total %d characteristics", characteristic_count));
state = STATE.w4_characteristic_read;
btstack.GATTReadValueOfCharacteristic(testHandle, testCharacteristic);
}
break;
case w4_characteristic_read:
if (packet instanceof GATTCharacteristicValueQueryResult){
System.out.println("Read complete");
System.out.println( packet.toString());
state = STATE.w4_characteristic_write;
byte [] data = { 'B', 'T', 's', 't', 'a', 'c', 'k'};
btstack.GATTWriteValueOfCharacteristic(testHandle, testCharacteristic, data.length, data);
}
break;
case w4_characteristic_write:
if (packet instanceof GATTQueryComplete){
System.out.println("Write complete, search for ACC service");
state = STATE.w4_acc_service_result;
btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(this.acc_service_uuid)); // not working
}
break;
case w4_connected_acc:
if (packet instanceof HCIEventLEConnectionComplete){
HCIEventLEConnectionComplete event = (HCIEventLEConnectionComplete) packet;
testHandle = event.getConnectionHandle();
System.out.println(String.format("Connection complete, status %d, handle %x", event.getStatus(), testHandle));
System.out.println("Search for ACC service");
state = STATE.w4_acc_service_result;
byte [] uuid = new byte[16];
Util.flipX(this.acc_service_uuid, uuid); // works
btstack.GATTDiscoverPrimaryServicesByUUID128(testHandle, new BT_UUID(uuid));
}
break;
case w4_acc_service_result:
System.out.println(String.format("w4_acc_service_result state"));
if (packet instanceof GATTServiceQueryResult){
GATTServiceQueryResult event = (GATTServiceQueryResult) packet;
System.out.println(String.format("ACC service found %s", event.getService().getUUID()));
accService = event.getService();
break;
}
if (packet instanceof GATTQueryComplete){
if (accService == null) {
System.out.println("No acc service found");
break;
}
System.out.println("ACC Service found, searching for acc enable characteristic");
state = STATE.w4_acc_enable_characteristic_result;
byte [] uuid = new byte[16];
Util.flipX(this.acc_chr_enable_uuid, uuid);
btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid));
}
break;
case w4_acc_enable_characteristic_result:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
enableCharacteristic = event.getCharacteristic();
System.out.println("Enable ACC Characteristic found ");
}
if (packet instanceof GATTQueryComplete){
if (enableCharacteristic == null) {
System.out.println("No acc enable chr found");
break;
}
System.out.println("Write enable acc characteristic");
state = STATE.w4_write_acc_enable_result;
btstack.GATTWriteValueOfCharacteristic(testHandle, enableCharacteristic, 1, this.acc_enable);
}
break;
case w4_write_acc_enable_result:
if (packet instanceof GATTQueryComplete){
System.out.println("Acc enabled,searching for acc client config characteristic");
byte [] uuid = new byte[16];
Util.flipX(this.acc_chr_client_config_uuid, uuid);
btstack.GATTDiscoverCharacteristicsForServiceByUUID128(testHandle, accService, new BT_UUID(uuid));
state = STATE.w4_acc_client_config_characteristic_result;
}
break;
case w4_acc_client_config_characteristic_result:
if (packet instanceof GATTCharacteristicQueryResult){
GATTCharacteristicQueryResult event = (GATTCharacteristicQueryResult) packet;
configCharacteristic = event.getCharacteristic();
System.out.println("ACC Client Config Characteristic found");
}
if (packet instanceof GATTQueryComplete){
if (configCharacteristic == null) {
System.out.println("No acc config chr found");
break;
}
System.out.println("Write ACC Client Config Characteristic");
state = STATE.w4_acc_data;
btstack.GATTWriteClientCharacteristicConfiguration(testHandle, configCharacteristic, this.acc_notification);
}
break;
case w4_acc_data:
if (packet instanceof GATTQueryComplete){
System.out.println("Acc configured for notification");
break;
}
if (packet instanceof GATTNotification){
System.out.println("Acc Value");
System.out.println(packet.toString());
btstack.GAPDisconnect(testHandle);
}
default:
break;
}
}
void test(){
System.out.println("LE Test Application");
// connect to BTstack Daemon via default port on localhost
btstack = new BTstack();
btstack.setTcpPort(BTstack.DEFAULT_TCP_PORT);
btstack.registerPacketHandler(this);
boolean ok = btstack.connect();
if (!ok) {
System.out.println("Failed to connect to BTstack Server");
return;
}
System.out.println("BTstackSetPowerMode(1)");
state = STATE.w4_btstack_working;
btstack.BTstackSetPowerMode(1);
}
public static void main(String args[]){
new GATTClientTest().test();
}
}

434
java/generate.py Executable file
View File

@ -0,0 +1,434 @@
#!/usr/bin/env python
# BlueKtichen GmbH (c) 2014
import glob
import re
import sys
import os
print '''
Java binding generator for BTstack
Copyright 2014, BlueKitchen GmbH
'''
# com.bluekitchen.btstack.BTstack.java templates
java_btstack_header = \
'''/**
* BTstack Client Library
*/
package %s;
public class BTstack extends BTstackClient {
'''
java_btstack_command = '''
public boolean %s(%s){
// %s
int command_len = %s;
byte[] command = new byte[command_len];
Util.storeBt16(command, 0, Util.opcode(%s, %s));
int offset = 2;
Util.storeByte(command, offset, command_len - 3);
offset++;
%s
Packet packet = new Packet(Packet.HCI_COMMAND_PACKET, 0, command, command.length);
return sendPacket(packet);
}
'''
java_btstack_footer = '''
}
'''
# com.bluekitchen.btstack.EventFactory template
java_event_factory_template = \
'''package {0};
import {0}.event.*;
public class EventFactory {{
/** @brief event codes */
{1}
public static Event eventForPacket(Packet packet){{
int eventType = Util.readByte(packet.getBuffer(), 0);
switch (eventType){{
{2}
case 0x3e: // LE_META_EVENT
int subEventType = Util.readByte(packet.getBuffer(), 2);
switch (subEventType){{
{3}
default:
return new Event(packet);
}}
default:
return new Event(packet);
}}
}}
}}
'''
java_event_factory_event = '''
case {0}:
return new {1}(packet);
'''
java_event_factory_subevent = '''
case {0}:
return new {1}(packet);
'''
# com.bluekitchen.btstack.events.* template
java_event_template = \
'''package {0}.event;
import {0}.*;
public class {1} extends Event {{
public {1}(Packet packet) {{
super(packet);
}}
{2}
{3}
}}
'''
java_event_getter = \
'''
public {0} get{1}(){{
{2}
}}
'''
java_event_getter_data = \
'''int len = get{0}();
byte[] result = new byte[len];
System.arraycopy(data, {1}, result, 0, len);
return result;'''
java_event_to_string = \
'''
public String toString(){{
StringBuffer t = new StringBuffer();
t.append("{0} < type = ");
t.append(String.format("0x%02x, ", getEventType()));
t.append(getEventType());
{1} t.append(" >");
return t.toString();
}}
'''
# global variables/defines
package='com.bluekitchen.btstack'
gen_path = 'gen/' + package.replace('.', '/')
hci_cmds_h_path = '../include/btstack/hci_cmds.h'
hci_cmds_c_path = '../src/hci_cmds.c'
hci_h_path = '../src/hci.h'
defines = dict()
defines_used = set()
def assert_dir(path):
if not os.access(path, os.R_OK):
os.makedirs(path)
def cap(x):
if x.lower() == 'btstack':
return 'BTstack'
acronyms = ['GAP', 'GATT', 'HCI', 'L2CAP', 'LE', 'RFCOMM', 'SM', 'SDP', 'UUID16', 'UUID128']
if x.upper() in acronyms:
return x.upper()
return x.capitalize()
def camel_case(name):
return ''.join(map(cap, name.split('_')))
def camel_case_var(name):
if name in ['uuid128', 'uuid16']:
return name
camel = camel_case(name)
return camel[0].lower() + camel[1:]
def read_defines(infile):
global defines
with open (infile, 'rb') as fin:
for line in fin:
parts = re.match('#define\s+(\w+)\s+(\w*)',line)
if parts and len(parts.groups()) == 2:
(key, value) = parts.groups()
defines[key] = value
def java_type_for_btstack_type(type):
param_types = { '1' : 'int', '2' : 'int', '3' : 'int', '4' : 'long', 'H' : 'int', 'B' : 'BD_ADDR',
'D' : 'byte []', 'E' : 'byte [] ', 'N' : 'String' , 'P' : 'byte []', 'A' : 'byte []', 'S' : 'byte []',
'J' : 'int', 'L' : 'int', 'V' : 'byte []', 'U' : 'BT_UUID',
'X' : 'GATTService', 'Y' : 'GATTCharacteristic', 'Z' : 'GATTCharacteristicDescriptor' }
return param_types[type]
def size_for_type(type):
param_sizes = { '1' : 1, '2' : 2, '3' : 3, '4' : 4, 'H' : 2, 'B' : 6, 'D' : 8, 'E' : 240, 'N' : 248, 'P' : 16,
'A' : 31, 'S' : -1, 'V': -1, 'J' : 1, 'L' : 2, 'U' : 16, 'X' : 20, 'Y' : 24, 'Z' : 18}
return param_sizes[type]
def create_command_java(fout, name, ogf, ocf, format, params):
global java_btstack_command
ind = ' '
param_store = {
'1' : 'Util.storeByte(command, offset, %s);',
'J' : 'Util.storeByte(command, offset, %s);',
'2' : 'Util.storeBt16(command, offset, %s);',
'H' : 'Util.storeBt16(command, offset, %s);',
'L' : 'Util.storeBt16(command, offset, %s);',
'3' : 'Util.storeBt24(command, offset, %s);',
'4' : 'Util.storeBt32(command, offset, %s);',
'D' : 'Util.storeBytes(command, offset, %s, 8);',
'E' : 'Util.storeBytes(command, offset, %s, 240);',
'P' : 'Util.storeBytes(command, offset, %s, 16);',
'A' : 'Util.storeBytes(command, offset, %s, 31);',
'S' : 'Util.storeBytes(command, offset, %s);',
'B' : 'Util.storeBytes(command, offset, %s.getBytes());',
'U' : 'Util.storeBytes(command, offset, %s.getBytes());',
'X' : 'Util.storeBytes(command, offset, %s.getBytes());',
'Y' : 'Util.storeBytes(command, offset, %s.getBytes());',
'Z' : 'Util.storeBytes(command, offset, %s.getBytes());',
'N' : 'Util.storeString(command, offset, %s, 248);',
}
# method arguments
arg_counter = 1
args = []
for param_type, arg_name in zip(format, params):
arg_type = java_type_for_btstack_type(param_type)
arg_size = size_for_type(param_type)
arg = (param_type, arg_type, arg_size, arg_name)
args.append(arg)
arg_counter += 1
# method argument declaration
args2 = []
for arg in args:
args2.append('%s %s' % (arg[1], arg[3]))
args_string = ', '.join(args2)
# command size (opcode, len)
size_fixed = 3
size_var = ''
for arg in args:
size = arg[2]
if size > 0:
size_fixed += size
else:
size_var += ' + %s.length' % arg[3]
size_string = '%u%s' % (size_fixed, size_var)
store_params = ''
length_name = ''
for (param_type, arg_type, arg_size, arg_name) in args:
if param_type in ['L', 'J']:
length_name = arg_name
if param_type == 'V':
store_params += ind + 'Util.storeBytes(command, offset, %s, %s);' % (arg_name, length_name) + '\n';
store_params += ind + 'offset += %s;\n' % length_name;
length_name = ''
else:
store_params += ind + (param_store[param_type] % arg_name) + '\n';
size = arg_size
if size > 0:
store_params += ind + 'offset += %u;\n' % arg_size;
else:
store_params += ind + 'offset += %s.length;\n' % arg_name
fout.write( java_btstack_command % (name, args_string, format, size_string, ogf, ocf, store_params))
def mark_define_as_used(term):
if term.startswith('0'):
return
defines_used.add(term)
def java_define_string(key):
global defines
return ' public static final int %s = %s;\n' % (key, defines[key])
def java_defines_string(keys):
return '\n'.join( map(java_define_string, sorted(keys)))
def parse_commands(infile):
global gen_path
assert_dir(gen_path)
outfile = '%s/BTstack.java' % gen_path
with open(outfile, 'w') as fout:
fout.write(java_btstack_header % package)
with open (infile, 'rb') as fin:
params = []
for line in fin:
parts = re.match('.*@param\s*(\w*)\s*', line)
if parts and len(parts.groups()) == 1:
param = parts.groups()[0]
params.append(camel_case_var(param))
continue
declaration = re.match('const\s+hci_cmd_t\s+(\w+)[\s=]+', line)
if declaration:
command_name = camel_case(declaration.groups()[0])
if command_name.endswith('Cmd'):
command_name = command_name[:-len('Cmd')]
continue
definition = re.match('\s*OPCODE\\(\s*(\w+)\s*,\s+(\w+)\s*\\)\s*,\s\\"(\w*)\\".*', line)
if definition:
(ogf, ocf, format) = definition.groups()
if len(params) != len(format):
params = []
arg_counter = 1
for f in format:
arg_name = 'arg%u' % arg_counter
params.append(arg_name)
arg_counter += 1
create_command_java(fout, command_name, ogf, ocf, format, params);
mark_define_as_used(ogf)
mark_define_as_used(ocf)
params = []
continue
fout.write('\n /** defines used */\n\n')
for key in sorted(defines_used):
fout.write(java_define_string(key))
fout.write(java_btstack_footer)
def create_event(event_name, format, args):
global gen_path
global package
global java_event_template
param_read = {
'1' : 'return Util.readByte(data, %u);',
'J' : 'return Util.readByte(data, %u);',
'2' : 'return Util.readBt16(data, %u);',
'3' : 'return Util.readBt24(data, %u);',
'4' : 'return Util.readBt32(data, %u);',
'H' : 'return Util.readBt16(data, %u);',
'L' : 'return Util.readByte(data, %u);',
'B' : 'return Util.readBdAddr(data, %u);',
'X' : 'return Util.readGattService(data, %u);',
'Y' : 'return Util.readGattCharacteristic(data, %u);',
'Z' : 'return Util.readGattCharacteristicDescriptor(data, %u);',
# 'D' : 'Util.storeBytes(data, %u, 8);',
# 'E' : 'Util.storeBytes(data, %u, 240);',
# 'N' : 'Util.storeString(data, %u, 248);',
# 'P' : 'Util.storeBytes(data, %u, 16);',
# 'A' : 'Util.storeBytes(data, %u, 31);',
# 'S' : 'Util.storeBytes(data, %u);'
}
gen_event_path = '%s/event' % gen_path
outfile = '%s/%s.java' % (gen_event_path, event_name)
with open(outfile, 'w') as fout:
offset = 2
getters = ''
length_name = ''
for f, arg in zip(format, args):
# just remember name
if f in ['L','J']:
length_name = camel_case(arg)
if f == 'V':
access = java_event_getter_data.format(length_name, offset)
size = 0
else:
access = param_read[f] % offset
size = size_for_type(f)
getters += java_event_getter.format(java_type_for_btstack_type(f), camel_case(arg), access)
offset += size
to_string_args = ''
for arg in args:
to_string_args += ' t.append(", %s = ");\n' % arg
to_string_args += ' t.append(get%s());\n' % camel_case(arg)
to_string_method = java_event_to_string.format(event_name, to_string_args)
fout.write(java_event_template.format(package, event_name, getters, to_string_method))
def create_events(events):
global gen_path
gen_path_events = gen_path + '/event'
assert_dir(gen_path_events)
for event_type, event_name, format, args in events:
event_name = camel_case(event_name)
create_event(event_name, format, args)
def create_event_factory(events, le_events, defines):
global gen_path
global package
global java_event_factory_event
global java_event_factory_template
outfile = '%s/EventFactory.java' % gen_path
cases = ''
for event_type, event_name, format, args in events:
event_name = camel_case(event_name)
cases += java_event_factory_event.format(event_type, event_name)
subcases = ''
for event_type, event_name, format, args in le_events:
event_name = camel_case(event_name)
subcases += java_event_factory_subevent.format(event_type, event_name)
with open(outfile, 'w') as fout:
defines_text = java_defines_string(defines)
fout.write(java_event_factory_template.format(package, defines_text, cases, subcases))
def parse_events(path):
global gen_path
events = []
le_events = []
params = []
event_types = set()
format = None
with open (path, 'rb') as fin:
for line in fin:
parts = re.match('.*@format\s*(\w*)\s*', line)
if parts and len(parts.groups()) == 1:
format = parts.groups()[0]
parts = re.match('.*@param\s*(\w*)\s*', line)
if parts and len(parts.groups()) == 1:
param = parts.groups()[0]
params.append(param)
parts = re.match('#define\s+(\w+)\s+(\w*)',line)
if parts and len(parts.groups()) == 2:
(key, value) = parts.groups()
if format != None:
if key.lower().startswith('hci_subevent_'):
le_events.append((value, key.lower().replace('hci_subevent_', 'hci_event_'), format, params))
else:
events.append((value, key, format, params))
event_types.add(key)
params = []
format = None
return (events, le_events, event_types)
# # read defines from hci_cmds.h and hci.h
read_defines(hci_cmds_h_path)
read_defines(hci_h_path)
# # parse commands and generate BTstack.java
parse_commands(hci_cmds_c_path)
# parse hci.h to get used events
(events, le_events, event_types) = parse_events(hci_cmds_h_path)
# create events, le meta events, and event factory
create_events(events)
create_events(le_events)
create_event_factory(events, le_events, event_types)
# done
print 'Done!'

View File

@ -0,0 +1,42 @@
package com.bluekitchen.btstack;
public class BD_ADDR {
public static final int LEN = 6;
byte address[] = new byte[LEN];
public byte[] getBytes(){
return address;
}
public BD_ADDR(String text){
String[] parts = text.split(":");
if (parts.length != LEN) return;
for (int i = 0; i < LEN ; i++){
Util.storeByte(address, LEN - 1 - i, Integer.parseInt(parts[i], 16));
}
}
public BD_ADDR(byte address[]){
if (address.length != LEN) return;
System.arraycopy(address, 0, this.address, 0, LEN);
}
public String toString(){
StringBuffer buffer = new StringBuffer();
for (int i = LEN - 1 ; i >= 0 ; i--){
if (i != LEN - 1){
buffer.append(":");
}
buffer.append(String.format("%02X", Util.readByte(address, i)));
}
return buffer.toString();
}
public static void main(String args[]){
BD_ADDR addr = new BD_ADDR("11:22:33:44:55:66");
Util.hexdump(addr.getBytes());
System.out.println( addr.toString());
}
}

View File

@ -0,0 +1,86 @@
package com.bluekitchen.btstack;
import java.util.Arrays;
// UUID stored in big endian format
// TODO store UUID internally as LE
public class BT_UUID {
private static final byte BluetoothBaseUUID[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */
(byte)0x80, 0x00, /* - */ 0x00, (byte)0x80, 0x5F, (byte)0x9B, 0x34, (byte)0xFB };
byte [] uuid = new byte[16];
public BT_UUID(long uuid32){
System.arraycopy(BluetoothBaseUUID, 0, uuid, 0, 16);
Util.storeNet32(uuid, 0, uuid32);
}
public BT_UUID(byte[] uuid128LE){
Util.flipX(uuid128LE, uuid);
}
public BT_UUID(String uuidString){
String parts[] = uuidString.split("-");
if (parts.length != 5) return;
Util.storeNet32(uuid, 0, Long.parseLong(parts[0], 16));
Util.storeNet32(uuid, 4, Long.parseLong(parts[1]+parts[2], 16));
Util.storeNet32(uuid, 8, Long.parseLong((parts[3]+parts[4]).substring(0, 8), 16));
Util.storeNet32(uuid, 12, Long.parseLong((parts[3]+parts[4]).substring(8, 16), 16));
}
public String toString(){
return String.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
}
public long getUUID32(){
for (int i = 4; i < 16 ; i++){
if (uuid[i] != BluetoothBaseUUID[i]) return 0;
}
return Util.readNet32(uuid, 0);
}
public byte[] getBytes(){
byte result[] = new byte[16];
Util.flipX(uuid, result);
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(uuid);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof BT_UUID)) {
return false;
}
BT_UUID other = (BT_UUID) obj;
if (!Arrays.equals(uuid, other.uuid)) {
return false;
}
return true;
}
public static void main (String arg[]){
BT_UUID uuid = new BT_UUID(0x2800);
System.out.println("0x2800 = " + uuid);
System.out.println("32bit UUID = " + uuid.getUUID32());
String uuidString = uuid.toString();
BT_UUID uuid2 = new BT_UUID(uuidString);
System.out.println("equal " + uuid.equals(uuid2));
}
}

View File

@ -0,0 +1,99 @@
package com.bluekitchen.btstack;
public class BTstackClient {
/**
* BTstack Server Client
* uses background receive thread
*/
public static final int DEFAULT_TCP_PORT = 13333;
public static final String DEFAULT_UNIX_SOCKET = "/tmp/BTstack";
private volatile SocketConnection socketConnection;
private PacketHandler packetHandler;
private boolean connected;
private Thread rxThread;
private String unixDomainSocketPath;
private int tcpPort;
public BTstackClient(){
connected = false;
socketConnection = null;
rxThread = null;
}
public void setUnixDomainSocketPath(String path){
this.unixDomainSocketPath = path;
}
public void setTcpPort(int port){
this.tcpPort = port;
}
public void registerPacketHandler(PacketHandler packetHandler){
this.packetHandler = packetHandler;
}
public boolean connect(){
if (tcpPort == 0){
try {
Class<?> clazz = Class.forName("com.bluekitchen.btstack.SocketConnectionUnix");
socketConnection = (SocketConnection) clazz.newInstance();
if (unixDomainSocketPath != null){
socketConnection.setUnixDomainSocketPath(unixDomainSocketPath);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
return false;
} catch (InstantiationException e) {
e.printStackTrace();
return false;
} catch (IllegalAccessException e) {
e.printStackTrace();
return false;
}
} else {
// TODO implement SocketConnectionTcp
socketConnection = new SocketConnectionTCP();
socketConnection.setTcpPort(tcpPort);
}
connected = socketConnection.connect();
if (!connected) return false;
rxThread = new Thread(new Runnable(){
@Override
public void run() {
while (socketConnection != null && !Thread.currentThread().isInterrupted()){
Packet packet = socketConnection.receivePacket();
if (packet == null) break;
if (packet.getPacketType() == Packet.HCI_EVENT_PACKET){
packetHandler.handlePacket(EventFactory.eventForPacket(packet));
continue;
}
packetHandler.handlePacket(packet);
}
System.out.println("Rx Thread: Disconnected");
}
});
rxThread.start();
return true;
}
public boolean sendPacket(Packet packet){
if (socketConnection == null) return false;
return socketConnection.sendPacket(packet);
}
public void disconnect(){
if (socketConnection == null) return;
if (rxThread == null) return;
rxThread.interrupt();
socketConnection.disconnect();
socketConnection = null;
}
}

View File

@ -0,0 +1,23 @@
package com.bluekitchen.btstack;
public class Event extends Packet {
public Event(byte data[], int payloadLen){
super(HCI_EVENT_PACKET, 0, data, payloadLen);
}
public Event(Packet packet){
super(HCI_EVENT_PACKET, 0, packet.getBuffer(), packet.getPayloadLen());
}
public int getEventType(){
return Util.readByte(data, 0);
}
public String toString(){
StringBuffer t = new StringBuffer();
t.append(String.format("Event type %d, len %d: ", getEventType(), getPayloadLen()));
t.append(Util.asHexdump(data, payloadLen));
return t.toString();
}
}

View File

@ -0,0 +1,55 @@
package com.bluekitchen.btstack;
import java.util.Arrays;
public class GATTCharacteristic {
/**
uint16_t start_handle;
uint16_t value_handle;
uint16_t end_handle;
uint16_t properties;
uint8_t uuid128[16];
*/
public static final int LEN = 24;
byte data[] = new byte[LEN];
public byte[] getBytes(){
return data;
}
public GATTCharacteristic(byte address[]){
if (address.length != LEN) return;
System.arraycopy(address, 0, this.data, 0, LEN);
}
public int getStartHandle(){
return Util.readBt16(data, 0);
}
public int getValueHandle(){
return Util.readBt16(data, 2);
}
public int getEndHandle(){
return Util.readBt16(data, 4);
}
public int getProperties(){
return Util.readBt16(data, 6);
}
public BT_UUID getUUID(){
return new BT_UUID(Arrays.copyOfRange(data, 8, 8 + 16));
}
@Override
public String toString() {
return "GattCharacteristic [getStartHandle()=" + getStartHandle()
+ ", getValueHandle()=" + getValueHandle()
+ ", getEndHandle()=" + getEndHandle() + ", getProperties()="
+ getProperties() + ", getUUID()=" + getUUID() + "]";
}
}

View File

@ -0,0 +1,38 @@
package com.bluekitchen.btstack;
import java.util.Arrays;
public class GATTCharacteristicDescriptor {
/**
uint16_t handle;
uint8_t uuid128[16];
*/
public static final int LEN = 18;
byte data[] = new byte[LEN];
public byte[] getBytes(){
return data;
}
public GATTCharacteristicDescriptor(byte address[]){
if (address.length != LEN) return;
System.arraycopy(address, 0, this.data, 0, LEN);
}
public int getHandle(){
return Util.readBt16(data, 0);
}
public BT_UUID getUUID(){
return new BT_UUID(Arrays.copyOfRange(data, 2, 2 + 16));
}
@Override
public String toString() {
return "GattCharacteristicDescriptor [getHandle()=" + getHandle()
+ ", getUUID()=" + getUUID() + "]";
}
}

View File

@ -0,0 +1,44 @@
package com.bluekitchen.btstack;
import java.util.Arrays;
public class GATTService {
/**
uint16_t start_group_handle;
uint16_t end_group_handle;
uint8_t uuid128[16];
*/
public static final int LEN = 20;
byte data[] = new byte[LEN];
public byte[] getBytes(){
return data;
}
public GATTService(byte data[]){
if (data.length != LEN) return;
System.arraycopy(data, 0, this.data, 0, LEN);
}
public int getStartGroupHandle(){
return Util.readBt16(data, 0);
}
public int getEndGroupHandle(){
return Util.readBt16(data, 2);
}
public BT_UUID getUUID(){
return new BT_UUID(Arrays.copyOfRange(data, 4, 4 + 16));
}
@Override
public String toString() {
return "GattService [getStartGroupHandle()=" + getStartGroupHandle()
+ ", getEndGroupHandle()=" + getEndGroupHandle()
+ ", getUUID()=" + getUUID() + "]";
}
}

View File

@ -0,0 +1,48 @@
package com.bluekitchen.btstack;
public class Packet {
public static final int HCI_COMMAND_PACKET = 1;
public static final int HCI_EVENT_PACKET = 4;
protected byte[] data;
protected int payloadLen;
protected int packetType;
protected int channel;
public int getPacketType() {
return packetType;
}
public byte[] getBuffer() {
return data;
}
public int getPayloadLen(){
return payloadLen;
}
public int getChannel() {
return channel;
}
public Packet(int packetType, int channel, byte[] buffer, int payloadLen){
this.packetType = packetType;
this.channel = channel;
this.data = new byte[payloadLen];
System.arraycopy(buffer, 0, this.data, 0, payloadLen);
this.data = buffer;
this.payloadLen = payloadLen;
}
public String toString(){
StringBuffer t = new StringBuffer();
t.append(String.format("Packet %d, channel %d, len %d: ", packetType, channel, payloadLen));
t.append(Util.asHexdump(data, payloadLen));
return t.toString();
}
public void dump(){
System.out.println(this.toString());
}
}

View File

@ -0,0 +1,5 @@
package com.bluekitchen.btstack;
public interface PacketHandler {
void handlePacket(Packet packet);
}

View File

@ -0,0 +1,18 @@
package com.bluekitchen.btstack;
public abstract class SocketConnection {
public abstract boolean connect();
public abstract boolean sendPacket(Packet packet);
public abstract Packet receivePacket();
public abstract void disconnect();
public void setUnixDomainSocketPath(String path) {
}
public void setTcpPort(int port) {
}
}

View File

@ -0,0 +1,104 @@
package com.bluekitchen.btstack;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class SocketConnectionTCP extends SocketConnection {
private int port;
private Socket socket;
private InputStream in;
private OutputStream out;
private byte inHeader[] = new byte[6];
private byte inPayload[] = new byte[2000];
public SocketConnectionTCP(){
socket = null;
}
public void setTcpPort(int port) {
this.port = port;
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#connect()
*/
@Override
public boolean connect() {
try {
socket = new Socket("localhost", port);
in = socket.getInputStream();
out = socket.getOutputStream();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#sendPacket(com.bluekitchen.btstack.Packet)
*/
@Override
public boolean sendPacket(Packet packet) {
if (out == null) return false;
try {
System.out.println("Send "); Util.hexdump(packet.getBuffer(), packet.getPayloadLen());
out.write(headerForPacket(packet));
out.write(packet.getBuffer());
out.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#receivePacket()
*/
@Override
public Packet receivePacket() {
if (in == null) return null;
int bytes_read = Util.readExactly(in, inHeader, 0, 6);
if (bytes_read != 6) return null;
int packetType = Util.readBt16(inHeader, 0);
int channel = Util.readBt16(inHeader, 2);
int len = Util.readBt16(inHeader, 4);
Util.readExactly(in, inPayload, 0, len);
Packet packet = new Packet(packetType, channel ,inPayload, len);
return packet;
}
/* (non-Javadoc)
* @see com.bluekitchen.btstack.SocketConnection#disconnect()
*/
@Override
public void disconnect() {
if (socket != null){
try {
socket.close();
} catch (IOException e) {
}
}
}
private byte[] headerForPacket(Packet packet) {
byte header[] = new byte[6];
Util.storeBt16(header, 0, packet.getPacketType());
Util.storeBt16(header, 2, packet.getChannel());
Util.storeBt16(header, 4, packet.getBuffer().length);
return header;
}
}

View File

@ -0,0 +1,146 @@
package com.bluekitchen.btstack;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Util {
public static int opcode(int ogf, int ocf){
return ocf | (ogf << 10);
}
public static int readByte(byte[] buffer, int offset){
int data = buffer[offset];
if (data >= 0) return data;
return data + 256;
}
public static int readBt16(byte[] buffer, int offset){
return readByte(buffer, offset) | (readByte(buffer, offset + 1) << 8);
}
public static int readBt24(byte[] buffer, int offset){
return readByte(buffer, offset) | (readByte(buffer, offset + 1) << 8) | (readByte(buffer, offset + 2) << 16) ;
}
public static long readBt32(byte[] buffer, int offset){
return (((long) readByte(buffer, offset+3)) << 24) | readByte(buffer, offset) | (readByte(buffer, offset + 1) << 8) | (readByte(buffer, offset + 2) << 16) ;
}
public static BD_ADDR readBdAddr(byte[] buffer, int offset){
return new BD_ADDR(Arrays.copyOfRange(buffer, offset, offset + BD_ADDR.LEN));
}
public static GATTService readGattService(byte[] buffer, int offset){
return new GATTService(Arrays.copyOfRange(buffer, offset, offset + GATTService.LEN));
}
public static GATTCharacteristic readGattCharacteristic(byte[] buffer, int offset){
return new GATTCharacteristic(Arrays.copyOfRange(buffer, offset, offset + GATTCharacteristic.LEN));
}
public static GATTCharacteristicDescriptor readGattCharacteristicDescriptor(byte[] buffer, int offset){
return new GATTCharacteristicDescriptor(Arrays.copyOfRange(buffer, offset, offset + GATTCharacteristicDescriptor.LEN));
}
public static int readExactly(InputStream in, byte[] buffer, int offset, int len){
int readTotal = 0;
try {
while (len > 0){
int read;
read = in.read(buffer, offset, len);
if (read < 0) break;
len -= read;
offset += read;
readTotal += read;
}
} catch (IOException e) {
}
return readTotal;
}
public static void storeByte(byte[] buffer, int offset, int value){
buffer[offset] = (byte) value;
}
public static void storeBt16(byte[] buffer, int offset, int value){
storeByte(buffer, offset, value & 0xff);
storeByte(buffer, offset+1, value >> 8);
}
public static void storeBt24(byte[] buffer, int offset, int value){
storeByte(buffer, offset, value & 0xff);
storeByte(buffer, offset+1, value >> 8);
storeByte(buffer, offset+2, value >> 16);
}
public static void storeBt32(byte[] buffer, int offset, long value){
storeByte(buffer, offset, (int) (value & 0xff));
storeByte(buffer, offset+1, (int) (value >> 8));
storeByte(buffer, offset+2, (int) (value >> 16));
storeByte(buffer, offset+3, (int) (value >> 24));
}
public static void storeBytes(byte[] buffer, int offset, byte[] value) {
System.arraycopy(value, 0, buffer, offset, value.length);
}
public static void storeBytes(byte[] buffer, int offset, byte[] value, int len) {
int bytes_to_copy = Math.min(value.length, len);
System.arraycopy(value, 0, buffer, offset, bytes_to_copy);
for (int i = bytes_to_copy; i < len ; i++){
buffer[i] = 0;
}
}
public static void storeString(byte[] buffer, int offset, String value, int len) {
byte data[] = new byte[0];
try {
data = value.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
storeBytes(buffer, offset, data, len);
}
public static String asHexdump(byte[] buffer, int len){
StringBuffer t = new StringBuffer();
for (int i = 0; i < len ; i++){
t.append(String.format("0x%02x, ", readByte(buffer, i)));
}
return t.toString();
}
public static String asHexdump(byte[] buffer){
return asHexdump(buffer, buffer.length);
}
public static void hexdump(byte[] buffer, int len){
System.out.println(asHexdump(buffer, len));
System.out.println();
}
public static void hexdump(byte[] buffer){
hexdump(buffer, buffer.length);
}
public static void flipX(byte src[], byte dst[]){
int len = src.length;
if (len != dst.length) return;
for (int i = 0; i < len ; i++) {
dst[len - 1 - i] = src[i];
}
}
public static void storeNet32(byte[] buffer, int offset, long value) {
storeByte(buffer, offset+3, (int) (value & 0xff));
storeByte(buffer, offset+2, (int) (value >> 8));
storeByte(buffer, offset+1, (int) (value >> 16));
storeByte(buffer, offset, (int) (value >> 24));
}
public static long readNet32(byte[] buffer, int offset) {
return (((long) readByte(buffer, offset)) << 24) | (readByte(buffer, offset + 1) << 16) | (readByte(buffer, offset + 2) << 8) | readByte(buffer, offset + 3);
}
}