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:

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.

nRF Scan

  • Discover the services and the characteristics on the device. The Environmental Sensing service has the UUID 0x181A with characteristics Temperature 0x2A6E, Humidity 0x2A6F and Pressure 0x2A6D.

nRF Service

  • 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.

nRF Notif

Files at this revision

API Documentation at this revision

Comitter:
jernej_vrscaj
Date:
Sat Dec 29 13:33:02 2018 +0000
Commit message:
Initial commit.

Changed in this revision

BSP_B-L475E-IOT01.lib Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
module.json Show annotated file Show diff for this revision Revisions of this file
shields/TARGET_ST_BLUENRG.lib Show annotated file Show diff for this revision Revisions of this file
source/EnvironmentalService_v2.h Show annotated file Show diff for this revision Revisions of this file
source/debug.h Show annotated file Show diff for this revision Revisions of this file
source/main.cpp Show annotated file Show diff for this revision Revisions of this file
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;
+}