Search code examples
androidbluetooth-lowenergyandroid-bluetooth

How do you get data from a Bluetooth LE device


I have a bluetooth barcode scanner that supports bluetooth LE and I am trying to get the barcode information from it when one is scanned.

I can connect to it fine onServicesDiscovered gets called in my BluetoothGattCallback but I am not sure what to do from there.

With a classic bluetooth connection you would get a InputStream from a BluetoothSocket and you would just wait for the read() to give you data but I am not sure how it works with Bluetooth LE. I tried looping through the BluetoothGattCharacteristic's checking the property and if its a read property I call gatt.readCharacteristic(characteristic); but that just gives me useless information and that's even before I attempt to scan something.

So how do I get the barcode data from the scanner?

this is the scanner I have https://www.zebra.com/us/en/support-downloads/scanners/ultra-rugged-scanners/li3608-li3678.html


Solution

  • The data provided by BLE devices is called Characteristics. These data packages are specially formed, tightly packed byte arrays that encode specific values for specific Services. You can check out the Assigned Numbers at the official Bluetooth website. Here you'll find the defined (authoritative) GATT services and the belonging characteristics.

    For example, you have a BLE cycling computer that reports speed and cadence. You look up the Cycling Speed and Cadence item in the assigned numbers specs. The GATT Services (chapter 3.4) contains the UUID (0x1816) of the service. You then go to the Bluetooth Specifications page and do the following:

    • Search for Cycling Speed and Cadence service. The results will have the link to the specs of the CSC service. This contains useful info about the service, its characteristics and whatnot. You check out the characteristics and take note of whatever interests you. In our example it would be the CSC Measurement charactertics. The next point explains how to get the fields of this.
    • Search for GATT Specification Supplement. The results list will contain the many revisions of the GATT Specification Supplement. This doc contains all of the GATT characteristics fields, type info, etc. In the PDF search for CSC Measurement which should take you to chapter 3.61. It describes the CSC fields, the data size in octets (bytes), and additional info about the values.

    This was the Bluetooth LE part in general, now back to Android. Note, that you'll have to look up these fields in order to get the values from the characteristics. I'm just gonna assume that you already have the characteristic that you want to get the data from. Here's a quick sample that retrieves the wheel and crank revolutions (if available).

    BluetoothGattCharacteristic characteristic = ... ;
    
    int offset = 0; // we define the offset that is to be used when reading the next field
    
    // FORMAT_* values are constants in BluetoothGattCharacteristic
    // these represent the values you can find in the "Value Fields" table in the "Format" column
    int flags = characteristic.getIntValue(FORMAT_UINT8, offset);
    
    offset += 1; // UINT8 = 8 bits = 1 byte
    
    // we have to check the flags' 0th bit to see if C1 field exists 
    if ((flags & 1) != 0) {
        int cumulativeWheelRevolutions = characteristic.getIntValue(FORMAT_UINT32, offset);
        offset += 4; // UINT32 = 32 bits = 4 bytes
        
        int lastWheelEventTime = characteristic.getIntValue(FORMAT_UINT16, offset);
        offset += 2; // UINT16 = 16 bits = 2 bytes
    }
    
    // we have to check the flags' 1st bit to see if C2 field exists 
    if ((flags & 2) != 0) {
        int cumulativeCrankRevolutions = characteristic.getIntValue(FORMAT_UINT16, offset);
        offset += 2;
        
        int lastCrankEventTime = characteristic.getIntValue(FORMAT_UINT16, offset);
        offset += 2;
    }
    

    The flags field needs to be checked for specific bits because it is possible that the device does not report every type of data, e.g. it doesn't count the wheel revolutions. The selected characteristic's sheet always contains the relevant information about this field (if exists).

    It's also worth noting that the documentation says that

    The CSC Measurement characteristic (CSC refers to Cycling Speed and Cadence) is a variable length structure containing a Flags field and, based on the contents of the Flags field, may contain one or more additional fields [...]

    This is why you cannot assume that cumulative crank revolutions value is to be found at the 7 bytes (8 + 32 + 16 bits; 1 + 4 + 2 bytes respectively) offset and the offset should be counted as you progress along the fields.


    This was an example for reading Cycling Speed and Cadence values from a BLE device. You'll have to look up these available fields and values for every device (or rather the service) you want to support in your application. If the device is a special one and it cannot be found in this GATT directory, you'll need to consult the device's manual, SDK, or vendor for more info.