Spiderman has joined some LEGO spacemen in a quest to web enable a BLE GATT temperature sensor and display the readings on their 1x1cm brick display...
While they're working on that, I'll share what's under the hood :)
Temperature Sensor in Thingy:52
The Thingy:52 by Nordic Semiconductor has a lot of built-in sensors (temperature, humidity, pressure, air quality, color, accelerometer, gyroscope, and more).
First, switch it on:
In our case, we are interested in reading values from the temperature sensor, so let's take a look in the documentation for the device:
Note: The xxxx
in the base UUID should be replaced with numbers from the same column for service and characteristic(s).
From this, we can see two 128bit UUIDs we need:
-
ef680200-9b35-4933-9b10-52ffa9740042
- the "Weather station service" -
ef680201-9b35-4933-9b10-52ffa9740042
- the "Temperature characteristic"
It's also a good idea to fetch the battery level, which follows the officially assigned 16bit UUID for battery service:
Connecting to the Thingy:52 from a web application is quite easy, just remember to list the services that might be required for your app in the list of optionalServices
:
const THINGY_CONFIGURATION_SERVICE_UUID = 'ef680100-9b35-4933-9b10-52ffa9740042';
const WEATHER_STATION_SERVICE_UUID = 'ef680200-9b35-4933-9b10-52ffa9740042';
...
const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [THINGY_CONFIGURATION_SERVICE_UUID] }],
optionalServices: [
'battery_service',
WEATHER_STATION_SERVICE_UUID
]
});
Temperature data
The temperature data is made available under the Weather Station Service
and in order to read the data, you'll need to subscribe to the temperature updates on the Temperature Characteristic
:
const TEMPERATURE_CHARACTERISTIC_UUID = 'ef680201-9b35-4933-9b10-52ffa9740042';
...
async #startThermometerNotifications(server) {
const service = await server.getPrimaryService(WEATHER_STATION_SERVICE_UUID);
const characteristic = await service.getCharacteristic(TEMPERATURE_CHARACTERISTIC_UUID);
characteristic.addEventListener('characteristicvaluechanged', this.#onThermometerChange.bind(this));
return characteristic.startNotifications();
}
Whenever there is an update, read out the values:
_onThermometerChange(event) {
const target = event.target;
const integer = target.value.getInt8(0);
const decimal = target.value.getUint8(1);
const celsius = Number.parseFloat(`${integer}.${decimal}`);
const temperature = {
celsius,
fahrenheit: celsius * 9 / 5 + 32,
kelvin: celsius + 273.15
}
this.dispatchEvent(new CustomEvent('thermometer', {
detail: temperature
}));
}
Thermometer Web Component
We'll need some visualization for the thermometer, and I found a very nice thermometer made in pure css by Mircea Georgescu, which will be a good base for a simple thermometer web component.
Disclaimer: I am definitely not a CSS expert, so sorry Mircea for the hacks I made :)
For the purpose of this demo, I have hacked the linear-gradient used for the positioning of the level and the position of the temperature reading:
_handleTemperature({detail}) {
this.#celsius.innerHTML = `${detail.celsius.toFixed(1)}°C`;
this.#celsius.style.transform = `translateY(${-detail.celsius/2}px)`;
const perc = 50 - (detail.celsius * 0.32);
this.#thermometer.style.background = `linear-gradient(#fff 0%, #fff ${perc}%, #d00 ${perc}%, #d00 100%)`
}
The full source of the component is here.
Building the application
Handling the connection to the Thingy:52 will be done with a scaled down variant what I did in another post: Generic Sensors and Thingy:52. It injects a small widget in the page that handles connectivity and shows battery levels.
Besides that (the thingy52-widget
), the application only consists of the new thermometer UI component:
<body>
<thingy52-widget></thingy52-widget>
<div class="flex-container">
<div class="content">
<div class="col">
<h2>Web Thermometer</h2>
<thermometer-ui></thermometer-ui>
</div>
</div>
</div>
</body>
All working together
Repo: https://github.com/larsgk/web-thermometer
Demo: https://larsgk.github.io/web-thermometer
Feedback, requests, issue reports and PRs are very welcome!
Enjoy ;)