DEV Community

w4ilun
w4ilun

Posted on

1

Using Web Bluetooth in Chrome to visualize IMU data

(Note: these posts are migrated from my previous medium.com blog)

[https://github.com/w4ilun/web-bluetooth-curie-imu](https://github.com/w4ilun/web-bluetooth-curie-imu)

GitHub logo w4ilun / web-bluetooth-curie-imu

Arduino/Genuino 101 CurieIMU Web Bluetooth Orientation Visualiser

The Web Bluetooth API allows you to interact with Bluetooth LE peripherals from your browser using JavaScript. It’s been available since Chrome 45+, but you’ll have to enable it manually since it’s still an experimental feature. You can check out the API and examples here.

To start, I combined code examples for the Arduino 101 on using the built-in IMU and Bluetooth LE features; all of the Curie’s IMU readings are sent as notifications over Bluetooth LE by updating the characteristic’s value. The sketch below shows how to do so with the CurieBLE and CurieIMU libraries.

#include <CurieBLE.h>
#include <CurieIMU.h>
#include <MadgwickAHRS.h>
BLEPeripheral blePeripheral;
BLEService imuService("BEEF"); //give your custom service an ID
BLECharacteristic imuChar("FEED", BLERead | BLENotify, 20); //give your custom characteristic an ID, properties, and length
Madgwick filter;
unsigned long microsPerReading, microsPrevious;
float accelScale, gyroScale;
void setup(){
Serial.begin(9600);
// instantiate BLE peripheral
blePeripheral.setLocalName("CurieIMU"); //broadcast device name
blePeripheral.setAdvertisedServiceUuid(imuService.uuid()); // add the service UUID
blePeripheral.addAttribute(imuService); // add your custom service
blePeripheral.addAttribute(imuChar); // add your custom characteristic
blePeripheral.begin();
Serial.println("Bluetooth device active, waiting for connections...");
// start the IMU and filter
CurieIMU.begin();
CurieIMU.setGyroRate(25);
CurieIMU.setAccelerometerRate(25);
filter.begin(25);
// set the accelerometer range to 2G
CurieIMU.setAccelerometerRange(2);
// set the gyroscope range to 250 degrees/second
CurieIMU.setGyroRange(250);
// initialize variables to pace updates to correct rate
microsPerReading = 1000000 / 25;
microsPrevious = micros();
}
void loop() {
int aix, aiy, aiz;
int gix, giy, giz;
float ax, ay, az;
float gx, gy, gz;
float roll, pitch, heading;
unsigned long microsNow;
String str = "";
// check if it's time to read data and update the filter
microsNow = micros();
if(microsNow - microsPrevious >= microsPerReading){
// read raw data from CurieIMU
CurieIMU.readMotionSensor(aix, aiy, aiz, gix, giy, giz);
// convert from raw data to gravity and degrees/second units
ax = convertRawAcceleration(aix);
ay = convertRawAcceleration(aiy);
az = convertRawAcceleration(aiz);
gx = convertRawGyro(gix);
gy = convertRawGyro(giy);
gz = convertRawGyro(giz);
// update the filter, which computes orientation
filter.updateIMU(gx, gy, gz, ax, ay, az);
// print the heading, pitch and roll
roll = filter.getRoll();
pitch = filter.getPitch();
heading = filter.getYaw();
str = str + roll + "," + pitch + "," + heading;
Serial.println(str);
BLECentral central = blePeripheral.central();
if(central){ // if a central is connected to peripheral
const unsigned char imuCharArray[20] = {
str[0],str[1],str[2],str[3],str[4],
str[5],str[6],str[7],str[8],str[9],
str[10],str[11],str[12],str[13],str[14],
str[15],str[16],str[17],str[18],str[19]
};
imuChar.setValue(imuCharArray, 20); //notify central with new data
}
// increment previous time, so we keep proper pace
microsPrevious = microsPrevious + microsPerReading;
}
}
float convertRawAcceleration(int aRaw){
// since we are using 2G range
// -2g maps to a raw value of -32768
// +2g maps to a raw value of 32767
float a = (aRaw * 2.0) / 32768.0;
return a;
}
float convertRawGyro(int gRaw){
// since we are using 250 degrees/seconds range
// -250 maps to a raw value of -32768
// +250 maps to a raw value of 32767
float g = (gRaw * 250.0) / 32768.0;
return g;
}
view raw curieimuble.ino hosted with ❤ by GitHub

On the web side, Chrome’s Web Bluetooth APIs were used to detect, connect, and read notifications from a Bluetooth LE peripheral device (our Arduino 101). For security reasons, all of this is enabled only after the user interacts with the page, so the navigator.bluetooth method is called after you click connect. From then on, you can discover the peripherals services and characteristics; we subscribe to these characteristics and receive IMU data in the callback function:

//BLE
let service = 0xBEEF;
let characteristic = 0xFEED;
let byteLength = 20;
//IMU global vars for processing
let yaw = 0.0;
let pitch = 0.0;
let roll = 0.0;
window.onload = () => {
document.getElementById('connect').onclick = connectBLE;
}
function connectBLE(){
//BLE setup. Connect and get service/characteristic notifications
navigator.bluetooth.requestDevice({ filters: [{ services: [service] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService(service))
.then(service => service.getCharacteristic(characteristic))
.then(characteristic => {
return characteristic.startNotifications()
.then(_ => {
characteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
});
})
.then(_ => {
console.log('Notifications have been started.');
})
.catch(error => { console.log(error); });
function handleCharacteristicValueChanged(event){
var value = event.target.value;
var str = "";
//concat and split string for roll, pitch, yaw (e.g. "-0.58,2.20,328.76")
for(var i=0; i<byteLength; i++){
str = str + String.fromCharCode(value.getUint8(i));
}
var imu = str.split(',');
//update globals
roll = parseFloat(imu[0]);
pitch = parseFloat(imu[1]);
yaw = parseFloat(imu[2]);
}
}
view raw curieimublue.js hosted with ❤ by GitHub

The IMU data (roll, pitch, & yaw) gets updated, and we update them as globals as well in our script; this allows our Processing.js sketch to read from the global scope the IMU data, and update the canvas WebGL rendering.

Since everything is done in Chrome and no additional server side processing is required, I hosted everything on GitHub pages, so everyone can try it out for themselves https://w4ilun.github.io/web-bluetooth-curie-imu/

You can also find all of the code above on GitHub: https://github.com/w4ilun/web-bluetooth-curie-imu

Enjoy!

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more