Hello,
I am trying to write a JNA mapping for the SimpleBLE Library on macOS. In order to call the native c functions I need to, I have created JNA struct mappings of the structs here. I have also confirmed that the SimpleBLE library itself works properly, with a small c application.
##################################################################################
E.g. Structs:
public static class uuid_t extends Structure {
private static final List<String> FIELDS = Arrays.asList("value");
public byte[] value = new byte[37]; // SIMPLEBLE_UUID_STR_LEN
@Override
protected List<String> getFieldOrder(){
return FIELDS;
}
}
public static class descriptor_t extends Structure {
private static final List<String> FIELDS = Arrays.asList("uuid");
public uuid_t uuid;
@Override
protected List<String> getFieldOrder(){
return FIELDS;
}
}
public class characteristic_t extends Structure {
private static final List<String> FIELDS = Arrays.asList(
"uuid",
"can_read",
"can_write_request",
"can_write_command",
"can_notify",
"can_indicate",
"descriptor_count",
"descriptors"
);
public uuid_t uuid;
public byte can_read;
public byte can_write_request;
public byte can_write_command;
public byte can_notify;
public byte can_indicate;
public long descriptor_count;
public descriptor_t[] descriptors = new descriptor_t[16];
@Override
protected List<String> getFieldOrder(){
return FIELDS;
}
}
public class service_t extends Structure {
private static final List<String> FIELDS = Arrays.asList(
"uuid",
"data_length",
"data",
"characteristic_count",
"characteristics"
);
public uuid_t uuid;
public long data_length;
public byte[] data = new byte[27];
public long characteristic_count;
public characteristic_t[] characteristics = new characteristic_t[16];
@Override
protected List<String> getFieldOrder(){
return FIELDS;
}
##################################################################################
When calling the "simpleble_peripheral_write_command" function with service_uuid and characteristic_uuid structs as defined above (and actually set by the SimpleBLE dylib call to "simpleble_peripheral_services_get" function), the struct arguments seem to take up more space in memory than what the SimpleBLE dylib expects. I have concluded this by attaching an lldb debugger (output screenshot below) which shows the value of the arguments when write_command is called.
This is how I am setting my structs and calling write_command:
// Data to send
byte[] data = new byte[]{0x8a, 0x03, 0xff};
// service_t struct
libsimpleble.service_t service_t = new libsimpleble.service_t();
// Populate the service_t struct with services and characteristics
// index 0 = 65333333-A115-11E2-9E9A-0800200CA100 (service I want to use)
boolean success = !simpleble.getPeripheralServices(peripheral, 0, service_t);
// [1] = 65333333-A115-11E2-9E9A-0800200CA102 (characteristic I want to use)
libsimpleble.characteristic_t characteristic_t = service_t.characteristics[1];
// setting the function arguments to the UUID struct of the service/characteristics (has one field "value" which is called by SimpleBLE dylib
libsimpleble.uuid_t service_uuid = service_t.uuid;
libsimpleble.uuid_t characteristic_uuid = characteristic_t.uuid;
simpleble.writeCommand(peripheral, service_uuid, characteristic_uuid, data, data.length);
And this is the debugger output:
I think, the service_uuid struct is overflowing the memory allocated for service, characteristic, and data - data has been set to the value of the service_t.uuid.value which is why I think this is the case.
I was hoping someone had some experience with this before and would be able to offer some suggestions. Apologies for the formatting!!
Thanks
(lldb) fr v
(SimpleBLE::Safe::Peripheral *) handle = 0x0000600001c68720
(simpleble_uuid_t) service = (value = "\U00000006\0\0\0\0\0\0\0\xe8ng\f\U00000003\0\0\0\x90hg\f\U00000003\0\0\0)H\xc09\U00000001\0\0\0\U00000001\0\0\0\xcb")
(simpleble_uuid_t) characteristic = (value = "@\U00000011\0\xe6\a\0\0\0\0jg\f\U00000003\0\0\0 &\xcf9\U00000001\0\0\0\U00000006\0\0\0\0\0\0\0\xe8ng\f\U00000003")
(const uint8_t *) data = 0x00007fcab4896400 "65333333-a115-11e2-9e9a-0800200ca100"
(size_t) data_length = 140508589024992
(SimpleBLE::Safe::Peripheral *) peripheral = 0x00007fcb0400b0e0
(bool) success = false
public interface libsimpleble extends Library {
libsimpleble INSTANCE = (libsimpleble)
Native.load(libName, libsimpleble.class);
[structure Bindings, incomplete Java code]
And this is the debugger output:
I think, the service_uuid struct is overflowing the memory allocated for service, characteristic, and data - data has been set to the value of the service_t.uuid.value which is why I think this is the case.
I was hoping someone had some experience with this before and would be able to offer some suggestions. Apologies for the formatting!!
-------------------------------------------------------------
public interface libsimpleble extends Library {
libsimpleble INSTANCE = Native.load("simpleble.so" /*Replace with real value*/, libsimpleble.class);
int writeCommand(simpleble_peripheral_t handle, uuid_t.ByValue service, uuid_t.ByValue characteristics, byte[] data, LibC.size_t dataLength);
public static class simpleble_peripheral_t extends PointerType {}
@FieldOrder({"value"})
public static class uuid_t extends Structure {
public static class ByValue extends uuid_t implements Structure.ByValue {};
public byte[] value = new byte[37]; // SIMPLEBLE_UUID_STR_LEN
}
@FieldOrder({"uuid"})
public static class descriptor_t extends Structure {
public static class ByValue extends descriptor_t implements Structure.ByValue {};
public uuid_t uuid;
}
@FieldOrder({"uuid", "can_read", "can_write_request", "can_write_command", "can_notify", "can_indicate","descriptor_count","descriptors"})
public static class characteristic_t extends Structure {
public static class ByValue extends characteristic_t implements Structure.ByValue {};
public uuid_t uuid;
public byte can_read;
public byte can_write_request;
public byte can_write_command;
public byte can_notify;
public byte can_indicate;
public LibC.size_t descriptor_count;
public descriptor_t[] descriptors = new descriptor_t[16];
}
@FieldOrder({"uuid", "data_length", "data", "characteristic_count", "characteristics"})
public static class service_t extends Structure {
public static class ByValue extends service_t implements Structure.ByValue {};
public uuid_t uuid;
public LibC.size_t data_length;
public byte[] data = new byte[27];
public LibC.size_t characteristic_count;
public characteristic_t[] characteristics = new characteristic_t[16];
}
}
-------------------------------------------------------------
-------------------------------------------------------------
public class Demo {
public static void main(String[] args) {
// Name needs to be adjusted
simpleble_peripheral_t peripheral = null;
// Data to send
byte[] data = new byte[]{ (byte) 0x8a, (byte) 0x03, (byte) 0xff};
// service_t struct
service_t service_t = new libsimpleble.service_t();
// Populate the service_t struct with services and characteristics
// index 0 = 65333333-A115-11E2-9E9A-0800200CA100 (service I want to use)
boolean success = !getPeripheralServices(peripheral, 0, service_t);
// [1] = 65333333-A115-11E2-9E9A-0800200CA102 (characteristic I want to use)
characteristic_t characteristic_t = service_t.characteristics[1];
// setting the function arguments to the UUID struct of the service/characteristics (has one field "value" which is called by libsimpleble dylib
uuid_t.ByValue service_uuid = new uuid_t.ByValue();
System.arraycopy(service_t.uuid.value, 0, service_uuid.value, 0, service_t.uuid.value.length);
uuid_t.ByValue characteristic_uuid = new uuid_t.ByValue();
System.arraycopy(characteristic_t.uuid.value, 0, characteristic_uuid.value, 0, characteristic_t.uuid.value.length);
libsimpleble.INSTANCE.writeCommand(peripheral, service_uuid, characteristic_uuid, data, new LibC.size_t(data.length));
}
}
-------------------------------------------------------------