Airthings Wave Plus
Airthings Wave Plus is a air quality monitor with Bluetooth Low Energy capability. It's capable of measuring temperature, humidity and also CO2, Radon, and VOC. The unit runs on 2 AA batteries. You can poll its status via Bluetooth LE periodically using any device that supports Bluetooth including a Raspberry Pi or an ESP32.
Bluetooth LE
The Airthings Wave Plus uses the service UUID b42e1c08-ade7-11e4-89d3-123b93f75cba
. The measurements can be read through the characteristic UUID b42e2a68-ade7-11e4-89d3-123b93f75cba
.
You will have to look at the Bluetooth LE advertisements to see which MAC address your unit has. The serial number on the device doesn't seem to correspond to the MAC address it has.
Push readings via MQTT
Using OpenMQTTGateway on a ESP32, we can read the unit's readings via Bluetooth LE and push it to a MQTT server.
Create a MQTTtoBT command. You should publish this message to something like: openmqtt/ESP32/commands/MQTTtoBT/config
.
{
"ble_read_address":"D8:71:4D:A9:CB:C3",
"ble_read_service":"b42e1c08-ade7-11e4-89d3-123b93f75cba",
"ble_read_char":"b42e2a68-ade7-11e4-89d3-123b93f75cba",
"value_type":"HEX",
"ttl":10,
"immediate":true
}
You should then hopefully be able to hear back once a connection gets established.
{
"id":"D8:71:4D:A9:CB:C3",
"service":"b42e1c08-ade7-11e4-89d3-123b93f75cba",
"characteristic":"b42e2a68-ade7-11e4-89d3-123b93f75cba",
"read":"015f220073008400ba07ddab5a0247000000ac05",
"success":true
}
To get periodic measurements, I've set up a recurring node in NodeRED that polls the device every 5 minutes and dumps the data into InfluxDB.
Decoding the raw Bluetooth LE data
The data returned back is 40 bytes long. We need to decode this using the following data structure:
typedef struct
{
uint8_t version = 0;
uint8_t humidity = 0;
uint8_t ambientLight = 0;
uint8_t unused01 = 0;
uint16_t radon = 0;
uint16_t radonLongterm = 0;
uint16_t temperature = 0;
uint16_t pressure = 0;
uint16_t co2 = 0;
uint16_t voc = 0;
} WavePlusRawReadings;
In the example above, we'll break the incoming data 015f220073008400ba07ddab5a0247000000ac05
into their individual fields and decode it into useful measurements.
Hex | Name | Final reading |
---|---|---|
01
|
Version | Version 1 |
5f
|
Humidity | 0x5f = 95 / 2 = 47.5% |
22
|
Ambient Light | 0x22 = 34 |
00
|
Unused | |
7300
|
Radon, 24 hours | 0x0073 = 115 Bq |
8400
|
Radon, long term | 0x0084 = 132 Bq |
ba07
|
Temperature | 0x07ba = 1978 / 100 = 19.78 C |
ddab
|
Air Pressure | 0xabdd = 43997 / 50 = 879.94 Pa |
5a02
|
CO2 | 0x025a = 602 ppm |
4700
|
VOC | 0x0047 = 71 ppb |
0000
|
?? | |
ac05
|
?? | 0x05ac = 1452 |
NodeRED function
Here's the decode function I used. I'm sure there's a better way of doing it, but this should work.
var version = parseInt(msg.payload.read.substring(0, 2), 16);
var humidity = parseInt(msg.payload.read.substring(2, 4), 16) / 2.0;
var light = parseInt(msg.payload.read.substring(4, 6), 16);
var radon24 = parseInt(msg.payload.read.substring(10, 12) + msg.payload.read.substring(8, 10), 16);
var radonlt = parseInt(msg.payload.read.substring(14, 16) + msg.payload.read.substring(12, 14), 16);
var temp = parseInt(msg.payload.read.substring(18, 20) + msg.payload.read.substring(16, 18), 16) / 100.0;
var pressure = parseInt(msg.payload.read.substring(22, 24) + msg.payload.read.substring(20, 22), 16) / 50.0;
var co2 = parseInt(msg.payload.read.substring(26, 28) + msg.payload.read.substring(24, 26), 16);
var voc = parseInt(msg.payload.read.substring(30, 32) + msg.payload.read.substring(28, 30), 16);
var a = parseInt(msg.payload.read.substring(34, 36) + msg.payload.read.substring(30, 32), 16);
var b = parseInt(msg.payload.read.substring(38, 40) + msg.payload.read.substring(36, 38), 16);
ESPHome integration
I tried using ESPHome using the following configs, but had issues getting it to read the Bluetooth data properly. For some reason, my ESP32 kept on getting a lld_pdu_get_tx_flush_nb HCI packet count mismatch (0, 1)
error. The same device running the AirthingsMQTT Bridge sketch or the OpenMQTTGateway firmware had no such issues.
sensor:
- platform: airthings_wave_plus
ble_client_id: airthings
update_interval: 1min # default
temperature:
name: "Airthings Wave Plus Temperature"
radon:
name: "Airthings Wave Plus Radon"
radon_long_term:
name: "Airthings Wave Plus Radon Long Term"
pressure:
name: "Airthings Wave Plus Pressure"
humidity:
name: "Airthings Wave Plus Humidity"
co2:
name: "Airthings Wave Plus CO2"
tvoc:
name: "Airthings Wave Plus VOC"
ble_client:
- mac_address: d8:71:4d:a9:cb:c3
id: airthings
esp32_ble_tracker:
Regardless, I like the more intuitive way of dealing with just MQTT messages using OpenMQTTGateway rather than ESPHome. The whole Home Assistant integration thing with ESPHome just didn't work out properly for me (for one thing, the ESP device kept appearing offline periodically? Adding additional BLE sensors requires rebuilding the sketch/reuploading? OMG just reports back everything it can see without needing to rewrite/reflash anything and is superior in that regard). Gah!
See also
- ESPHome's driver - https://github.com/esphome/esphome/blob/dev/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp
- AirthingsMQTT with Wave Plus support - https://github.com/molepage/AirthingsMQTT/blob/master/AirthingsMQTT.ino