This tutorial walks you through a tiny, privacy-first web app that reads only the advertised CO2 level from a nearby HibouAir sensor using a BleuIO USB BLE dongle. There’s no pairing, cloud, or backend—just your browser, the dongle, and a page that decodes a numeric CO2 value broadcast in BLE advertisements and renders it as a color bar (default window 400–2000 ppm) with a simple “High CO2” warning when your threshold is crossed.
This project is a follow-up to the Ambient-Adaptive Noise Bar with BleuIO & HibouAir. We reused the same structure and UI, but swap the decoder to read CO2 instead of noise.
This project is a follow-up to the Ambient-Adaptive Noise Bar with BleuIO & HibouAir. We reused the same structure and UI, but swap the decoder to read CO2 instead of noise.What you’ll build
A single HTML file that talks to BleuIO over the Web Serial API. The page puts BleuIO in the central role, periodically runs a targeted scan for your HibouAir Board ID, and parses the Manufacturer Specific Data (MSD) bytes in each advertisement to extract CO2 (ppm). The value drives a horizontal gradient bar; cross the threshold and a warning banner appears. Everything runs locally in the browser.
- Code:GitHub repository
- Try it now:Live demo on GitHub Pages — plug in BleuIO, and press Connect. Make sure to pass correct HibouAir Board ID
CO2 is a practical proxy for ventilation. Elevated levels are associated with stale air, drowsiness, and reduced productivity. Many spaces—meeting rooms, classrooms, offices, homes—benefit from quick visual feedback so people know when to air out the room. Reading only a single, device-computed number from BLE advertisements keeps the design simple, fast, and privacy-preserving.
Hardware & softwareHow it works (at a glance)BLE devices periodically broadcast short advertisement packets with real-time CO2 values. We can read them without pairing.
This page filters to a specific Board ID, captures the advertisement line, extracts the longest hex payload, and then decodes CO2 from a fixed position inside the MSD. The result is mapped to a 0–100% fill of the bar (for a display window of 400–2000 ppm), and we show a banner when CO2 ≥ threshold (default 1000 ppm).
Below is the exact function used in this project:
function decodeCo2FromAdv(hex) {
// sanitize → bytes
hex = (hex || '').replace(/[^0-9A-F]/gi, '');
if (hex.length % 2) hex = hex.slice(0, -1);
const b = new Uint8Array(hex.length / 2);
for (let i = 0; i < b.length; i++) b[i] = parseInt(hex.substr(i*2,2), 16);
// locate MSD anchor and read CO2 at fixed offset (big-endian)
for (let i = 0; i <= b.length - 5; i++) {
if (b[i] === 0x5B && b[i+1] === 0x07 && b[i+2] === 0x05) {
const idx = i + 23; // CO2 MSB position in this layout
if (idx + 1 < b.length) {
return (b[idx] << 8) | b[idx+1]; // ppm
}
}
}
return null;
}
The BLE flowWhen you click Connect, the page opens a serial session to BleuIO and sends:
AT+CENTRAL
once, to enter scanning modeAT+FINDSCANDATA=<BOARD_ID>=3
every cycle to run a 3-second targeted scan- The reader consumes lines until BleuIO prints
SCAN COMPLETE
, then waits and repeats
Each time an advertisement arrives, the page extracts the hex payload, decodes CO2, updates the bar, and toggles the High CO2 banner if the threshold is exceeded.
OutputYou’ll see a horizontal color bar labeled with the current CO2 ppm. The bar fills from left to right as values rise within the 400–2000 ppm window. A bold High CO2 banner appears when the reading crosses your threshold (default 1000 ppm), serving as a polite nudge to improve ventilation.
Use casesThis simple CO2 bar works well anywhere people gather and air can get stale. In meeting rooms and classrooms it provides a live cue to crack a window or switch on ventilation as occupancy rises. In open offices it nudges teams toward timely air exchanges, helping reduce stuffiness and afternoon dips in alertness. At home it’s a lightweight way to keep bedrooms and living spaces fresh during gatherings or winter months with closed windows. Shared studios and makerspaces also benefit from quick, ambient feedback without the overhead of dashboards or wall displays.
Because the app reads only a single numeric value that HibouAir already broadcasts, it avoids handling personal data and is easy to deploy in privacy-sensitive environments.
Accuracy & practical notesThis is a lightweight indicator, not a calibration tool. CO2 readings in advertisements update periodically and represent the sensor’s current value. Placement matters: keep your HibouAir within a reasonable range of BleuIO to reduce missed packets. If your environment regularly exceeds the default window, you can adjust the display range and threshold in the code.
Extend the projectYou can grow this prototype in several practical directions. Start by logging readings to CSV or IndexedDB for simple trend analysis over days or weeks. If you have multiple sensors, add a multi-device view that scans several Board IDs and presents compact tiles in one page. For automation, trigger a webhook or send a serial command to control a fan or relay whenever CO2 exceeds your threshold. You can also pair it with the earlier Noise Bar and show Noise + CO2 side-by-side for a fuller picture of comfort and productivity.
Comments