Contents


Overview

Skill Level: Intermediate

More willingness to have a go than skill required

Trying out the very latest functionality of the ESP32/Arduino SDK this demonstrates how to connect Bluetooth Low Energy beacons to the Watson IoT Platform.

Step-by-step

  1. Before you start

    Be warned, this uses bleeding edge functionality of the ESP32/Arduino SDK and API's that will certainly change. It is however an interesting and much anticiapted area of functionality so it seems worthwhile showing a working example now so you can get your hands dirty experimenting with it. 

    Note also that this will skip over the basics of connecting to the Watson IoT Platform with an ESP32/Arduino. For more help on that see the previous developerWorks article “Connect an ESP32 to the Watson IoT Platform!“.

     

  2. A quick introduction

    Bluetooth Low Energy provides a way for devices to broadcast small messages to nearby devices with very low power. These devices are know as BLE “beacons” and they can do interesting things such as having something happen when a device comes within proximity to a certain location, or to have battery powered sensors which can broadcast readings for years without changing a battery.

    Commonly these beacon messages are received by BLE enable devices such as smartphones but that is often not terribly convenient, for example, your temperature sensor only has its data captured when you happen to have your smartphone nearby.

    You can use custom propriety hardware and SDK's to receive and process the beacon messages but these are often expensive and/or far to complex for a non-expert to get to grips with. The Arduino functionality for the ESP32 microcontroller changes this and provides a cheap and easy to use device with both Bluetooth and WiFi capabilities which can bridge BLE beacon messages to the Internet.

  3. Scenario

    What this is going to show is how to make a BLE beacon which broadcasts a simple message, and a BLE-WiFi Gateway which receives the beacon messages and publishes them with MQTT to the Watson IoT Platform. You could use any other IoT platform but the Watson one is free to get started with and using its “Quickstart” service means there isn't even any sign-up required to demonstrate something running.

    This is going to have two parts:

    1. the beacon device broadcasting messages
    2. the gateway device which receives the broadcast messages and forwards them to the Internet
  4. The BLE beacon

    The test BLE beacon is a simple Arduino sketch that spends most of its time deepSleeping and waking up regularly to send a BLE advertising message which inludes a count of the number if times its woken up. I've used an ESP32 module on an adapter plate, powered by a couple of AA batteries. While asleep it uses about 5 microAmps and to wakeup – send the message – and go back to sleep takes about 120 milliseconds, so it should run for quite a long time powered by the AA's.

    B1

    You could of course use any other type of ESP32 module and the “development” style boards with built-in USB support are much easier to get going with, however the extra components included on the board mean is uses a lot more current while deep sleeping so they're not really suitable as a long term battery powered device.

    The Arduino code to run on that is in Github here, and looks like:

    #include "SimpleBLE.h"

    #define SLEEP_SECS 30

    SimpleBLE ble;

    String beaconMsg = "ESP32xx"; // the x's get overwritten

    // this variable maintained over deep sleeps
    RTC_DATA_ATTR static uint16_t wakeupCount = 0;

    void setup() {
    Serial.begin(115200);

    byte* bytes = beaconMsg.c_str();
    bytes[5] = wakeupCount;
    bytes[6] = wakeupCount >> 8;

    ble.begin(beaconMsg);

    // a delay before shutdown otherwise the packets aren't sent completely
    // the delay required seems to depend on the payload length
    delay(20);

    ble.end();

    Serial.printf("Wakeup count=%i, awake for %i ms, deep sleeping for %i secs...\n",
    wakeupCount++, millis(), SLEEP_SECS);
    esp_deep_sleep(SLEEP_SECS * 1000000);
    }

    void loop() {
    // doesn't ever get here
    }

    You can see the Arduino code is pretty simple thanks to the SimpleBLE library. It is a little hacky though with delay and the way the sensor value is encoded into the message. This is because the current SimpleBLE library isn't really designed for this use and doesn't yet provide any way to include advertising data in the broadcast message. I expect this will change as the ESP32/Arduino API's are enhanced, but for now this gives enough to get going with for this BLE Gateway example.

    The SimpleBLE library was added to the ESP32/Arduino code on the 23 February so you need to get the ESP32/Arduino code from after then.

    If you have other Bluetooth devices that broadcast BLE advertising messages these might work with this example Gateway too. I've a “Tempo” which is a Bluetooth temperature sensor which does work, and I've included code in the Gateway to show extracting the tempo temperature readings.

    Tempo

  5. The BLE-Watson Gateway

    For the gateway device I've used a Sparkfun ESP32 Thing board. You could use any other type of ESP32 but the “development” style boards are easiest to get going with and they also enable easily powering the gateway from something like a USB phone charger.

    GW1

    The Gateway Arduino sketch is larger and includes lots of boiler-plate Bluetooth code, so I'll only show an extract here of the more interesting parts. The complete sketch can be found in Github here.

    #include <stdint.h>
    #include <string.h>
    #include <stdbool.h>
    #include <stdio.h>
    #include "controller.h"

    #include "bt.h"
    #include "bt_trace.h"
    #include "bt_types.h"
    #include "btm_api.h"
    #include "bta_api.h"
    #include "bta_gatt_api.h"
    #include "esp_gap_ble_api.h"
    #include "esp_gattc_api.h"
    #include "esp_gatt_defs.h"
    #include "esp_bt_main.h"

    #include <WiFi.h>
    #include <PubSubClient.h>

    //-------- Customise these values -----------
    const char* ssid = "<yourWifiSSID>";
    const char* password = "<yourWifiPassword>";

    #define ORG "quickstart" // your organization or "quickstart"
    #define DEVICE_TYPE "ESP32" // your registered device type or not used with "quickstart"
    #define DEVICE_ID "myEsp32" // use this default for quickstart or customize to your registered device id
    #define TOKEN "<yourDeviceToken>" // your device token or not used with "quickstart"
    //-------- Customise the above values --------

    char server[] = ORG ".messaging.internetofthings.ibmcloud.com";
    char topic[] = "iot-2/evt/status/fmt/json";
    char authMethod[] = "use-token-auth";
    char token[] = TOKEN;
    char clientId[] = "d:" ORG ":" DEVICE_TYPE ":" DEVICE_ID;

    WiFiClient wifiClient;
    PubSubClient client(server, 1883, wifiClient);

    void setup() {
    Serial.begin(115200);
    initWiFi();
    if (btStart()) {
    gattc_client_test();
    }
    }

    void loop() {
    // nothing to do here
    }

    void gotReading(uint8_t *adv_data) {
    // This is from the test ESP32 beacon
    uint16_t wakeupCount = (int16_t)((adv_data[11] << 8) | adv_data[10]);
    Serial.printf("My BLE device wakeup count=%i\n", wakeupCount);
    doPublish("wakeup", String(wakeupCount));
    }

    void gotTempoReading(uint8_t *adv_data) {
    // My "Tempo" device sends min, current, and max temperature in two byte fields
    float temp = ((int16_t)((adv_data[27] << 8) | adv_data[26])) / 10.0;
    Serial.printf("My Tempo temp=%0.1f\n", temp);
    doPublish("temp", String(temp, 1));
    }

    void doPublish(String id, String value) {
    if (!!!client.connected()) {
    Serial.print("Reconnecting client to "); Serial.println(server);
    while (!!!client.connect(clientId, authMethod, token)) {
    Serial.print(".");
    delay(500);
    }
    Serial.println();
    }

    String payload = "{ \"d\" : { \"" + id + "\":" + value + "}}";
    Serial.print("Publishing payload: "); Serial.println(payload);

    if (client.publish(topic, (char*) payload.c_str())) {
    Serial.println("Publish ok");
    } else {
    Serial.println("Publish failed");
    }
    }

    void initWiFi() {
    Serial.print("Connecting to "); Serial.print(ssid);
    if (strcmp (WiFi.SSID().c_str(), ssid) != 0) {
    WiFi.begin(ssid, password);
    }
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    Serial.println(""); Serial.print("WiFi connected, IP address: "); Serial.println(WiFi.localIP());
    }

    // remaining code from the IDF GATT Client example only change in the esp_gap_cb function...

     

  6. Putting it all together

    Once you've got those devices all programmed and powered on the remote Bluetooth messages should start appearing on the Watson IoT Platform Quickstart service.

    Here is what my devices look like:

    pic1

    And going to the Quickstart URL – https://quickstart.internetofthings.ibmcloud.com/#/device/myEsp32/sensor/ – you should see somehting like this:

    Quickstart1

     

  7. Conclusion

    I hope you find this an interesting and useful look at how to start experimenting with using BLE devices and the Watson IoT Platform. The beacon is presently a little unreliable, I expect due to the abuse of the SimpleBLE library, and not all of the messages end up at the Gateway, the ESP32 based Gateway appears to work well though and it successfully receives all the messages from the BLE temperature sensor device.

    I'll try to keep the example code in Github updated as the API's evolve so check back here to see how things progress. I'm also interested to hear your thoughts and if you try this how you get on so please do use the comment section below.    

1 Comment on "Experiments with Bluetooth and Watson"

  1. Note that this example is presently broken when using the latest Arduino/ESP32 SDK, with the compile failing with a “Sketch too big” error. This appears to be a problem with the Arduino/ESP32 SDK and is being worked on here: https://github.com/ibm-watson-iot/device-arduino/issues/15

Join The Discussion