So, I am developing a Java BLE Android Module using Eclipse (to code the module and Appcelerator (to make the Android App). I am trying to receive data from a BLE Device and show it on an Android Phone. I can scan the device and connect to it.
But I really, really can't receive any data from it.
I have tried at least 10 different stuff but...
The main problem is that I don't know the BLE API very well, and I am a little noobie in Java. Can anyone please help a poor soul to actually read the data from the device?
The main problem is setting the Bluetooth Characteristic UUID (which I have). I just don't know how to do it...
Bellow are the codes for the Module...
public class AndroidbleModule extends KrollModule {
public static final String LCAT = "BLE";
private BluetoothManager btManager;
private BluetoothAdapter btAdapter;
private BluetoothDevice btDevice;
private TiApplication appContext;
private Activity activity;
private KrollFunction onFound;
private KrollFunction onConnections;
private BluetoothLeScanner btScanner;
private UUID uuid;
BluetoothGatt bluetoothGatt;
BluetoothGattCharacteristic btChar;
BluetoothGattCallbackHandler btData;
KrollDict kd;
Boolean isConnected = false;
BluetoothGatt connectedGatt;
private ScanCallback scanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();
if (device != null) {
BluetoothDeviceProxy btDeviceProxy = new
BluetoothDeviceProxy(device);
if (device.getName() != null) {
Log.d(LCAT, "Found: " + device.getName() + " " +
device.getAddress());
ArrayList<String> ids = new ArrayList<String>();
if (device.getUuids() != null) {
for (ParcelUuid id1 : device.getUuids()) {
ids.add(id1.toString());
}
}
btDevice = device;
kd = new KrollDict();
kd.put("name", btDevice.getName());
kd.put("macaddress", btDevice.getAddress());
fireEvent("nb_DevicesFound", kd);
btScanner.stopScan(scanCallback);
}
}
}
};
#Kroll.method
public boolean connect()
{
try {
bluetoothGatt = btDevice.connectGatt(appContext, true,
new BluetoothGattCallbackHandler(AndroidbleModule.this));
if (bluetoothGatt != null) {
System.out.println("***** ***** Connected to: =====>>>>> " + btDevice.getAddress() + " " + btDevice.getName());
this.fireEvent("nb_onConnect",null);
isConnected = true;
bluetoothGatt = connectedGatt;
}
} catch (Exception e) {
isConnected = false;
this.fireEvent("nb_NoConnection", null);
}
return true;
};
#Kroll.method
public void readData()
{
System.out.println("WHAT THE HELL DO I DO????");
}
}
public final class BluetoothGattCallbackHandler extends
BluetoothGattCallback {
private static final String LCAT = AndroidbleModule.LCAT;
private KrollProxy proxy;
private static final String UUID_SERVICE_TS002004 = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_WRITE_TS002004 = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
private static final String UUID_CHARACTERISTIC_NOTIFY_TS002004 = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
BluetoothGattCharacteristic btCharacteristic;
public BluetoothGattCallbackHandler(KrollProxy proxy) {
super();
this.proxy = proxy;
}
#Override
public void onConnectionStateChange(final BluetoothGatt gatt,
final int status, final int newState) {
KrollDict kd = new KrollDict();
kd.put("newState", newState);
kd.put("status", status);
if (proxy.hasListeners("didConnectionStateChange")) {
proxy.fireEvent("didConnectionStateChange", kd);
}
gatt.discoverServices();
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.i(LCAT,"onServicesDiscovered");
if (status != BluetoothGatt.GATT_SUCCESS) return;
btCharacteristic =
gatt.getService(UUID.fromString(UUID_SERVICE_TS002004)).getCharacteristic(UUID.fromString(UUID_CHARACTERISTIC_NOTIFY_TS002004));
gatt.setCharacteristicNotification(btCharacteristic,true);
BluetoothGattDescriptor descriptor = btCharacteristic.getDescriptor(UUID.fromString(UUID_CHARACTERISTIC_WRITE_TS002004));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt,
final BluetoothGattCharacteristic characteristic) {
byte[] data = characteristic.getValue();
Log.i(LCAT, "Char changed " + data.toString());
for (BluetoothGattDescriptor descriptor :
characteristic.getDescriptors()) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
}
}
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
Log.i(LCAT,"onCharacteristicRead");
}
#Override
public void onDescriptorRead(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
Log.i(LCAT,"onDescriptorRead");
}
}
I expect to some good soul that will go to Heaven to have mercy on me and help me get those sweet bytes of data.
One of the things that is missing in your code, is to Set Notifications, so the Central can listen to the Peripheral's response.
Try something like this:
public void setNotifications() {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
characteristic.addDescriptor(descriptor);
characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeDescriptor(descriptor);
bluetoothGatt.setCharacteristicNotification(characteristic, true);
}
After that, you can send commands to the device and hear it's return.
public void returnData(String data) {
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
String dataString = data;
dataString.getBytes();
writeCharacteristic.setValue(dataString);
writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(writeCharacteristic);
}
First of all, read the bluetooth overview. Check if bluetooth permissions have been added to the project.
One thing that is wrong here is that isConnected=true is set too early, because you can consider that you are connected after ble services has been discovered and (status == BluetoothGatt.GATT_SUCCESS). Otherwise, you can't read and write characteristics.
A good starting point can be this repo from google. It is old project and I'm not sure if you have to update some dependency to make it compile, but nothing important.
Establish a connection and start reading bytes is easy, but if you want establish a reliable connection with ble device, due to android fragmentation and manufacturerers that don't follow the bluetooth specs, it can be super difficult, almost imposible to make a bluetooth low energy that works perfect for all devices.
Once you have started to read some bytes, I suggest this video to learn some important tricks about. If you want go deeper, read carefully this fantastic resource about ble.
Once you start to get desperate with ble, probably it will be a good moment to read this list of known issues
Finally, you will discover that the best thing that you can do with ble low energy in android is use open source libraries like the Nordic semiconductor ble library or RxAndroid Ble.
But before use a ble library, it is a good practice understand what is doing the library and understand why you need it.
EDIT: I have never used appcelerator, but here you have a bluetooth module for appcelerator titanium.
You have the wrong uuid for the Client Characteristic Configuration Descriptor. It should be 00002902-0000-1000-8000-00805f9b34fb but you have written 6E400002-B5A3-F393-E0A9-E50E24DCCA9E.
Related
After connectivityManager.getActiveNetworkInfo() got deprecated in API 29, it was quite a struggle to put together a network monitoring function that would also distinguish metered (cell) vs WiFi connection.
The problems I faced were 2:
connectivityManager.isActiveNetworkMetered() -- when both WiFi and Mobile data were on, it returned true (at least on my emulator, and I couldn't check on a device). When only WiFi was on, it returned false, as expected.
connectivityManager.isDefaultNetworkActive() -- it would always be active even if WiFi and Mobile were off.
The only way I found to work around this is via (1) onCapabilitiesChanged() and (2) getActiveNetwork()==null. The code is pasted below.
Do you know if there are other ways to do it? The whole implementation seems cumbersome, and the internet is full of deprecated examples.
Thanks
private static void monitorNetworks(){
NetworkRequest.Builder builder = new NetworkRequest.Builder();
connectivityManager.registerNetworkCallback(
builder.build(),
ncb = new ConnectivityManager.NetworkCallback() {
#Override
public void onAvailable(Network network) {
scanAndSend();
}
#Override
public void onLost(Network network) {
scanAndSend();
}
#Override
public void onUnavailable(){
scanAndSend();
}
#Override
public void onCapabilitiesChanged (Network network,
NetworkCapabilities networkCapabilities){
boolean metered = !networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
Log.d(TAG, "NET_CAPABILITY_NOT_METERED: " +
String.valueOf(networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)));
sendMetered(metered);
}
}
);
}
private static void scanAndSend(){
boolean is_connected = false;
if(connectivityManager.getActiveNetwork()==null) is_connected = false;
else is_connected = true;
sendConnectivityIntent(IS_NETWORK_AVAILABLE, is_connected);
}
private static void sendMetered(boolean metered) {
sendIntent(IS_NETWORK_METERED, metered);
}
private static void sendIntent(String val_name, boolean val) {
Intent intent = new Intent();
intent.setAction(CONNECTIVITY_ACTION);
intent.putExtra(val_name, val);
context.sendBroadcast(intent);
}
With my current configuration, other android device gets to read the set UUID just fine but when it comes to iOS, my colleague is getting variants of UUID from this advertisement/GATT broadcast. Do I really need to broadcast GATT for iOS to discover me?
Starting the advertisement:
#ReactMethod
private void advertise(Callback advCallBack) {
...
private static UUID myUUID = UUID.fromString("A85A30E5-93F3-42AE-86EB-33BFD8133597");
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH).setConnectable(false).build();
ParcelUuid pUuid = new ParcelUuid(myUUID);
AdvertiseData data = new AdvertiseData.Builder().setIncludeDeviceName(false).addServiceUuid(pUuid)
.setIncludeTxPowerLevel(true).build();
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising(settings, data, advertisingCallback);
}
Starting the GATT Server:
#ReactMethod
private void startServer(Callback srvCallBack) {
mBluetoothGattServer = mBluetoothManager.openGattServer(getReactApplicationContext(), mGattServerCallback);
if (mBluetoothGattServer == null) {
srvCallBack.invoke(false);
return;
}
mBluetoothGattServer.addService(new BluetoothGattService(myUUID, BluetoothGattService.SERVICE_TYPE_PRIMARY));
srvCallBack.invoke(true);
}
private BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() {
#Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
return;
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
return;
}
}
};
We don't need to connect to each other, we just need to scan for its existence and maybe pass bits of information which I can't do right now from android because just including the device name is giving me an Error code 1 which basically means that the payload is bigger than the allowed 31 bytes for the advertisement packet. Any advise?
I solved the problem by including a serviceData on the advertising data.
AdvertiseData advertiseData = new AdvertiseData.Builder().setIncludeDeviceName(true)
.addServiceUuid(new ParcelUuid(SERVICE_UUID)).addServiceData(new ParcelUuid(SERVICE_UUID), serviceData)
.setIncludeTxPowerLevel(true).build();
For a project I want to send acceleration data via BLE 4.2 (with DLE). An acceleration packet consists of 81 values (24 bits each, total 243 bytes). My goal is to send 18 packets (1458 acceleration packets or 4374 bytes). I use a Samsung Galaxy A3 2017 Edition as a central and a microcontroller (nRF52832dk) as a peripheral. After all packets have been sent, the connection to the smartphone is terminated and new 1458 acceleration packets are recorded. After that, an RTC activates sleep mode, on the nRF52832dk, for 1 minute. After 1 minute, the nRF52832dk is going to advertising for a new connection with the smartphone. The following figure illustrates the problem:
https://ibb.co/Syb4GDg
The software on the nRF52832dk works perfectly fine. I am able to record data, go into sleep mode, wake-up after 1 minute and advertise for new connection. My problem is the software application. I use these tutorials:
• https://github.com/googlesamples/android-BluetoothLeGatt
• https://developer.android.com/guide/topics/connectivity/bluetooth-le
in BluetoothLeGatt I have changed:
mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
with the second parameter on true its possible for the smartphone to autoconnect. This works fine (for the first connection with the nRF)
Originally it was:
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
Once a connection has been established, the callback routine is called by the GATT handler.
// Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
broadcastUpdate(intentAction);
}
}
#Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
#Override
// Change in the characteristic
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}; </i>
after the connection is established and the first packet is received, i get into the broadcastUpdate
<i>private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
// This is special handling for the nRF52 profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_NRF.equals(characteristic.getUuid())) {
byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
} else {
// For all other profiles, writes the data formatted in HEX.
final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) {
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar));
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
}
}
sendBroadcast(intent);
}
Now I got two problems (I had the same two problems before did the change in the code).
First Problem:
My first problem is, that I just receive only one of the 18 BLE package (the first one).
Second Problem:
My first Problem is also causing my second Problem. The Application does not recognize that the connection has been terminated from the nRF52832dk.
and I have another question. what exactly does
endBroadcast(intent);
?
This post is very complicated, I think you mean you cannot receive nrf52 data.
You need check nrf52 support how many MTU, BLE protect max is '247' and valid data length is '244' valid length is (DEVICE_MTU - OPCODE_LENGTH(1) - HANDLE_LENGTH(2));
So as you say you want receive 2474 bytes, it's at least 17 times.
current process at android is onCharacteristicChanged - > onCharacteristicRead
if you want trigger onCharacteristicChanged you need open remote device notification like this:
private void EnableNotification(BluetoothGattDescriptor d, BluetoothGatt gatt)
{
gatt.SetCharacteristicNotification(d.Characteristic, true);
d.SetValue(new byte[] { 1, 1 });
gatt.WriteDescriptor(d);
}
i'm beginner of BLE(Bluetooth-LowEnergy). I wanted to get data from android device via BLE.
I've already read about characteristic in Google Document.
and i've already searched on google too.
my device didn't respond to my request byte code.
i think it's because i set wrong characteristics.
cus i think i didn't understand about characteristics perfectly.
does anybody help me to set right characteristics please ?
Here's custom Uuid(it's better to see added Images on the top.)
CUSTOM SERVICE
0783b03e-8535-b5a0-7140-a304d2495cb7
NOTIFY Uuid : 0783B03E-8535-B5A0-7140-A304D2495CB8
Read Uuid : 00002a19-0000-1000-8000-00805f9b34fb
Write Uuid : 0783b03e-8535-b5a0-7140-a304d2495cba
and Here's Uuid I set
public final UUID serviceUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cb7");
public final UUID notifyUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cb8");
public final UUID readUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
public final UUID writeUuid = UUID.fromString("0783b03e-8535-b5a0-7140-a304d2495cba");
and Here's my code
BluetoothHandler.java
targetGattCharacteristic = targetGattService.getCharacteristic(Define.GetInstance().notifyUuid);
BluetoothGattCharacteristic readGattCharacteristic = targetGattService.getCharacteristic(Define.GetInstance().notifyUuid);
if (readGattCharacteristic != null) {
mBleService.setCharacteristicNotification(readGattCharacteristic, true);
} else {
callInterface();
return;
}
(BluetoothService.java)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor gD = new BluetoothGattDescriptor(UUID.fromString(Define.GetInstance().readUuid.toString()), BluetoothGattDescriptor.PERMISSION_WRITE | BluetoothGattDescriptor.PERMISSION_READ);
characteristic.addDescriptor(gD);
if (Define.GetInstance().notifyUuid.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(Define.GetInstance().readUuid.toString()));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
i solved it by referring to this site.
https://medium.com/#avigezerit/bluetooth-low-energy-on-android-22bc7310387a
I am working with a BLE device in ANDROID.
Here is where I set up the endpoint 0000fff4-0000-1000-8000-00805f9b34fb to receive notifications (CLIENT_CHARACTERISTIC_CONFIG is 00002902-0000-1000-8000-00805f9b34fb
public void k2DigitalNotification(BluetoothGattCharacteristic characteristic,
boolean enabled)
{
Boolean myStatus;
if (MY_BLUETOOTH_SERVICE.equals(characteristic.getUuid()))
{
Log.v(TAG, "Characteristic: " + characteristic.getUuid());
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
myStatus = mBluetoothGatt.writeDescriptor(descriptor);
Log.v(TAG,"Write Status::"+myStatus);
bluetoothGatt.setCharacteristicNotification(characteristic, true);
}
}
Here is the code where I write out to the device. THIS WORKS! 100%, the data shows up at the embedded BLE device. (endpoint is 0000fff1-0000-1000-8000-00805f9b34fb )
public void k2digitalWriteToCharacteristic(BluetoothGattCharacteristic characteristic) {
if (BLE_ENDPOINT.equals(characteristic.getUuid()))
{
Log.v(TAG,"0Xfff1");
byte[] data3Send = new byte[4];
data3Send[0] = 0x31;
data3Send[1] = 0x01;
data3Send[2] = 0x5a;
data3Send[2] = 0x0d;;
characteristic.setValue(data3Send);
boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
Log.v(TAG, "Status is:" + String.valueOf(status));
}
}
The BLE device is sending the data out.....I've verified this in apps like nRFMaster from Nordic Semi or BLEScanner from Bluepixel.
But I NEVER EVER see the callback.
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.v(TAG,"A Characteristic Change?!?!?!");
byte[] data = characteristic.getValue();
Log.v(TAG,"Here is the data: "+data[0]);
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
Any help? I've been banging my head against the wall all week.
My issue was regarding the ble device was busy. I was attempting to read and subscribe without much gap in time and the ble device was busy. you can check if the enable notification was successful with the following code:
Boolean status = bluetoothGatt.writeDescriptor(descriptor);
This will return true if the writeDescriptor was successful and false otherwise - hence helping you debug if the ble device is busy.