Here is a list of the posts in this challenge
Gr0G - 03 - High-pressure system design
Gr0G - 07 - Playing with the Gertbot
Gr0G - 11 - Building the box (2)
Gr0G - 12 - Building the high-pressure system
Gr0G - 13 - Building the high-pressure system (2)
Source code available at https://github.com/ambrogio-galbusera/gr0g, https://github.com/ambrogio-galbusera/gr0g-ble-android and https://github.com/ambrogio-galbusera/gr0g-ble
Let's now talk about the Android app that connects to the Gr0G through Bluetooth LE
Permissions
In order to use Bluetooth features in your application, you must declare two permissions. The first of these is BLUETOOTH. You need this permission to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data.
The other permission that you must declare is ACCESS_FINE_LOCATION. Your app needs this permission because a Bluetooth scan can be used to gather information about the location of the user. This information may come from the user's own devices, as well as Bluetooth beacons in use at locations such as shops and transit facilities. Many sample apps also include ACCESS_COARSE_LOCATION and BLUETOOTH_ADMIN, but these are not strictly required
The permissions have to be added to AndroidManifest.xml file
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ag.gr0g.readwrite" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Device scanning
An activity (ScanActivity) has been implemented to perform a scan for BLE devices. The activity implements the BluetoothAdapter.LeScanCallback interface, required by the startLScan function. Such interface defines just a function, which i invoke whenever a new BLE device is detected
@Override public void onLeScan(final BluetoothDevice newDeivce, final int newRssi, final byte[] newScanRecord) { runOnUiThread(new Runnable() { @Override public void run() { mDeviceAdapter.update(newDeivce, newRssi, newScanRecord); } }); }
The method simply adds a new entry to the mDeviceAdapter, which is the datasource for the list widget
The code to initialize the Bluetooth adapter is in the init() method. Here we ensure that BLE is supported and then get the Bluetooth adapter object
if (!BleUtil.isBLESupported(this)) { ... return; } // BT check BluetoothManager manager = BleUtil.getManager(this); if (manager != null) { mBTAdapter = manager.getAdapter(); } if (mBTAdapter == null) { ... return; } if (!mBTAdapter.isEnabled()) { ... return; }
Finally we can start the scan (see startScan() method)
mBTAdapter.startLeScan(this);
Accessing Gr0G data
When a BLE device is clicked in the list, the DeviceActivity is opened. This activity receives the Bluetooth device that has been selected as an extra parameter.
The DeviceActivity has the following layout
The first think to do, is to connect to the device itself
mConnGatt = mDevice.connectGatt(this, false, mGattcallback);
the mGattCallback is an instance of the class BluetoothGattCallback and implements these methods
onConnectionStateChanged
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) { // handle device connection / disconnection }
It is very common to see a status 133 in this method when trying to connect to a device, especially while you are developing your code. The status 133 can have many causes and some of them you can control:
- Make sure you always call close() when there is a disconnection. If you don’t do this you’ll get a 133 for sure next time you try.
- Make sure you always use TRANSPORT_LE when calling connectGatt()
- Restart your phone if you see this while developing. You may have corrupted the stack by debugging and it is in a state where it doesn’t behave normal again. Restarting your phone may fix things.
- Make sure your device is advertising. The connectGatt with autoconnect set to false times out after 30 seconds and you will receive a 133.
- Change the batteries of your device. Devices typically start behaving erratically when they battery level is very low.
In my case, I had to apply option 2 and make a connect retry in case of disconnection
public void onConnectionStateChange(BluetoothGatt gatt, int status,int newState) { // handle device connection / disconnection .... } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mStatus = newState; mConnGatt = mDevice.connectGatt(null, false, mGattcallback, BluetoothDevice.TRANSPORT_LE); mStatus = BluetoothProfile.STATE_CONNECTING;
onServiceDiscovered
This method is called when a new service has been discovered. In our implementation, the UI controls are properly initialized by storing a Characteristic object in the UI control's tag
onCharacteristicRead
This method is called whenever a characteristic read operation has completed. A characteristic is read by code like this (v is a View object). In this app, I created a task (UpdateDataTask) that reads characteristics every 2 seconds
BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) v.getTag(); if (mConnGatt.readCharacteristic(ch)) { // success }
All the characteristic values are sent as double. To extract the value, we use the following code
final byte[] bvalue = characteristic.getValue(); reverse(bvalue); double value = ByteBuffer.wrap(bvalue).getDouble();
where
- get the array of byte sent by the BLE device
- reverse the bytes order in the array
- convert the array of byte to a double
onCharacteristicWrite
This method is called whenever a characteristic write operation has completed. A characteristic is read by code like this (v is a View object). A characteristic is written when
- the value of either temperature setpoint or humidity setpoint is changed
- the light switch's status changes
- the light release button is clicked
BluetoothGattCharacteristic ch = (BluetoothGattCharacteristic) mHumiditySetpoint .getTag(); ch.setValue(new byte[] { (byte) Integer.parseInt(mHumiditySetpoint.getText().toString()) }); if (mConnGatt.writeCharacteristic(ch)) { setProgressBarIndeterminateVisibility(true); }
Source code
The full source code of the application is available at https://github.com/ambrogio-galbusera/gr0g-ble-android
The app has been developed with Android Studio
Useful links
These are some useful links and posts about BLE on Android
https://developer.android.com/guide/topics/connectivity/bluetooth
https://medium.com/@martijn.van.welie/making-android-ble-work-part-1-a736dcd53b02
https://medium.com/@shahar_avigezer/bluetooth-low-energy-on-android-22bc7310387a