mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-29 04:20:20 +00:00
Java Client Library for BTstack Daemon
This commit is contained in:
parent
c51407e08f
commit
b9d53f0c6c
103
java/android/com/bluekitchen/btstack/SocketConnectionUnix.java
Normal file
103
java/android/com/bluekitchen/btstack/SocketConnectionUnix.java
Normal 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;
|
||||
}
|
||||
}
|
296
java/android/com/bluekitchen/lescan/MainActivity.java
Normal file
296
java/android/com/bluekitchen/lescan/MainActivity.java
Normal 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
53
java/build.xml
Normal 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>
|
264
java/example/com/bluekitchen/GATTClientTest.java
Normal file
264
java/example/com/bluekitchen/GATTClientTest.java
Normal 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
434
java/generate.py
Executable 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!'
|
42
java/src/com/bluekitchen/btstack/BD_ADDR.java
Normal file
42
java/src/com/bluekitchen/btstack/BD_ADDR.java
Normal 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());
|
||||
}
|
||||
}
|
86
java/src/com/bluekitchen/btstack/BT_UUID.java
Normal file
86
java/src/com/bluekitchen/btstack/BT_UUID.java
Normal 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));
|
||||
}
|
||||
}
|
99
java/src/com/bluekitchen/btstack/BTstackClient.java
Normal file
99
java/src/com/bluekitchen/btstack/BTstackClient.java
Normal 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;
|
||||
}
|
||||
}
|
23
java/src/com/bluekitchen/btstack/Event.java
Normal file
23
java/src/com/bluekitchen/btstack/Event.java
Normal 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();
|
||||
}
|
||||
}
|
55
java/src/com/bluekitchen/btstack/GATTCharacteristic.java
Normal file
55
java/src/com/bluekitchen/btstack/GATTCharacteristic.java
Normal 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() + "]";
|
||||
}
|
||||
}
|
@ -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() + "]";
|
||||
}
|
||||
}
|
44
java/src/com/bluekitchen/btstack/GATTService.java
Normal file
44
java/src/com/bluekitchen/btstack/GATTService.java
Normal 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() + "]";
|
||||
}
|
||||
}
|
48
java/src/com/bluekitchen/btstack/Packet.java
Normal file
48
java/src/com/bluekitchen/btstack/Packet.java
Normal 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());
|
||||
}
|
||||
}
|
5
java/src/com/bluekitchen/btstack/PacketHandler.java
Normal file
5
java/src/com/bluekitchen/btstack/PacketHandler.java
Normal file
@ -0,0 +1,5 @@
|
||||
package com.bluekitchen.btstack;
|
||||
|
||||
public interface PacketHandler {
|
||||
void handlePacket(Packet packet);
|
||||
}
|
18
java/src/com/bluekitchen/btstack/SocketConnection.java
Normal file
18
java/src/com/bluekitchen/btstack/SocketConnection.java
Normal 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) {
|
||||
}
|
||||
}
|
104
java/src/com/bluekitchen/btstack/SocketConnectionTCP.java
Normal file
104
java/src/com/bluekitchen/btstack/SocketConnectionTCP.java
Normal 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;
|
||||
}
|
||||
}
|
146
java/src/com/bluekitchen/btstack/Util.java
Normal file
146
java/src/com/bluekitchen/btstack/Util.java
Normal 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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user