I am working on a Bluetooth Low Energy (BLE) app. I have a BLE device (scale) which measures weight. I am able to connect with this device. But I am not getting how to read data (weight value) from it.
I want to know if my app is connected to any BLE device, so what are the steps to get notified by device in order to get updated data.
Okay, following is the for my Activity which I am using..
public class BlogBLEActivity extends Activity implements OnItemClickListener
{
private final static String TAG = BlogBLEActivity.class.getSimpleName();
private BluetoothAdapter bluetoothAdapter;
BluetoothManager bluetoothManager;
boolean hasBleFeature = false;
TextView tvMessage;
int messageId = R.string.doesnt_support_ble;
int colorId = android.R.color.holo_red_light;
private boolean mScanning;
private Handler handler = new Handler();
private static final long SCAN_PERIOD = 10000;
private static final int REQUEST_ENABLE_BT = 1209;
ListView listView;
ArrayList<BluetoothDevice> listDevices;
BleDeviceAdapter bleDeviceAdapter;
TextView tvHumidity;
TextView tvTemperature;
TextView tvPressure;
boolean isConnected = false;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.blog_ble);
initParameters();
initViews();
scanLeDevice(true);
}
@SuppressLint("NewApi")
void initParameters()
{
hasBleFeature = getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
Log.i(TAG, "hasBleFeature : " + hasBleFeature);
if (hasBleFeature)
{
messageId = R.string.supports_ble;
colorId = android.R.color.holo_blue_light;
} else
{
messageId = R.string.doesnt_support_ble;
colorId = android.R.color.holo_red_light;
}
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();// BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled())
{
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
listDevices = new ArrayList<BluetoothDevice>();
bleDeviceAdapter = new BleDeviceAdapter(this, listDevices);
}
void initViews()
{
tvHumidity = (TextView) findViewById(R.id.blog_ble_tv_humidity);
tvTemperature = (TextView) findViewById(R.id.blog_ble_tv_temprature);
tvPressure = (TextView) findViewById(R.id.blog_ble_tv_pressure);
tvMessage = (TextView) findViewById(R.id.blog_ble_tv_message);
tvMessage.setText(getResources().getString(messageId));
tvMessage.setTextColor(getResources().getColor(colorId));
listView = (ListView) findViewById(R.id.blog_ble_list_view);
listView.setAdapter(bleDeviceAdapter);
listView.setOnItemClickListener(this);
}
@SuppressLint("NewApi")
void scanLeDevice(final boolean enable)
{
if (enable)
{
handler.postDelayed(new Runnable()
{
@SuppressLint("NewApi")
@Override
public void run()
{
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}, SCAN_PERIOD);
mScanning = false;
bluetoothAdapter.startLeScan(leScanCallback);
} else
{
mScanning = false;
bluetoothAdapter.stopLeScan(leScanCallback);
}
}
@SuppressLint("NewApi")
private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback()
{
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
{
runOnUiThread(new Runnable()
{
@Override
public void run()
{
if (device != null)
{
bleDeviceAdapter.add(device);
bleDeviceAdapter.notifyDataSetChanged();
}
}
});
}
};
class BleDeviceAdapter extends ArrayAdapter<BluetoothDevice>
{
public BleDeviceAdapter(Context context, List<BluetoothDevice> objects)
{
super(context, R.layout.row_ble_device, R.id.row_ble_device_tv_name, objects);
}
@SuppressLint("NewApi")
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder) row.getTag();
if (holder == null)
{
holder = new ViewHolder(row);
row.setTag(holder);
}
BluetoothDevice device = getDevice(position);
holder.tvName.setText("" + device.getName());
Log.i(TAG, "" + device.getName());
return row;
}
}
BluetoothDevice getDevice(int position)
{
return (BluetoothDevice) listView.getAdapter().getItem(position);
}
@SuppressLint("NewApi")
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
{
BluetoothDevice device = getDevice(position);
Toast.makeText(this, "" + device.getName(), Toast.LENGTH_SHORT).show();
BluetoothGatt connectGatt = device.connectGatt(this, false, mGattCallback);
}
/* Client Configuration Descriptor */
private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_SERVICE = UUID.fromString("0000780a-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_FEATURE_CHAR = UUID.fromString("00008aa0-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_MEASUREMENT_CHAR = UUID.fromString("00008aa1-0000-1000-8000-00805f9b34fb");
private static final UUID KITCHEN_SCALE_INTERMEDIATE_CHAR = UUID.fromString("00008aa2-0000-1000-8000-00805f9b34fb");
/*
* In this callback, we've created a bit of a state machine to enforce that
* only one characteristic be read or written at a time until all of our
* sensors are enabled and we are registered to get notifications.
*/
@SuppressLint("NewApi")
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback()
{
/* State Machine Tracking */
private int mState = 0;
private void reset()
{
mState = 0;
}
private void advance()
{
mState++;
}
/*
* Send an enable command to each sensor by writing a configuration
* characteristic. This is specific to the SensorTag to keep power low
* by disabling sensors you aren't using.
*/
private void enableNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Enabling weight scale");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_FEATURE_CHAR);
Log.i(TAG, "Feature Properties : "+characteristic.getProperties());
characteristic.setValue(new byte[]
{ 0x09 });
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
gatt.writeCharacteristic(characteristic);
}
/*
* Read the data characteristic's value for each sensor explicitly
*/
private void readNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Reading weight cal");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
gatt.readCharacteristic(characteristic);
}
/*
* Enable notification of changes on the data characteristic for each
* sensor by writing the ENABLE_NOTIFICATION_VALUE flag to that
* characteristic's configuration descriptor.
*/
private void setNotifyNextSensor(BluetoothGatt gatt)
{
BluetoothGattCharacteristic characteristic;
switch (mState)
{
case 0:
Log.i(TAG, "Set notify weight ");
characteristic = gatt.getService(KITCHEN_SCALE_SERVICE).getCharacteristic(KITCHEN_SCALE_MEASUREMENT_CHAR);
break;
default:
mHandler.sendEmptyMessage(MSG_DISMISS);
Log.i(TAG, "All Sensors Enabled");
return;
}
// Enable local notifications
gatt.setCharacteristicNotification(characteristic, true);
// Enabled remote notifications
BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR);
desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(desc);
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
Log.i(TAG, "Connection State Change: " + status + " -> " + connectionState(newState));
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED)
{
/*
* Once successfully connected, we must next discover all the
* services on the device before we can read and write their
* characteristics.
*/
gatt.discoverServices();
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services..."));
} else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED)
{
/*
* If at any point we disconnect, send a message to clear the
* weather values out of the UI
*/
mHandler.sendEmptyMessage(MSG_CLEAR);
} else if (status != BluetoothGatt.GATT_SUCCESS)
{
/*
* If there is a failure at any stage, simply disconnect
*/
gatt.disconnect();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status)
{
Log.i(TAG, "Services Discovered: " + status);
if (status == BluetoothGatt.GATT_SUCCESS)
{
Log.i(TAG, "No of services discovered: " + gatt.getServices().size());
mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "No of services discovered: " + gatt.getServices().size()));
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService bluetoothGattService : services)
{
UUID uuid = bluetoothGattService.getUuid();
Log.e(TAG, ""+uuid.toString());
List<BluetoothGattCharacteristic> characteristics = bluetoothGattService.getCharacteristics();
for (BluetoothGattCharacteristic bluetoothGattCharacteristic : characteristics)
{
UUID uuidC = bluetoothGattCharacteristic.getUuid();
Log.i(TAG, "Gatt Properties : "+bluetoothGattCharacteristic.getProperties());
Log.i(TAG, ""+uuidC.toString());
CharacteristicHelper helper = new CharacteristicHelper(bluetoothGattCharacteristic);
Log.i(TAG, "isRead : "+helper.isRead());
Log.i(TAG, "isWrite : "+helper.isWrite());
Log.i(TAG, "isNotify : "+helper.isNotify());
Log.i(TAG, "isWriteNoResponse : "+helper.isWriteNoResponse());
}
}
}
// mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS,
// "Enabling Sensors..."));
/*
* With services discovered, we are going to reset our state machine
* and start working through the sensors we need to enable
*/
reset();
enableNextSensor(gatt);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
Log.i(TAG, "onCharacteristicRead");
// For each read, pass the data up to the UI thread to update the
// display
/**methodToUpdateUI().*/
// After reading the initial value, next we enable notifications
setNotifyNextSensor(gatt);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
{
Log.i(TAG, "onCharacteristicWrite");
// After writing the enable flag, next we read the initial value
readNextSensor(gatt);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
{
Log.i(TAG, "onCharacteristicChanged");
/*
* After notifications are enabled, all updates from the device on
* characteristic value changes will be posted here. Similar to
* read, we hand these up to the UI thread to update the display.
*/
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
{
Log.i(TAG, "onDescriptorWrite");
// Once notifications are enabled, we move to the next sensor and
// start over with enable
advance();
enableNextSensor(gatt);
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status)
{
Log.i(TAG, "Remote RSSI: " + rssi);
}
private String connectionState(int status)
{
switch (status)
{
case BluetoothProfile.STATE_CONNECTED:
return "Connected";
case BluetoothProfile.STATE_DISCONNECTED:
return "Disconnected";
case BluetoothProfile.STATE_CONNECTING:
return "Connecting";
case BluetoothProfile.STATE_DISCONNECTING:
return "Disconnecting";
default:
return String.valueOf(status);
}
}
};
/*
* We have a Handler to process event results on the main thread
*/
private static final int MSG_PROGRESS = 201;
private static final int MSG_DISMISS = 202;
private static final int MSG_CLEAR = 301;
private Handler mHandler = new Handler()
{
@SuppressLint("NewApi")
@Override
public void handleMessage(Message msg)
{
BluetoothGattCharacteristic characteristic;
switch (msg.what)
{
case MSG_PROGRESS:
tvMessage.setText((String) msg.obj);
break;
case MSG_DISMISS:
tvMessage.setText("Service Enabled");
break;
case MSG_CLEAR:
tvMessage.setText("");
break;
}
}
};
}
In my activity, first of all I am scanning all the available devices & preparing ListView. On clicking on list item I connect to that particular device. When device's status become connected then I discover services. I have UUIDs of the device's service & its characteristics. But I am not sure how to write to any particular characteristics or enable or read data from it. Although I have tried this thing but I don't see any success.
If any one has any idea about it, so please help me.
BLE standard defines two ways to transfer data for the server to the client: notification and indication. Notifications and indications are initiated by the Server but enabled by the Client. Notification doesn't need to be acknowledged, so they are faster and an efficient way to read data continuously.
To find BLE devices, you use the startScan() method. This method takes a ScanCallback as a parameter. You must implement this callback, because that is how scan results are returned.
Had a device which required me to use
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
instead of
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
as explained in this question
Android BLE API: GATT Notification not received
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With