Skip to content

Bug: Ruuvi Air history decoder reconstructs VOC/NOx 9-bit values incorrectly #295

Description

@TheSomeMan

Summary

AirHistoryDecoder currently reconstructs VOC and NOx history values by treating the corresponding Flags bit as the most significant bit of the 9-bit value:

value_bit9 = (flags >> flag_bit) & 0x01
value = value_byte | (value_bit9 << 8)

This is inconsistent with the Ruuvi data format encoding and with the existing DF6 / E1 advertisement decoders in this project.

For Ruuvi Air VOC and NOx values, the value byte contains bits [8:1], while the corresponding Flags bit contains value bit [0] (LSB).

The correct reconstruction is:

value_lsb = (flags >> flag_bit) & 0x01
value = (value_byte << 1) | value_lsb

Specifically:

voc = (voc_byte << 1) | ((flags >> 6) & 0x01)
nox = (nox_byte << 1) | ((flags >> 7) & 0x01)

Affected code

The issue is in:

ruuvitag_sensor/decoders/air_history_decoder.py

in:

AirHistoryDecoder._get_9bit_value()

Why this is incorrect

The Ruuvi advertisement decoders in this project already implement the encoding correctly.

For example, the E1 decoder reconstructs VOC as:

voc_high_bits = data[9]
voc_lsb = (data[14] >> 6) & 0x01
voc = (voc_high_bits << 1) | voc_lsb

and NOx similarly.

The Ruuvi reference history decoder also uses the LSB-in-Flags interpretation:

https://github.com/ruuvi/ruuvi.air.ble_nus/blob/master/scripts/ruuvi_ble_nus_read_hist.py

The generic Ruuvi endpoint decoder uses the same scheme:

https://github.com/ruuvi/ruuvi.endpoints.c/blob/master/src/ruuvi_endpoints_internal.h

Example

For:

VOC byte = 0x01
Flags bit 6 = 1

the correct result is:

(0x01 << 1) | 1 = 3

The current Air history decoder instead produces:

0x01 | (1 << 8) = 257

Likewise, for:

NOx byte = 0x02
Flags bit 7 = 1

the correct result is:

(0x02 << 1) | 1 = 5

while the current decoder returns 258.

Tests

The existing Air history tests currently encode the same incorrect interpretation, so they need to be updated together with the decoder.

In particular, test_decode_voc_nox_with_flags() currently treats the Flags bit as value bit [8].

The regression test should verify values where the flag bit is set and distinguish clearly between the two interpretations, for example:

VOC: byte=0x01, Flags b6=1 -> 3
NOx: byte=0x02, Flags b7=1 -> 5

The invalid value remains unchanged:

byte=0xFF, flag=1
=> (0xFF << 1) | 1
=> 0x1FF
=> invalid

Expected fix

Change:

value_bit9 = (flags >> flag_bit) & 0x01
value = value_byte | (value_bit9 << 8)

to:

value_lsb = (flags >> flag_bit) & 0x01
value = (value_byte << 1) | value_lsb

and update the affected Air history test vectors accordingly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions