jnthbdn / rs-dht-pio

A PIO driver for DHT sensor
MIT License
3 stars 1 forks source link

Incorrect reported temperature when < 0C #2

Closed grukx closed 1 year ago

grukx commented 1 year ago

My DHT reported -3275.1C when the temp outside was a bit below 0C

DHT22: -3275.1C, 76.5%

Pretty sure it was not that cold outside 🤒

Regards, Geir

grukx commented 1 year ago

I can't see anything obvious

--deleted incorrect suggestion here---

Geir

jnthbdn commented 1 year ago

Hi, Thank for this new issue !

I think you're right there's probably an bug ^^ I will investigate this. When you got the value of -3275.1C, do you know the real temperature ? (even an approximation ?)

Thanks, Jonathan

jnthbdn commented 1 year ago

I try to refactoring the casts in the read function. Can you try the 0.5.2 ?

grukx commented 1 year ago

I see the problem the moment it dips below 0C. My guess is that there is an incorrect sign extension happening somewhere, however I have not found it yet. As you can see the number is very close to 32767. I guess it must happen before or possibly during the 0x7FFF mask. I have tried a few different casts, but with little luck so far. I will try 0.5.2 a little later today, but have to prioritize work first :D

grukx commented 1 year ago

I get it with this code

fn main() {
        let t:u32 = 0x0000FFEF;
        let mut final_t: i32 = (t & 0x7FFF) as i32;

        if (t & 0x8000) > 0 {
            final_t *= -1;
        }

        let temperature = final_t as f32 / 10.0;

    println!("Converted temp: {} ", temperature);
}
jnthbdn commented 1 year ago

Yes I have the same idea, but I don't understand what happen. I remove the sign bit, before convert the unsigned into a signed value. My guess for the 0.5.2, ts the casts to f32 from i32, when the value is negative, could probably gone wrong (the value bigger than the mantis, maybe ?).

No problem :D Same for me ! I'll try to put a DHT in my freezer during my lunch break ^^

jnthbdn commented 1 year ago

(Our messages have crossed ^^ [Is this only a french expression ? ^^])

From the DataSheet of the DHT22, when the temperature is below zero, the 16th bit is set to one, but the value is not "two's complement".

For example :

grukx commented 1 year ago

yes - and code is fine when tested with t=0x00008065, but that is seemingly not what gets passed in from read_data

grukx commented 1 year ago
fn main() {

        let chip_read:u32 = 0x80650000;
        let t: u32 = (chip_read & 0xFFFF0000) >> 16;

        let mut final_t: i32 = (t & 0x7FFF) as i32;

        if (t & 0x8000) > 0 {
            final_t *= -1;
        }

        let temperature = final_t as f32 / 10.0;

    println!("Converted temp: {} ", temperature);

}

Is fine - so if chip was returning 0x8065 (-10.1) it should work

jnthbdn commented 1 year ago

Why are you left align the value ?

        let chip_read:u32 = 0x80650000;
        let t: u32 = (chip_read & 0xFFFF0000) >> 16;

But your code, show something stupid I did. I return u32 but the value is on 16 bits.... It's probable better to return u16 instead....

grukx commented 1 year ago

That was me mixing up hum and temp returned from the sensor - so ignore my last code - the left align is nonsense. I was trying to include some of what happens in read_data - but this was wrong :D

grukx commented 1 year ago

As for returning a u16, I agree - but it is a bit weird that this happens as the conversions seem fine. I suspect that it happens closer to the sensor readout.

grukx commented 1 year ago

While on the topic of optimization - from compiler explorer it looks like its better to write the code like this (https://godbolt.org/z/rK1G1GE7G). It produces way less assembly.

pub fn test() -> f32 {
        let t:u32 = 0x80c1;
        let final_t: i16 = (t & 0x7FFF) as i16;

        let temperature:f32;
        if (t & 0x8000) > 0 {
            temperature= final_t as f32 / -10.0; 
        } else
        {
            temperature= final_t as f32 / 10.0;
        }

        temperature
    }

(sorry about the side-track)

jnthbdn commented 1 year ago

Ok I made some measures, and I get something strange... As a raw value I got: 65320 in binary 1111 1111 0010 0011, but I don't understand why, I have so many 1 for MSBs. If I remove all the 1s, the right value appears 0010 0011 = 23 => -2.3 °C.

But the datasheet said, only the MSB bit, should set to 1 on negative value... And it's not a problème with my algorithm because de CRC pass.

image

grukx commented 1 year ago

Very strange - I can do a test in my freezer - the outside temp has risen a bit so its no longer below 0 at night.

jnthbdn commented 1 year ago

I wonder if our sensors aren't actually DHT12? Because it seems that for this component we only need the first byte for the value... https://github.com/adafruit/DHT-sensor-library/blob/be6915c60d7ca6922a99b9c089d4e82c0ef12829/DHT.cpp#L100

jnthbdn commented 1 year ago

I've pushed version 0.6 with DHT12 support, can you try? It works for me. Where did you buy the DHT? (Me on aliexpress, maybe not the most reliable ^^)

jnthbdn commented 1 year ago

(I'm not sure if it works... I can't go below -25.5°C... I think I made a mistake...) I confirm I'm stupid....

grukx commented 1 year ago

dht12 is only down to -20

grukx commented 1 year ago

I bought mine locally as I was eager to get started - but I have two - maybe I should just open up one and check

grukx commented 1 year ago

no markings, the strange thing is that dht12 is supposed to also be i2c

grukx commented 1 year ago

tested in freezer with dh12, but once it got down to 0C it started reporting -25.5 - and decreased as it got colder. So that is not correct either.

jnthbdn commented 1 year ago

no markings, the strange thing is that dht12 is supposed to also be i2c Yes... That why I said I'm stupid.... (I just 'yanked' the 6.0.0)

It seems there different "type" of DHT22 some library call it TYPE2. But there is no real consensus around this. Somme libs simply ignore the first byte when the temperature is under 0°C, and some make something more clever: they invert (logic "not") the first byte when the temperature is under 0°C. And I think I'll make this, because during my test I observe the first byte with the value 1111 1110 when temperature is below -25°C.

grukx commented 1 year ago

When in DHT22 mode it got down to 0b1111_1110_1011_1011 -> which if I mask out the top bits until the first 0 gives 187, or -18.7 - which seems reasonable. Except its not how its supposed to be.

grukx commented 1 year ago

This ends up being just guesswork - but removing all leading ones from the MSB and converting at least gives some kind of sensible result. But it must be possible to dig up some more info on this Type2.

jnthbdn commented 1 year ago

Yes, I agree. I can't find any datasheet with this behaviour... I will add a dht22Type2 struct to keep both data structure.

grukx commented 1 year ago

There is something in here about DHT22's that require a different length reset pulse https://github.com/esphome/esphome/pull/926

Could it be that we are reading back invalid registers due to a bad reset?

I think that 18ms in the document is a typo. Everywhere else in there says minimum 1ms. There was someone in the Discord channel today that couldn't get his DTH22 working until he changed the 800us to 2ms. Did you actually find a DHT22 that would fail if the pulse was longer than 800us? When he tried 18ms, it still worked like I expected it to. But it did have to be more than 1ms because that wasn't enough. 1.5ms would probably be good.
grukx commented 1 year ago

Try a longer reset pulser maybe? 2ms?

jnthbdn commented 1 year ago

Well... inverting the first byte doesn't work... But by doing a "not" (or a two's complement), it seems to work....

Your link is interesting, but why it work on positive value ? I can try to add a longer pulse (but after the dinner 😉).

jnthbdn commented 1 year ago

Just try a 2ms reset, it don't change anything... But I think I'll keep it to 2ms.

grukx commented 1 year ago

Well, I tested a bit as well and my DHT is not happy with any longer reset than 1ms, in fact 800ms is in the middle of the pulse it tolerates as reset. setting x to 32 causes dht to fail to start at -20C (tested in freezer)

grukx commented 1 year ago

I will probably be investigating other sensors, this is too magical and undocumented for me 😞

grukx commented 1 year ago

Is there anything in the pio code that could do sign extension on the top byte for some reason? would be super strange, but I know no PIO code - yet.

jnthbdn commented 1 year ago

Hm... So maybe I will set X to 25, this should generate a pulse of 800µS... From my test it seems the result is two's complements...

I will probably be investigating other sensors, this is too magical and undocumented for me 😞

Yes, you are probably right ^^

Is there anything in the pio code that could do sign extension on the top byte for some reason?

In theory no, the PIO script read the pin state and shift the result into a 32 bits buffer. This buffer is sent once is full or when I forced it (for the CRC for example)

jnthbdn commented 1 year ago

but I know no PIO code - yet.

You can try it's not that hard ^^

jnthbdn commented 1 year ago

Ok ! It make me angry because it's not documented, but the data from the DHT are two's complemented... So I simply cast the result to i16 then into f32 (then divide by 10) and it works like a charm...

jnthbdn commented 1 year ago

If you want to try I will push the 0.5.3

grukx commented 1 year ago

Go for it

Its only 9 instructions! (just read some doc 🤣) Looks like they got their inspiration from the TIS-100 game 💯 (probably one of histories nerdiest games)

jnthbdn commented 1 year ago

Yeah ! It's very simple. I LOVE this game !!! But there are some limitations, your "PIO" program can only have 32 instructions.

jnthbdn commented 1 year ago

image I think the hygrometer doesn't like the freezer 😆

grukx commented 1 year ago

Ooops

jnthbdn commented 1 year ago

The 0.5.3 is available. You have to change Dht22 to Dht22Type2

jnthbdn commented 1 year ago

image Well... I need to dry my sensor but, the values are correct

Oh ! And I kept the old value for the reset pulse...

jnthbdn commented 1 year ago

Can I close this problem? Have you tested the new version, or have you changed sensors?

grukx commented 1 year ago

You can close this ticket - the reading worked down to -22, however I have some questions about the sensor accuracy at this temperature as it deviated by about 4 degrees from the thermal camera I have on the same pico. This could be something else though and I will re-check accuracy at some other time.

You can close this for now.

Thanks for the help.