Sergio
July 25, 2026, 10:39am
1
Hello.
I have 4 RuuviTags + 1 RuuviTagPro working flowlessly with the longlife firmware broadcasting data every 53sec. to my PC. I can see them on NodeRed through “Theengs BLE MQTT gateway”.
My recent Ruuvi Air is broadcasting every second its “Manufaturer data” and other non-esential data.
¿Would it be possible to send as payload the read values: CO2, VOC, Temp, Press, PPM… at least every 30 sec or so?
otso
July 28, 2026, 6:36am
2
Ruuvi Air sends the data both in Bluetooth 4 and Bluetooth 5 advertisements, details of the formats are at Data format 6 | docs and Data format E1 (Extended v1) | docs .
Both include the sensor data inside the Manufacturer data field, maybe your library does not include a parser for the data yet?
Sergio
July 28, 2026, 4:09pm
3
Ops. I didn’t know it was embedded inside the Manufacturer data. On RuuviTags it’s just straightforward.
Thank you for the guides.
May be helpful for someone with the same scenario. Google Gemini made this code for me:
Use with “function” node. Add a “throttle” node before if less messages are wanted.
let raw = msg.payload.manufacturerdata || msg.payload;
if (raw.startsWith("9904")) {
raw = raw.substring(4);
}
const buf = Buffer.from(raw, 'hex');
if (buf[0] === 0x06) {
return null;
}
if (buf[0] !== 0xE1) {
node.error("Format not supported: 0x" + buf[0].toString(16));
return null;
}
const flags = buf[28];
const vocLsb = (flags >> 6) & 1;
const noxLsb = (flags >> 7) & 1;
msg.payload = {
temperature_C: Math.round(buf.readInt16BE(1) * 0.005 * 100) / 100,
humidity_RH: Math.round(buf.readUInt16BE(3) * 0.0025 * 100) / 100,
pressure_hPa: Math.round((buf.readUInt16BE(5) + 50000) / 100 * 100) / 100,
pm1_0: buf.readUInt16BE(7) * 0.1,
pm2_5: buf.readUInt16BE(9) * 0.1,
pm4_0: buf.readUInt16BE(11) * 0.1,
pm10_0: buf.readUInt16BE(13) * 0.1,
co2_ppm: buf.readUInt16BE(15),
voc_index: (buf[17] << 1) | vocLsb,
nox_index: (buf[18] << 1) | noxLsb,
mac: buf.subarray(34, 40).toString('hex').match(/.{1,2}/g).join(':').toUpperCase()
};
return msg;
1 Like