Ramblings about IKEA VINDSTYRKA and its sensor implementation.
VINDSTYRKA contains a Sensirion SEN54 environmental sensor node, which can easily be interfaced with via I2C. The interesting bit is how IKEA implemented this sensor.
Implementations for ESPHome and Arduino are widely available, however these libraries yield different (and inaccurate) readings for temperature and humidity compared to what is displayed on VINDSTYRKA.
This discrepancy can be explained by the way IKEA processes the data, instead of using the preprocessed data from the sensor IKEA opted to use the raw sensor output (including undocumented mystery data!) to compute the values for temperature, humidity and tVOC trend.
The goal of this exercise is to come up with a correction curve that can be used to accurately post-process the SEN54 humidity and temperature data.
VINDSTYRKA (V
) sends the following commands to the SEN54 (S
):
Once at startup:
V -> S: 0x00 0x21 Start measurements
Repeated roughly every second:
Dir. Data Description Note
V -> S: 0x02 0x01 CMD: Read data ready flag
V <- S: 0x00 0x01 CRC Data ready flag response [0]
V -> S: 0x03 0xC4 CMD: Read measured values
V <- S: MSB LSB CRC PM1.0 reading * 10 (uint16)
MSB LSB CRC PM2.5 reading * 10 (uint16) [1]
MSB LSB CRC PM4.0 reading * 10 (uint16)
MSB LSB CRC PM10.0 reading * 10 (uint16)
MSB LSB CRC Processed humidity * 100 (uint16)
MSB LSB CRC Processed temperature * 200 (uint16)
MSB LSB CRC Processed VOC Index * 10 (uint16)
MSB LSB CRC Processed NOX Index * 10 (uint16)
V -> S: 0x03 0xD2 CMD: Read raw measurements
V <- S: MSB LSB CRC Raw humidity * 100 (uint16)
MSB LSB CRC Raw temperature * 200 (uint16)
MSB LSB CRC Raw VOC (uint16) [2]
MSB LSB CRC Raw NOX (uint16)
V -> S: 0x03 0xF5 CMD: Read raw mystery measurement
V <- S: MSB LSB CRC Raw humidity * 100 (uint16) [3]
MSB LSB CRC Raw temperature * 200 (uint16) [4]
??? ??? CRC Mystery word [5]
- VINDSTYRKA respects the data ready flag and validates the CRC's.
- VINDSTYRKA only appears to be using the PM2.5 reading from the processed values.
- Raw VOC influences the tVOC trend arrow and VOC Index value available through Zigbee cluster
0xFC7E
. - Value used to compute humidity.
- Value used to compute temperature.
- Undocumented bytes; influences humidity and temperature readings in a significant way.
To aide in this research I wrote an Arduino implementation of the SEN54 that allows VINDSTYRKA to be fed arbitrary values over I2C.
On startup VINDSTYRKA takes the humidity and temperature readings straight from the raw values returned by mystery command 0x03F5
, then a correction algorithm kicks in and the values start to drift away...
Continuously feeding 40 % humidity (40 * 100 = 4000), 25 degrees C (25 * 200 = 5000), and a mystery word of 0xFB0C
results in the device starting up with 40 % and 25 C on the display; then the values drift for ~15 minutes before finally settling on 68 % and 16 C 🤔.
Altering the mystery word slightly to 0xFB0B
results in the values going all over the place before slowly settling near the previous readouts.
The temperature value also influences the humidity readout (and vice versa).
This likely requires disassembly of the firmware to properly understand.
Simulating a raw VOC of 30000 starts the device off with the arrow pointing flat and a reported VOC Index of 100. Increasing the raw value to 31000 makes the arrow point DOWN and the reported VOC Index decrease.
After a while the VOC Index returns to 100 (the new baseline) and the arrow points flat again. Decreasing the value back to 30000 makes the arrow point UP and the VOC Index shoot up.
Within a few minutes VINDSTYRKA learns the new baseline and returns to reporting a VOC Index of 100.
To correlate the SEN54 output with the processed output of VINDSTYRKA I wrote an Arduino sketch that echos the SEN54 data over serial.
An anti-collision mechanism was implemented to allow both VINDSTYRKA and the logger to co-exist on the I2C bus without interfering with each other. The logger also ensures that it does not prematurely clear the "data ready" flag on the sensor, as that could influence the readings received by VINDSTYRKA.
Collision detection requires the SCL line to be duplicated to a second input pin on the Arduino, this allows the logger to time its transmissions within the idle time of VINDSTYRKA.
After each successful acquisition a CSV-formatted line containing the measured values, raw values, and mystery values is printed out to the serial port.
Processed readings from VINDSTYRKA can be acquired by interrogating the Zigbee clusters.
VINDSTYRKA exposes its values in the following Zigbee clusters:
0x0405 # Humidity
0x0402 # Temperature
0x042a # PM2.5
0xfc7e # VOC index
The values reported over Zigbee are identical to what is displayed on VINDSTYRKA (except for the VOC Index, which is displayed as a trend arrow). All values are reported in whole numbers with no decimal places.
To bring it all together I created a Python script that monitors the serial port for readings from the Arduino logger, after receiving a reading it then fetches the VINDSTYRKA values from Home Assistant through the ZHA Websocket API. Finally all these data points are written to a CSV file.
The recorded samples look like this:
2023-06-25T17:47:09.348163;9979784,9285;PRO,8,52,54,54,54,4362,5250,650,32767,RAW,4,3274,6303,31907,65535,MYS,3,3274,6303,64476,ZHA,4,6.0,67.0,5200,2400
2023-06-25T17:47:10.368067;9980802,9286;PRO,8,51,54,54,54,4362,5250,650,32767,RAW,4,3270,6304,31901,65535,MYS,3,3270,6304,64473,ZHA,4,5.0,67.0,5200,2400
2023-06-25T17:47:11.388047;9981818,9287;PRO,8,51,54,54,54,4362,5250,650,32767,RAW,4,3274,6304,31920,65535,MYS,3,3274,6304,64475,ZHA,4,5.0,67.0,5200,2400
2023-06-25T17:47:12.407920;9982836,9288;PRO,8,51,53,53,53,4362,5250,650,32767,RAW,4,3271,6301,31927,65535,MYS,3,3271,6301,64475,ZHA,4,5.0,67.0,5200,2400
2023-06-25T17:47:13.429023;9983854,9289;PRO,8,50,52,52,52,4362,5250,650,32767,RAW,4,3272,6302,31926,65535,MYS,3,3272,6302,64476,ZHA,4,5.0,67.0,5200,2400
Research continues once enough data points have been acquired.