Skip to content

Bugfix for DHT module to avoid timeouts on high system load #5848

Description

@seiuvwcdhol

I found an error in the DHT module, if the system load is high: I have many Python threads running and the DHT module constantly reports a timeout error. If system load is low, there is no error.

I have a fix for the problem: You should extend the atomic section to include the 18ms waiting. Simply move the mp_hal_quiet_timing_enter() before the mp_hal_delay_ms(18), see below. I see no problem to loose the 18ms because the DHT measurements should only be every 2-3 seconds.

The 18ms may be reduced for DHT22, because the data sheet needs at least 1ms delay. I use a 3ms delay with the DHT22 with no problems. But for DHT11 we need 18ms. So I would let the delay at 18ms.

The fix is easy, in the file:

micropython/drivers/dht/dht.c

ORIGINAL (line 51):

// issue start command
mp_hal_pin_od_high_dht(pin);
mp_hal_delay_ms(250);
mp_hal_pin_od_low(pin);
mp_hal_delay_ms(18);

mp_uint_t irq_state = mp_hal_quiet_timing_enter();

CHANGE TO:

// issue start command
mp_hal_pin_od_high_dht(pin);
mp_hal_delay_ms(250);
mp_hal_pin_od_low(pin);

// the atomic section should be before the delay of 18ms
// otherwise we have timeout errors on high system load
// Stefan Hammes, 2020-03-31
mp_uint_t irq_state = mp_hal_quiet_timing_enter();

mp_hal_delay_ms(18);

Or with diff:

$ diff dht.c-orig dht.c
55d54
<     mp_hal_delay_ms(18);
56a56,58
>     // the atomic section should be before the delay of 18ms
>     // otherwise we have timeout errors on high system load
>     // Stefan Hammes, 2020-03-31
57a60,61
>
>     mp_hal_delay_ms(18);

This gives correct DHT readings even with extremly high system load.

Please include this in the source code. Thanks.

Keep up the good work!

Stefan Hammes, Karlsruhe, Germany

Activity

  1. dpgeorge commented on Apr 5, 2020

    @dpgeorge
    Member

    Thanks for the detailed description and fix.

    What port/system are you using?

    It was my understanding that the 18ms delay was a minimum, so allowing it to possibly extend beyond 18ms was OK. But I guess that's not the case.

    If this fix is made then the mp_hal_delay_ms(18) would need to be changed to mp_hal_delay_us_fast(18000), because mp_hal_delay_ms may call scheduled Python code.

  2. seiuvwcdhol commented on Apr 5, 2020

    @seiuvwcdhol
    Author
  3. dpgeorge commented on Apr 6, 2020

    @dpgeorge
    Member

    Idea: More elaborate could be: We can use 18ms for the DHT11 and 3ms for the DHT22 (the information which sensor is used could be passed to the function).

    Yes that makes sense. 18ms is a long time (relatively) and reducing it to 3ms for DHT22 would be good.

  4. added a commit that references this issue on Jan 13, 2022
  5. Andrei-Pozolotin commented on Apr 2, 2022

    @Andrei-Pozolotin

    Another solution for a similar problem (random timeout errors in DHT):
    re-implement driver in viper for total control of DHT protocol parameters.

    #
    # origin:
    # https://github.com/micropython/micropython/tree/master/drivers/dht
    #
    
    import micropython  # @UnresolvedImport
    
    from time import sleep_ms  # @UnresolvedImport
    
    from machine import Pin  # @UnresolvedImport
    from machine import time_pulse_us  # @UnresolvedImport
    
    
    #
    # reimplement protocol in viper
    #
    @micropython.viper  # @UndefinedVariable
    def dht_read_viper(pin_obj:object, dht_buf:ptr8) -> int:  # @UndefinedVariable
    
            count = 0  # track bits
    
            # issue "0" start bit 10ms
            pin_obj.init(mode=Pin.OUT)
            pin_obj.value(1)
            sleep_ms(250)
            pin_obj.value(0)
            sleep_ms(10)
            pin_obj.value(1)
    
            # switch to line reading
            pin_obj.init(mode=Pin.IN)
    
            # accept start bit "1" of 80us
            ticks = int(time_pulse_us(pin_obj, 1, 250))
            if ticks < 0: return -1  # failure: missing start bit
    
            # accept data stream of 40+ bits
            while True:
                # bit duration: "0"=27us and "1"=70us, border is 48us
                ticks = int(time_pulse_us(pin_obj, 1, 250))
                if ticks < 0: break  # stop on first timeout
                if count < 40:  # collect first 5 bytes
                    index = count >> 3  # data byte index
                    entry = int(ticks > 48)  # "0" vs "1" bit entry
                    dht_buf[index] = (dht_buf[index] << 1) | (entry)
                count += 1
    
            # restore "1" line level after reading
            pin_obj.init(mode=Pin.OUT)
            pin_obj.value(1)
    
            if count < 40: return -2  # failure: data stream incomplete
    
            # verify received data integrity
            dht_total = 0
            for index in range(4):
                dht_total += dht_buf[index] & 0xFF
            dht_total = dht_total & 0xFF
            dht_check = dht_buf[4] & 0xFF
    
            if dht_total != dht_check: return -3  # failure: check sum mismatch
    
            return 0  # success: data received
    
    
    class DHTBase:
    
        def __init__(self, pin_obj:Pin):
            self.pin_obj = pin_obj
            self.dht_buf = bytearray(5)
    
        def measure(self) -> int:  # report error status
            return  dht_read_viper(self.pin_obj, self.dht_buf)
    
    
    class DHT22(DHTBase):
    
        def humidity(self) -> float:
            buf = self.dht_buf
            humi = buf[0] << 8 | buf[1]
            return humi * 0.1
    
        def temperature(self) -> float:
            buf = self.dht_buf
            temp = (buf[2] & 0x7F) << 8 | buf[3]
            if buf[2] & 0x80: temp = -temp
            return temp * 0.1
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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions