BLE example with Environmental Sensing service.
Dependencies: BSP_B-L475E-IOT01
Hello IoT BLE
This project uses Environmental Sensing Service and its characteristics:
Characteristic values can be read or they might be observed with notifications enabled.
Please note that this example uses slightly modified Environmental service.
Also there have been some opened issues regarding value types Issue 1 and Issue 2.
Note that this application was developed as a part of a larger project Hello IoT, but can be used as a standalone.
(Source files can be found at the end of this page.)
Running the application
The sample application can be seen on any BLE scanner on a smartphone. If you don't have a scanner on your phone, please install:
- nRF Master Control Panel for Android.
- LightBlue for iPhone
Hardware requirements are in the main readme.
Building instructions
Building with mbed CLI
If you'd like to use mbed CLI to build this, then you should refer to the main readme. The instructions here relate to using the Mbed Online Compiler.
Building with Mbed Online Compiler
In order to build this example in the Mbed Online Compiler, first import the example using the Import
button on the right hand side.
Next, select a platform to build for. This must either be a platform that supports BLE, for example the NRF51-DK, or one of the following:
List of platforms supporting Bluetooth Low Energy.
Or you must also add a piece of hardware and the supporting library that includes a Bluetooth Low Energy driver for that hardware, for example the K64F or NUCLEO_F401RE with the X-NUCLEO-IDB05A1
List of components supporting Bluetooth Low Energy.
Once you have selected your platform, compile the example and drag and drop the resulting binary onto your board.
Note: This example was tested with DISCO_L475VG_IOT01A (ref B-L475E-IOT01A), so when using onboard sensors instead of simulated ones, make sure you select this board.
For general instructions on using the Mbed Online Compiler, please see the mbed Handbook.
Checking for success
Note: Screen captures depicted below show what is expected from this example if the scanner used is nRF Connect. If you encounter any difficulties consider trying another scanner or another version of nRF Connect. Alternative scanners may require reference to their manuals.
- Build the application and install it on your board as explained in the building instructions.
- Open the BLE scanner on your phone.
- Start a scan and connect to a device named IoT_SENSOR.
- Discover the services and the characteristics on the device. The Environmental Sensing service has the UUID
0x181A
with characteristics Temperature0x2A6E
, Humidity0x2A6F
and Pressure0x2A6D
.
- At this point you can read or register for notifications. In figure below, all characteristics have been registered for notification and their values should change every second.
Revision 0:0681ebb27b3c, committed 2018-12-29
- Comitter:
- jernej_vrscaj
- Date:
- Sat Dec 29 13:33:02 2018 +0000
- Commit message:
- Initial commit.
Changed in this revision
diff -r 000000000000 -r 0681ebb27b3c BSP_B-L475E-IOT01.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BSP_B-L475E-IOT01.lib Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,1 @@ +http://developer.mbed.org/teams/ST/code/BSP_B-L475E-IOT01/#9dfa42666f03
diff -r 000000000000 -r 0681ebb27b3c mbed-os.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#2fd0c5cfbd83fce62da6308f9d64c0ab64e1f0d6
diff -r 000000000000 -r 0681ebb27b3c mbed_app.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app.json Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,16 @@ +{ + "target_overrides": { + "K64F": { + "target.features_add": ["BLE"], + "target.extra_labels_add": ["ST_BLUENRG"] + }, + "NUCLEO_F401RE": { + "target.features_add": ["BLE"], + "target.extra_labels_add": ["ST_BLUENRG"] + }, + "DISCO_L475VG_IOT01A": { + "target.features_add": ["BLE"], + "target.extra_labels_add": ["ST_BLUENRG"] + } + } +}
diff -r 000000000000 -r 0681ebb27b3c module.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/module.json Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,15 @@ +{ + "name": "Hello_IoT_BLE", + "version": "0.0.1", + "description": "Modified EnvironmentalService with Temperature, Humidity and Pressure characteristics with read and notification propreties", + "licenses": [ + { + "url": "https://spdx.org/licenses/Apache-2.0", + "type": "Apache-2.0" + } + ], + "dependencies": { + "ble": "^2.0.0" + }, + "bin": "./source" +}
diff -r 000000000000 -r 0681ebb27b3c shields/TARGET_ST_BLUENRG.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/shields/TARGET_ST_BLUENRG.lib Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/ble-x-nucleo-idb0xa1/#bcef7fa68b2deaa8ae7b2dbbe2648fdc8bae290b
diff -r 000000000000 -r 0681ebb27b3c source/EnvironmentalService_v2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/EnvironmentalService_v2.h Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,124 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BLE_ENVIRONMENTAL_SERVICE_H__ +#define __BLE_ENVIRONMENTAL_SERVICE_H__ + +#include "ble/BLE.h" +//#include "debug.h" + +/** +* @note +* Modified service (refer to EnvironmentalService.h): +* - notifications added +* - conversion to temperature, humidity and pressure integer values done in main.cpp +* - updateTemperature() input argument type changed from float to TemperatureType_t +*/ + +/** +* @class EnvironmentalService +* @brief BLE Environmental Service. This service provides temperature, humidity and pressure measurement. +* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.environmental_sensing.xml +* Temperature: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature.xml +* Humidity: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.humidity.xml +* Pressure: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.pressure.xml +*/ +class EnvironmentalService { +public: + typedef int16_t TemperatureType_t; + typedef uint16_t HumidityType_t; + typedef uint32_t PressureType_t; + + /** + * @brief EnvironmentalService constructor. + * @param ble Reference to BLE device. + * @param temperature_en Enable this characteristic. + * @param humidity_en Enable this characteristic. + * @param pressure_en Enable this characteristic. + */ + EnvironmentalService(BLE& _ble) : + ble(_ble), + temperatureCharacteristic(GattCharacteristic::UUID_TEMPERATURE_CHAR, &temperature, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + humidityCharacteristic(GattCharacteristic::UUID_HUMIDITY_CHAR, &humidity, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + pressureCharacteristic(GattCharacteristic::UUID_PRESSURE_CHAR, &pressure, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) + { + static bool serviceAdded = false; /* We should only ever need to add the information service once. */ + if (serviceAdded) { + return; + } + + GattCharacteristic *charTable[] = { &temperatureCharacteristic, + &humidityCharacteristic, + &pressureCharacteristic }; + + GattService environmentalService(GattService::UUID_ENVIRONMENTAL_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.gattServer().addService(environmentalService); + serviceAdded = true; + } + + /** + * @brief Update temperature characteristic. + * @param newTemperatureVal New temperature measurement. + */ + void updateTemperature(TemperatureType_t newTemperatureVal) + { + temperature = newTemperatureVal; +#ifdef DEBUG + pc.printf("T_char = %i\r\n", temperature); +#endif + ble.gattServer().write(temperatureCharacteristic.getValueHandle(), (uint8_t *) &temperature, sizeof(TemperatureType_t)); + } + + /** + * @brief Update humidity characteristic. + * @param newHumidityVal New humidity measurement. + */ + void updateHumidity(HumidityType_t newHumidityVal) + { + humidity = newHumidityVal; +#ifdef DEBUG + pc.printf("H_char = %u\r\n", humidity); +#endif + ble.gattServer().write(humidityCharacteristic.getValueHandle(), (uint8_t *) &humidity, sizeof(HumidityType_t)); + } + + /** + * @brief Update pressure characteristic. + * @param newPressureVal New pressure measurement. + */ + void updatePressure(PressureType_t newPressureVal) + { + pressure = newPressureVal; +#ifdef DEBUG + pc.printf("P_char = %u\r\n", pressure); +#endif + ble.gattServer().write(pressureCharacteristic.getValueHandle(), (uint8_t *) &pressure, sizeof(PressureType_t)); + } + +private: + BLE& ble; + + TemperatureType_t temperature; + HumidityType_t humidity; + PressureType_t pressure; + + ReadOnlyGattCharacteristic<TemperatureType_t> temperatureCharacteristic; + ReadOnlyGattCharacteristic<HumidityType_t> humidityCharacteristic; + ReadOnlyGattCharacteristic<PressureType_t> pressureCharacteristic; +}; + +#endif /* #ifndef __BLE_ENVIRONMENTAL_SERVICE_H__*/
diff -r 000000000000 -r 0681ebb27b3c source/debug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/debug.h Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,15 @@ +/* +* @ debug.h +* Include this file for debugging purposes. +*/ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#define DEBUG + +#ifdef DEBUG +Serial pc(USBTX, USBRX); +#endif + +#endif /* #ifndef __DEBUG_H__ */ \ No newline at end of file
diff -r 000000000000 -r 0681ebb27b3c source/main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/main.cpp Sat Dec 29 13:33:02 2018 +0000 @@ -0,0 +1,222 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <events/mbed_events.h> +#include "mbed.h" +#include "ble/BLE.h" +#include "EnvironmentalService_v2.h" +//#include "debug.h" + +// Uncomment this line if you want to use the board temperature sensor instead of +// a simulated one. +#define USE_BOARD_SENSORS + +#ifdef USE_BOARD_SENSORS +#include "stm32l475e_iot01_tsensor.h" +#include "stm32l475e_iot01_hsensor.h" +#include "stm32l475e_iot01_psensor.h" +#endif + +#define ADVERTISING_INTERVAL 1000 +#define UPDATE_VALUES_INTERVAL 1000 + +DigitalOut led1(LED1, 0); +DigitalOut led2(LED2, 0); + +const static char DEVICE_NAME[] = "IoT_SENSOR"; +static const uint16_t uuid16_list[] = {GattService::UUID_ENVIRONMENTAL_SERVICE}; +static EnvironmentalService *environmentServicePtr; + +/* initial dummy values */ +static float currentTemperature = -15.0; +static float currentHumidity = 0.0; +static float currentPressure = 260.0 * 100; // hPa -> Pascal + +static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE); +static int id_adv; +static int id_conn; + +void updateSensorValue(void) { + + static int16_t lastTemperature; + static uint16_t lastHumidity; + static uint32_t lastPressure; + + int16_t tempTemperature = 0; + uint16_t tempHumidity = 0; + uint32_t tempPressure = 0; + +#ifdef USE_BOARD_SENSORS + currentTemperature = BSP_TSENSOR_ReadTemp(); + currentHumidity = BSP_HSENSOR_ReadHumidity(); + currentPressure = BSP_PSENSOR_ReadPressure(); + currentPressure = currentPressure*100; // hPa -> Pascal +#else + /* dummy values */ + currentTemperature = (currentTemperature + 0.1f > 43.0f) ? -15.0f : currentTemperature + 0.1f; + currentHumidity = (currentHumidity + 0.1f > 100.0f) ? 0.0f : currentHumidity + 0.1f; + currentPressure = (currentPressure + 10 > 126000.0f) ? 26000.0f : currentPressure + 10; +#endif + +#ifdef DEBUG + pc.printf("\r\n"); + pc.printf("T_sensor = %.2f C\r\n", ((int16_t)(currentTemperature*100))/100.0); + pc.printf("H_sensor = %.2f %%\r\n", ((uint16_t)(currentHumidity*100))/100.0); + pc.printf("P_sensor = %.1f Pa\r\n", ((uint32_t)(currentPressure*10))/10.0); + pc.printf("\r\n"); +#endif + + tempTemperature = (int16_t)(currentTemperature*100); + tempHumidity = (uint16_t)(currentHumidity*100); + tempPressure = (uint32_t)(currentPressure*10); + + /* Update char values, but only if they differ from previous */ + if(tempTemperature != lastTemperature) + { + environmentServicePtr->updateTemperature(tempTemperature); + lastTemperature = tempTemperature; + } + if(tempHumidity != lastHumidity) + { + environmentServicePtr->updateHumidity(tempHumidity); + lastHumidity = tempHumidity; + } + if(tempPressure != lastPressure) + { + environmentServicePtr->updatePressure(tempPressure); + lastPressure = tempPressure; + } +} + +void periodicCallback(void) +{ + /* Do blinky on LED1 while advertising */ + led1 = !led1; + wait(0.25); + led1 = !led1; +} + +/* On Connection event start updating sensor values */ +void connectionCallback(const Gap::ConnectionCallbackParams_t *) +{ +#ifdef DEBUG + pc.printf("\r\n"); + pc.printf("Connection Event.\r\n"); +#endif + eventQueue.cancel(id_adv); + led1 = 0; + led2 = 1; // LED2 on when connected + updateSensorValue(); + id_conn = eventQueue.call_every(UPDATE_VALUES_INTERVAL, updateSensorValue); +} + +/* Restart Advertising on disconnection*/ +void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *) +{ + eventQueue.cancel(id_conn); + led2 = 0; // LED2 off when disconnected + BLE::Instance().gap().startAdvertising(); + id_adv = eventQueue.call_every(ADVERTISING_INTERVAL, periodicCallback); + + +#ifdef DEBUG + pc.printf("\r\n"); + pc.printf("Disconnection Event - Start Advertising...\r\n"); +#endif +} + +void onBleInitError(BLE &ble, ble_error_t error) +{ + /* Initialization error handling should go here */ +#ifdef DEBUG + pc.printf("BLE Init Error: %u\r\n", error); +#endif + + while(1) + { /* Do blinky on LED2 on Error */ + led2 = !led2; + wait(0.25); + } +} + +void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) +{ + BLE& ble = params->ble; + ble_error_t error = params->error; + + if (error != BLE_ERROR_NONE) { + onBleInitError(ble, error); + return; + } + + /* Ensure that it is the default instance of BLE */ + if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { + return; + } + + /* Callback function when connected */ + ble.gap().onConnection(connectionCallback); + /* Callback function when disconnected */ + ble.gap().onDisconnection(disconnectionCallback); + + /* Setup primary service. */ + environmentServicePtr = new EnvironmentalService(ble); + + /* setup advertising */ + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.gap().setAdvertisingInterval(ADVERTISING_INTERVAL); + ble.gap().startAdvertising(); +} + +void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) { + BLE &ble = BLE::Instance(); + eventQueue.call(Callback<void()>(&ble, &BLE::processEvents)); +} + +int main() +{ +#ifdef USE_BOARD_SENSORS + BSP_TSENSOR_Init(); + BSP_HSENSOR_Init(); + BSP_PSENSOR_Init(); +#endif + +#ifdef DEBUG + pc.printf("== BLE - EnvironmentalService ==\r\n"); +#endif + + id_adv = eventQueue.call_every(ADVERTISING_INTERVAL, periodicCallback); + + BLE &ble = BLE::Instance(); + ble.onEventsToProcess(scheduleBleEventsProcessing); + +#ifdef DEBUG + pc.printf("Init BLE...\r\n"); +#endif + + ble.init(bleInitComplete); + +#ifdef DEBUG + pc.printf("Init complete - Advertising...\r\n"); +#endif + + eventQueue.dispatch(); + + return 0; +}