
Recently, I’ve been looking through the internet for a way to interact with a microcontroller without a need to develop applications for all sorts of operating systems. What I’ve found is an interesting protocol called Web Bluetooth API
. This protocol lets the web browsers access the physical Bluetooth interface. It works on PCs and phones – you can find the compatibility here. It is worth mentioning that the protocol is still under heavy development.
There are two distinct safety features of this protocol:
- it works only on websites with a secure connection (HTTPS)
- the only way to trigger a scan for devices is a user gesture
The above make the development a little harder but definitely are useful from users safety perspective.
ESP32 example
In my experiment I’ve used ESP32 dev board
from Espressif Systems
. I’ve flashed the microcontroller with a slightly modified version of the ESP_notify
sketch that comes with a default ESP32 library for Arduino framework.
/*
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
Ported to Arduino ESP32 by Evandro Copercini
updated by chegewara
Create a BLE server that, once we receive a connection, will send periodic notifications.
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
The design of creating the BLE server is:
1. Create a BLE Server
2. Create a BLE Service
3. Create a BLE Characteristic on the Service
4. Create a BLE Descriptor on the characteristic
5. Start the service.
6. Start advertising.
A connect hander associated with the server starts a background task that performs notification
every couple of seconds.
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLEServer* pServer = NULL;
BLECharacteristic* pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
uint32_t value = 115;
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("Device connected!");
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Device disconnected!");
}
};
void setup() {
Serial.begin(115200);
// Create the BLE Device
BLEDevice::init("ESP32");
// Create the BLE Server
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE
);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
// Create a BLE Descriptor
pCharacteristic->addDescriptor(new BLE2902());
// Start the service
pService->start();
// Start advertising
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(false);
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
BLEDevice::startAdvertising();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
// notify changed value
if (deviceConnected) {
pCharacteristic->setValue((uint8_t*)&value, 4);
pCharacteristic->notify();
// value++;
delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}
// disconnecting
if (!deviceConnected && oldDeviceConnected) {
delay(500); // give the bluetooth stack the chance to get things ready
pServer->startAdvertising(); // restart advertising
Serial.println("start advertising");
oldDeviceConnected = deviceConnected;
}
// connecting
if (deviceConnected && !oldDeviceConnected) {
// do stuff here on connecting
oldDeviceConnected = deviceConnected;
}
}
What this code does is creating the server’s GATT Bluetooth profile with a service and custom characteristic. This characteristic lets the client read and write.
For this experiment, the microcontroller serves the value of 115
on a client’s read.
Website example
Web Bluetooth API is based on javascript promises which make working with Bluetooth asynchronously easier. The simple exemplary website created for demo purposes consists of a header, connect button, and a log div.
Website example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bluetooth test</title>
</head>
<body>
<h1> ESP32 BLE test web bluetooth API example </h1>
<button>CONNECT</button>
<h1></h1>
<div id="logs">Log output:</div>
</body>
</html>
<script>
// Function to display logs on the website instead of console
function Log(msg){
document.querySelector('#logs').innerHTML =
document.querySelector('#logs').innerHTML + "<br>" + msg
}
document.querySelector('button').addEventListener('click', async event => {
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['4fafc201-1fb5-459e-8fcc-c5c9c331914b']
})
.then(device => {
Log("connecting to " + device.name);
// Attempts to connect to remote GATT Server.
return device.gatt.connect();
})
.then(server => {
// Getting Service…
return server.getPrimaryService("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
})
.then(service => {
// Getting Characteristic…
return service.getCharacteristic("beb5483e-36e1-4688-b7f5-ea07361b26a8");
})
.then(characteristic => {
// Reading value
return characteristic.readValue();
})
.then(value => {
Log(`Value is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });
});
</script>
As you can see the Chrome on Android was able to interact with physical Bluetooth interface. The code has successfully read the value from a characteristics and displayed it onto the screen.
Summary
There is great potential in this protocol, because there is no need to develop custom applications for different platforms. All it needs is a browser supporting this protocol and access to Bluetooth interface.
Similarly to Web Bluetooth
there is Web USB
protocol being developed. It works in the same manner – the browser requests access to physical USB interface. Stay tuned for a post about Web USB.
In LPN Plant we connect consulting, technical expertise and financial effectiveness to design and implement low power wireless solutions for industry. If you are looking for product developers or just need support in a piece of your system feel free to contact us. If you enjoying this type of content feel free to share content on social media.