I'm trying to connect to a Bluetooth Peripheral I wrote for Mac OS X from Android. I have the following code below:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
String intentAction;
mBluetoothGatt = gatt;
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
mConnectionState = STATE_CONNECTED;
broadcastUpdate(intentAction);
Log.i(TAG, "Connected to GATT server.");
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);
mBluetoothGatt.setCharacteristicNotification(characteristic, false);
}
}
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.i(TAG, "onServicesDiscovered received: " + status);
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
// 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);
}
}
};
Most of the above documentation was on Google's website:
https://developer.android.com/guide/topics/connectivity/bluetooth-le.html#read
However, I keep getting status 129 on onServicesDiscovered and i'm not able to send any data to my peripheral. Is there something i'm doing incorrectly in the above? It seems i'm following the documentation and i'm not able to do it.
EDIT
Seems like in API 21 Google made some changes to the BLE API and they didn't update their documentation (obviously very convenient for us developers). I found the following tutorial helpful:
http://www.truiton.com/2015/04/android-bluetooth-low-energy-ble-example/
Basically with API 21+ you have to use BluetoothLeScanner mLEScanner for scanning for LE devices near by. When the scan is done we have a callback like so:
private ScanCallback mScanCallback = new ScanCallback() {
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
onScan(result.getDevice(), 0, null);
}
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
onScan(sr.getDevice(), 0, null);
}
}
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
We can get the devices from the ScanResult and use that for connection and it works on some devices but not all. I get it to work on my Nexus 7 but not my Samsung Galaxy S6. Android BLE is a MESS.
EDIT 2
For now we are just using Bluetooth Classic and not Bluetooth LE. Hopefully Google gets their LE stack figured out properly. I'm not able to get BLE code working on most of the devices in the market.
Even Google's own sample code works on less than half the devices I tested on!
I'm connecting to ios device (Peripheral )from android device as central, I could able to read and write the characteristic. Let me know, what exactly you are looking for now !!!
Follow the sample code
Related
I am developing an application that connects through Bluetooth Low Energy to a Bluetooth scale and read weight.
The scale is Hi Weigh CF 150.
I have followed this tutorial
https://punchthrough.com/android-ble-guide/
I am connecting to to the scale and I come to the point where I try to discover the services of the scale.
class GattCallBack extends BluetoothGattCallback {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
String deviceAddress = gatt.getDevice().getAddress();
if ( status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.w(TAG, "Scale Connected successfully.");
bluetoothGatt = gatt;
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
if (bluetoothGatt != null) {
bluetoothGatt.discoverServices();
}
}
});
//#TODO Store a reference to BluetoothGatt
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Succesfully disconected from Service $deviceAddress");
gatt.close();
} else {
Log.i(TAG, "Error $status encountered for #deviceAAddress! Disconnecting");
gatt.close();
}
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.w(TAG, " Devices service " + gatt.getDevice().getName() + ": " + gatt.getServices().size());
Iterator<BluetoothGattService> iter = gatt.getServices().iterator();
while (iter.hasNext()) {
Log.w(TAG, "Found service: " + iter.next().getUuid());
}
}
}
At this point the onServicesDiscovered(BluetoothGatt gatt, int status) callback always has zero discovered services with status 129.
Status 129 is a generic error from what I have understand and I have no other info about it.
By scanning the same scale with Nordic semiconstructor nrfConnect app, the services of the scale are discovered as you can see at the screenshot.
From the Nordic's app I can see that the service is "49535343-fe7d-4ae5-8fa9-9fafd205e455" and the characteristic for the weight is "49535343-1e4d-4bd9-ba61-23c647249616"
Is it possible that I have to use password to read the services (I a have given a password but I don't know how to use it).
Any help is appreciated.
I've been developing an app for Android who communicates with a physical piece of hardware via bluetooth. Until now we've been using normal bluetooth, but this has now changed to BLE. I am not an android developer, why this is a bit difficult for me.
At the moment I am just running a proof of concept. Whenever the physical device receives something, it transmits it back, and I can confirm this using proper BLE terminal apps. I've identified the characteristics I need to use, and I can connect to the device and send data to it. Though I cannot receive data, I am never receiving anything back.
The code shown below shows what I'm currently doing, trying to send (successfully) to the device and not so successfully receiving data. Am I totally misunderstand this concept?
Hope someone can give me a hand.
#Override
public void onConnectionStateChange(BluetoothGatt
gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.i("GattCallback", "connected");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
Log.i("GattCallback", "Disconnected");
break;
default:
System.out.println(newState);
}
}
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
byte[] newValue = characteristic.getValue();
System.out.println("RECEIVED: " + Arrays.toString(newValue));
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
for (BluetoothGattService gattService : gatt.getServices()) {
Log.i("derp", "onServicesDiscovered: service=" + gattService.getUuid());
for (BluetoothGattCharacteristic characteristic : gattService.getCharacteristics()) {
Log.i("derp", "onServicesDiscovered: characteristic=" + characteristic.getUuid());
if(characteristic.getUuid().toString().contains("9616")){
System.out.println("We have found the read characteristic.");
gatt.setCharacteristicNotification(characteristic, true);
BluetoothGattDescriptor desc = characteristic.getDescriptor(UUID.fromString("49535343-1e4d-4bd9-ba61-23c647249616"));
if(desc == null){
System.out.println("NULL!");
return;
}
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
System.out.println("Should be subscribed.");
}
else if(characteristic.getUuid().toString().contains("9bb3")){
System.out.println("Send characteristic found");
characteristic.setValue("Test\n\r");
boolean res = gatt.writeCharacteristic(characteristic);
if(res)
System.out.println("Success");
break;
}
}
}
}
}
I have a BLE device (smartwatch) in which i want to read all data from services and use in my application(Swift and Android), How can i access data from the custom services, but i cant see the UUID in the GATT services and characteristics
You can read data using this code. it's work for me.
When you Smartwatch connected then you needs to send request for service discover. So, Below callback method triggered. In this method get your service and characteristics.
#Override
public void onServicesDiscovered(final BluetoothGatt gatt, final int status) {
Log.e(TAG, "onServicesDiscovered: ");
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.w(TAG, "onServicesDiscovered received: " + status);
return;
}
BluetoothGattService customService = gatt.getService(CUSTOM_SERVICE_UUID);
if (customService != null) {
customNotifyCharacteristic = customService.getCharacteristic(CUSTOM_CHARACTERISTICS_NOTIFY_UUID);
customWriteCharacteristic = customService.getCharacteristic(CUSTOM_CHARACTERISTICS_WRITE_UUID);
customReadCharacteristic = customService.getCharacteristic(CUSTOM_CHARACTERISTICS_READ_UUID);
}
}
After getting all characteristics and services send read request like below.
put below line where you need to read data.
readCharacteristic(customReadCharacteristic);
below explain readCharacteristic method.
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
So, onCharacteristicRead callback triggered.
#Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
byte[] data = characteristic.getValue();
}
}
byte[] data = characteristic.getValue();
return your data which you want to read form device.
I'm writing printing data to the BluetoothGattCharacteristic of a Zebra ZD410 printer. I do this by chunking the data into 20 byte chunks and writing a chunk at a time with the following code:
mCharacteristic.setValue(bytes);
boolean status = mGatt.writeCharacteristic(mCharacteristic);
and then waiting until I receive BluetoothGattCallback.onCharacteristicWrite() before initiating the writing of the next chunk. This works fine.
If I disconnect() and close() the BluetoothGatt and later connect to the same device again with BluetoothDevice.connectGatt() and then try to write to the Characteristic after onServicesDiscovered() has been called is done and I have my Characteristic again, writing will fail. What I mean by this is that when I write to the Characteristic now, onCharacteristicWrite() will be called with a Characteristic who's getValue() returns the value of the last write on the old Gatt.
After trying to solve this for two days and reading tons of SO posts I haven't found a solution.
How can I fix this?
EDIT
Here is the code for the BluetoothGattCallback
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback()
{
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
FALog.i(TAG, "onConnectionStateChange Status: " + status);
switch (newState)
{
case BluetoothProfile.STATE_CONNECTED:
FALog.i(TAG, "gattCallback STATE_CONNECTED");
gatt.discoverServices();
break;
case BluetoothProfile.STATE_DISCONNECTED:
disconnectAndCloseGatt();
mCharacteristic = null;
connectionFailed();
FALog.e(TAG, "gattCallback STATE_DISCONNECTED");
break;
default:
FALog.e(TAG, "gattCallback STATE_OTHER");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
BluetoothGattService service = gatt.getService(PRINTER_SERVICE_UUID);
if (service != null)
{
BluetoothGattCharacteristic characteristic = service.getCharacteristic
(PRINTER_SERVICE_CHARACTERISTIC_UUID);
if (characteristic != null)
{
mCharacteristic = characteristic;
mInternalState = STATE_CONNECTED;
mState = State.CONNECTED;
notifyStateChanged();
print("~JA");
FALog.d(TAG, "Printer connected");
mBluetoothActivity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
mListener.onPrinterConnected();
}
});
}
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
FALog.d(TAG, "received onCharacteristicWrite " + new String(characteristic.getValue()) + "; success: " +
(status == BluetoothGatt.GATT_SUCCESS));
if (status == BluetoothGatt.GATT_SUCCESS)
{
handler.removeCallbacks(writeRunnable);
popQueueAndReleaseLock();
}
}
};
Try writeCharacteristic after the onDescriptorWrite() callback instead of onServicesDiscovered() callback. writeDescriptor holds the mDeviceBusy.
I'm working on a Android project which is to connect Nexus 7 and a bio sensor through BLE link. The problem is that, I can successfully detect and get list of services and characteristics of the sensor. When I write some data to the specific characteristic, onCharacteristicWrite is automatically called and showed me writing operation is successful. However, the sensor never receive anything from the tablet. And if I use similar app on iPhone, everything works fine. So there's no problem with the device.
Does anyone have any idea of the problem?
Here is my code for write:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnected = true;
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) {
mConnected = false;
Log.i(TAG, "Disconnected from GATT server.");
}
}
#Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
//Once detected services, write to characteristic for 6 times.
int count =6;
while(count>0){
writeCharacteristic();
count--;
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
#Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status){
if (status == BluetoothGatt.GATT_SUCCESS){
Log.d(TAG,"Write to Characteristic Success! !");
}
}
};
public boolean writeCharacteristic(){
//check mBluetoothGatt is available
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return false;
}
BluetoothGattService Service = mBluetoothGatt.getService(UUID_MY_SERVICE);
if (Service == null) {
Log.e(TAG, "service not found!");
return false;
}
BluetoothGattCharacteristic characteristic = Service
.getCharacteristic(UUID_MY_CHARACTERISTIC);
if (characteristic == null) {
Log.e(TAG, "char not found!");
return false;
}
byte[] value = {(byte)300,(byte)100,(byte)100};
characteristic.setValue(value);
boolean status = mBluetoothGatt.writeCharacteristic(characteristic);
return status;
}
The output shows "Write to Characteristic Success! !" for six times, thus the writing operation succeeded. However, the device shows that nothing been received from tablet. I also tried to write one byte at a time, or add a timer to let the tablet write to sensor every 2 seconds. But none of them worked. Any ideas?
(Answered by question edit. Converted to a community wiki answer. See What is the appropriate action when the answer to a question is added to the question itself? )
The OP wrote:
Follow Up:
The problem solved by manually pairing the tablet with the device first in the setting instead of pairing by code.
So only using the code snippet of connecting Gatt provided by Android is not good enough to pair the device. I should add another code I found online to pair the devices if I don't want to pair them manually every time:
private void pairDevice(BluetoothDevice device) {
try {
Log.d("pairDevice()", "Start Pairing...");
Method m = device.getClass()
.getMethod("createBond", (Class[]) null);
m.invoke(device, (Object[]) null);
Log.d("pairDevice()", "Pairing finished.");
} catch (Exception e) {
Log.e("pairDevice()", e.getMessage());
}
}