Bluetooth LE - blusensor/blusensor-developer GitHub Wiki
Introduction
You can use any Bluetooth Low Energy API to access sensor data, download logged data, configure alarms, and much more on our bluSensor® devices.
For more information how to use Bluetooth Low Energy on your platform we refer to the platform’s API documents.
Bluetooth Low Energy SDKs
- iOS: https://developer.apple.com/bluetooth/
- Android: https://developer.android.com/guide/topics/connectivity/bluetooth-le
- Xamarin: https://developer.xamarin.com/api/namespace/Android.Bluetooth.LE/
- Xamarin: https://developer.xamarin.com/api/namespace/CoreBluetooth/
- Web: https://github.com/WebBluetoothCG/web-bluetooth/blob/gh-pages/implementation-status.md
Official Bluetooth SIG ID
Almendo Technologies GmbH (0x06E8)
Please find our official ID here: Bluetooth - Company ID
Advertisements
Service UUIDs
bluSensor® devices are using the following unique service UUIDs
a8a82630-10a4-11e3-ab8c-f23c91aec05e
Advertisement Scan Record
All our sensors are broadcasting sensor data using manufacturer specific data.
To filter for bluSensor® devices during discovery you can use the service UUID that is part of the Scan Record. Please note that using scan filters on Android is buggy and it is better to implement your own filter mechanism using the provided UUID from the Scan Record.
Service UUID (Scan Record)
a8a82630-10a4-11e3-ab8c-f23c91aec05e
Manufacturer Specific Data (Scan Record)
bluSensor® AIR (1st generation) (legacy)
sensor type | alarm code | sensor data |
---|---|---|
1 byte | 1 byte | variable bytes |
bluSensor® BSPx models (2nd generation)
company ID | company ID | protocol version | device type | device model | device hw rev | status bits | status code | sensor data |
---|---|---|---|---|---|---|---|---|
0x06 | 0xE8 | 1 byte | 1 byte | 1 byte | 1 byte | 1 byte | 1 byte | 10 bytes |
Company ID
This is our official company identifier assigned by Bluetooth SIG (https://www.bluetooth.com/)
Protocol Version
This is the sensor's protocol version (current version=1)
Device Types
device type | description |
---|---|
01 | Humidity & Temperature |
02 | Motion (Accelerometer) |
03 | Motion 3D Fusion (Euler) |
04 | Air Flow |
05 | Light Ambient |
06 | Motion (Accelerometer, Magnetometer, Gyroscope) |
07 | Motion (Accelerometer) |
08 | Motion Sport |
09 | Air Quality Index |
10 | Air Quality |
11 | Motion Counter |
12 | Temperature |
13 | Temperature (reserved) |
14 | Infrared Array Camera |
15 | Particulate Matter |
16 | Proximity Distance |
17 | Light Ambient |
18 | Magnetometer |
19 | People Detection |
20 | People Detection (reserved) |
21 | Proximity Counter |
22 | Bearing |
23 | HVAC |
24 | Air Quality CO2 |
25 | Relay |
Device Models (legacy)
device model | description | type |
---|---|---|
01 | bluSensor® AIR | Humidity & Temperature |
Device Models (new)
device model | description | type |
---|---|---|
09 | bluSensor® BSP02AIR | Humidity & Temperature |
10 | bluSensor® BSP02AIQ | Air Quality |
11 | bluSensor® BSP02AIX | Air Quality Index |
16 | bluSensor® BSP03AIX | Air Quality Index |
17 | bluSensor® BSP03AIXC | Air Quality CO2 |
18 | bluSensor® BSP03PM | Particilate Matter |
19 | bluSensor® BSP03TEM | Temperature Probe |
Device Hardware Revision
This is the hardware revision number
Device Status Bits
These are internal status bits
Device Status Codes
These are internal status codes
Device Sensor Data
This contains device specific sensor data (depends on device type)
Device Sensor Data - Humidity and Temperature
sensor state | temperature | humidity |
---|---|---|
1 byte | 2 byte (signed) | 2 byte |
Example Conversion bluSensor BSPx (Android/Java)
float humidity = ((int) (sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00)) / 100.0f;
short temperature_signed = (short) ((sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00));
float temperature = temperature_signed / 100.0f;
Example Conversion bluSensor AIR (Android/Java)
private float convertHumidty(byte[] data) {
int hum;
float rHVal;
hum = (data[2] & 0xff) | ((data[3] << 8) & 0xff00);
rHVal = -6.0f + 125.0f * (float) hum / (float) 65536;
return rHVal;
}
private float converTemperature(byte[] data) {
int temp;
float tempVal;
temp = (data[0] & 0xff) | ((data[1] << 8) & 0xff00);
tempVal = -46.85f + 175.72f * (float) temp / (float) 65536;
return tempVal;
}
Device Sensor Data - Air Quality
sensor state | temperature | humidity | eco2 | tvoc | aiq index |
---|---|---|---|---|---|
1 byte | 2 byte (signed) | 2 byte | 2 byte | 2 byte | 1 byte |
Example Conversion (Android/Java):
//TEMPERATURE (signed, factor 100)
short temperature_signed = (short) ((sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00));
float tem = temperature_signed / 100.0;
//HUMIDITY (unsigned, factor 100)
float hum = ((int) (sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00)) / 100.0;
//CO2 (unsigned, factor 1)
int co2 = (int) (sensorData[4] & 0xff) | ((sensorData[5] << 8) & 0xff00);
//TVOC (unsigned, factor 1)
int tvoc = (int) (sensorData[6] & 0xff) | ((sensorData[7] << 8) & 0xff00);
//AIQ INDEX (unsigned, factor 1)
int aiq = (int) sensorData[8];
Device Sensor Data - Air Quality Index
sensor state | temperature | humidity | unused | voc_index |
---|---|---|---|---|
1 byte | 2 byte (signed) | 2 byte | 2 byte | 2 byte |
Example Conversion (Android/Java):
//TEMPERATURE (signed, factor 100)
short temperature_signed = (short) ((sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00));
float tem = temperature_signed / 100.0;
//HUMIDITY (unsigned, factor 100)
float hum = ((int) (sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00)) / 100.0;
//VOC INDEX
int voc_index = (int) (sensorData[6] & 0xff) | ((sensorData[7] << 8) & 0xff00);
Device Sensor Data - Air Quality CO2
sensor state | temperature | humidity | co2 | voc_index |
---|---|---|---|---|
1 byte | 2 byte (signed) | 2 byte | 2 byte | 2 byte |
Example Conversion (Android/Java):
//TEMPERATURE (signed, factor 100)
short temperature_signed = (short) ((sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00));
float tem = temperature_signed / 100.0;
//HUMIDITY (unsigned, factor 100)
float hum = ((int) (sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00)) / 100.0;
//CO2
int co2 = (int) (sensorData[4] & 0xff) | ((sensorData[5] << 8) & 0xff00);
//VOC INDEX
int voc_index = (int) (sensorData[6] & 0xff) | ((sensorData[7] << 8) & 0xff00);
Device Sensor Data - Particulate Matter
sensor state | PM 1.0 | PM2.5 | PM4.0 | PM 10.0 |
---|---|---|---|---|
1 byte | 2 byte | 2 byte | 2 byte | 2 byte |
Example Conversion (Android/Java):
float pm_1 = ((int)(sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00))/10.0;
float pm_2 = measurement.value_1=((int)(sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00))/10.0;
float pm_4 = measurement.value_2=((int)(sensorData[4] & 0xff) | ((sensorData[5] << 8) & 0xff00))/10.0;
float pm_10 = measurement.value_3=((int)(sensorData[6] & 0xff) | ((sensorData[7] << 8) & 0xff00))/10.0;
Services
Device Information Service
180a
Please find official specification here: Bluetooth - Device Information Service
Battery Service
180f
Please find official specification here: Bluetooth - Battery Service
bluSensor® Service
a8a82630-10a4-11e3-ab8c-f23c91aec05e
All bluSensor® devices implement the following characteristics. The logger functionality needs some configurations to be opertional. See details below.
Sensor Data
a8a82631-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read/notify
- LENGTH: 4 bytes
- FORMAT: measurement (see how to convert sensor data section below)
LED
a8a82645-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: write
- LENGTH: 1 byte
- FORMAT: 0x01 for blink
MAC Address
a8a82643-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read
- LENGTH: 6 bytes
- FORMAT: Mac Address Bytes XX:XX:XX:XX:XX:XX
Logger
The logger of bluSensor needs some configurations to be operational. The configuration steps are.
Enable Logging
- set time reference from your smartphone
- configure logger interval
- enable logging
Download Log
- listen to notifications on sensor data characteristic on your smartphone
- start sending log data by using logger control characteristic
- store ordered measurements on your smartphone (4-byte chunks)
Build Log Timestamps
- calculate timestamp for each measurement using time reference
- 1st measurement = time reference
- 2nd measurement = time reference + logger interval
- 3rd measurement = time reference + logger interval * 2
- and so on...
Logger Time Reference
a8a82636-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read/write
- LENGTH: 4 bytes
- FORMAT: any reference timestamp that your app can handle. You need this timestamp later on to calculate the timestamps of each individual measurement.
Logger Interval
a8a82634-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read/write
- LENGTH: 2 bytes
- FORMAT: interval in seconds. It is mandatory to use a logger interval of 5min/10min/15min/30min. You CANNOT use any other interval!
Logger On/Off
CHARACTERISTIC: a8a82633-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read/write
- LENGTH: 1 byte
- FORMAT: 0=OFF 1=ON
Logger Control
a8a82635-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: write
- LENGTH: 1 byte
- FORMAT:
- 1=SEND (sends log via Logger Data Characteristic)
- 2=RESET (delete data and stop logger)
- 3=REBOOT (reboot device and stop logger)
Logger Data
a8a82637-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: notify
- LENGTH: 16 bytes
- FORMAT: 4 measurements each 4bytes long are sent (see previous chapter for format)
Logger Flash Size
a8a82950-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read
- LENGTH: 4 bytes
- FORMAT: size of flash memory in bytes
Logger Usage
a8a82646-10a4-11e3-ab8c-f23c91aec05e
- PROPERTIES: read
- LENGTH: 4 bytes
- FORMAT: size of used flash memory in bytes
Converting Sensor Data
Humidity & Temperature (1st generation) (legacy)
The sensor data is 4 bytes and can be converted using the following mechanism and formular.
int hum = (data[2] & 0xff) | ((data[3] << 8) & 0xff00);
float relHum = -6.0f + 125.0f * (float) hum / (float) 65536;
int temp = (data[0] & 0xff) | ((data[1] << 8) & 0xff00);
float tempC = -46.85f + 175.72f * (float) temp / (float) 65536;
Example:
0100ac667a63 => 42.5% and 23.5°C
Humidity & Temperature, CO2, TVOC (2nd generation)
//TEMPERATURE (signed, factor 100)
short temperature_signed = (short) ((sensorData[0] & 0xff) | ((sensorData[1] << 8) & 0xff00));
float tem = temperature_signed / 100.0;
//HUMIDITY (unsigned, factor 100
float hum = ((int) (sensorData[2] & 0xff) | ((sensorData[3] << 8) & 0xff00)) / 100.0;
//CO2 (unsigned, factor 1)
int co2 = (int) (sensorData[4] & 0xff) | ((sensorData[5] << 8) & 0xff00);
//TVOC (unsigned, factor 1)
int tvoc = (int) (sensorData[6] & 0xff) | ((sensorData[7] << 8) & 0xff00);
//AIQ INDEX (unsigned, factor 1)
int aiq = (int) sensorData[8];
SDK Documentation coming soon:
- Accelerometer
- 3D Fusion (Euler)
- Air Flow
- Ambient Light
- Accelerometer, Magnetometer, Gyroscope
- Accelerometer (Low Energy)
- ShakeIt Sports Tracker
- Air Quality (Industrial)
- Air Quality
- Usage Counter
- Temperature Probe (PTC)
- Temperature Probe (NTC)
- Infrared Array Camera
- Particulate Matter
- Distance
- Ambient Light
- Highspeed Magnetometer
- People Presence Detector
- People Counter
- Distance Counter
Development Tools
There are many tools available for iOS and Android that you can use for testing and development.
Here are some of the tools available:
- BLExplr (iOS)
- LightBlue Explorer (iOS and Android)
- nRF Connect (iOS and Android)
- Blue Gecko (iOS and Android)
Troubleshooting
Unfortunately, Android's BLE API is much less convenient than its iOS counterpart. Here is a short summary what you need to consider:
- BLE GATTs commands need to be queued. Android does not support calling multiple GATT commands at a time.
- A really good blog for Android and BLE can be found here: