Sensirion / embedded-sps

Embedded i2c Driver for Sensirion Particulate Matter Sensors - Download the Zip Package from the Release Page
https://github.com/Sensirion/embedded-sps/releases
BSD 3-Clause "New" or "Revised" License
45 stars 15 forks source link

Driver returning very strange readings. #55

Closed monkeytronics closed 3 years ago

monkeytronics commented 3 years ago

Hi, I'm using the provided driver with pretty much the example usage. It works. I can read the firmware version (2.2), the device serial number. And I can apparantly read back the values. The problem is the values it reads are impossible (over 3000 um/m3 in a home). As others have said, the PM2.5 and PM10 values are identical.

I (3302767) SPS30 Particulate Matter Sensor Task: measured values:
        3032.95 pm1.0
        3207.23 pm2.5
        3207.23 pm4.0
        3207.23 pm10.0
        20713.03 nc0.5
        24136.81 nc1.0
        24205.48 nc2.5
        24213.31 nc4.5
        24217.41 nc10.0
        typical particle size = 0.28

This in fact leads me to believe it is working. Or at least, sort of. I'm also using the SCD30, SHT30, SGP40 devices. And they all seem to be fine. They are using the same common libraries to manipulate bytes to floats. I'm working on an ESP32, which is little endian, but the conversion seems fine for the other sensors.

I don't know what a 'sensible' result should look like. Anyone care to post one and offer an opinion as to what might be causing this? Thanks.

I did a back of the envelope, based on the assumption that particles under 0.5um are all 0.28um diameter. 20,000 particles, based on the density of human skin dust (1500kg/m3), it gives 194 ug/m3. Which is still horrifically high, so I don't think I can trust that either.

additional info I had a look at the read mesurement command with a scope. The command is W69, 03, 00. All good! The Read back is as follows: R69, 45, 8B, EF, 0E, 23, 39, 45, 93, 15, 0B, a8, etc... To be extra careful, I checked the integrity of the bytes by checking the CRC.

The payload for the first chunk is 0x45, 0x8B, 0x0E, 0x23. I knocked up a little snippet to see what it should be:

#include <stdio.h>
#include <stdint.h>

float bytesToFloat(uint8_t *bytes, int big_endian);

int main()
{
    #define b3 0x45
    #define b2 0x8B
    #define b1 0x0E
    #define b0 0x23

    unsigned char byte_array[] = { b3, b2, b1, b0 };
    float output = bytesToFloat(byte_array, 1);
    printf("Your float is %f.", output);

    return 0;
}

float bytesToFloat(uint8_t *bytes, int big_endian) {
    float f;
    uint8_t *f_ptr = (uint8_t *) &f;
    if (big_endian) {
        f_ptr[3] = bytes[0];
        f_ptr[2] = bytes[1];
        f_ptr[1] = bytes[2];
        f_ptr[0] = bytes[3];
    } else {
        f_ptr[3] = bytes[3];
        f_ptr[2] = bytes[2];
        f_ptr[1] = bytes[1];
        f_ptr[0] = bytes[0];
    }
    return f;
}

The output is

Your float is 4449.767090.

I've had a look at the following resource: single precision float and I'm confident that the interpretation of the bytes is correct. And 4449 is what I actually measured with this library. So, it looks like either I'm sitting inside a volcano, or there is something else wrong that's causing the device to read back crazy values... Sensirion, help me out. This is very odd indeed.

Very last edit I thought I should completely rule out any misinterpretation of the float format since there was a handy means available in the integer16 data format. I set that with :

#define SPS_CMD_START_MEASUREMENT_ARG 0x0500

and got the exact same results.

winkj commented 3 years ago

What is your supply voltage? I've seen similar high values when using the SPS30 with at 3.3V instead of 5V, e.g. here: https://github.com/Sensirion/arduino-sps/issues/20

monkeytronics commented 3 years ago

@winkj Yes! You are right! It turns out that the power supply (from PC USB) wasn't holding the 5V. It was fine until I added the extra load, and I didn't think to recheck it. Duh! Occam's razor.

Also, would be useful to add the following few lines of code to the code example. Since we aren't really giving it a long run while testing, this doesn't get done. Might help to see nice accurate results while we are in early dev stage.

// Start Fan Clean : spins it up for about 10s at full speed to blow all the crap out.
    ESP_LOGI(TAG, "SPS30 Fan Manual Cleaning Started");
    ret = sps30_start_manual_fan_cleaning();
    if (ret < 0) {
        ESP_LOGE(TAG, "error in cleaning cycle!\n");
    }  

    vTaskDelay(10 * 1000 / portTICK_RATE_MS);    /* wait 10s */