arendst / Tasmota

Alternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at
https://tasmota.github.io/docs
GNU General Public License v3.0
21.97k stars 4.77k forks source link

Tasmota 8.1.0.7: DHT11 Temp =NULL & Humid = NULL #7717

Closed adharyanto closed 4 years ago

adharyanto commented 4 years ago

PROBLEM DESCRIPTION

A clear and concise description of what the problem is.

After upgrade OTA from 8.1.0.6 to 8.1.0.7, the DHT11 sensor gives Temp = NULL deg C, and Humidity = NULL %. I know that to use the old sensor must insert USE_DHT_OLD, but the problem; this insertion can not be done remotely (OTA).... need to go to the site, open the esp8266 compartment and flash manually... I am thinking that this additional (USE_DHT_OLD) should be done remotely (OTA) as well, no need to flash manually...

REQUESTED INFORMATION

Make sure your have performed every step and checked the applicable boxes before submitting your issue. Thank you!

- [ ] If using rules, provide the output of this command: `Backlog Rule1; Rule2; Rule3`:

Rules output here:

- [ ] Provide the output of this command: `Status 0`:

STATUS 0 output here: 13:12:16 CMD: status 0 13:12:16 MQT: PintuGerbang/stat/STATUS = {"Status":{"Module":18,"FriendlyName":["Pintu Gerbang","Sonoff2","Sonoff3","Sonoff4"],"Topic":"PintuGerbang","ButtonTopic":"0","Power":0,"PowerOnState":3,"LedState":1,"LedMask":"FFFF","SaveData":1,"SaveState":1,"SwitchTopic":"0","SwitchMode":[0,0,0,0,0,0,0,0],"ButtonRetain":0,"SwitchRetain":0,"SensorRetain":0,"PowerRetain":0}} 13:12:16 MQT: PintuGerbang/stat/STATUS1 = {"StatusPRM":{"Baudrate":115200,"SerialConfig":"5N1","GroupTopic":"sonoffs","OtaUrl":"http://thehackbox.org/tasmota/tasmota-minimal.bin","RestartReason":"Software/System restart","Uptime":"0T00:09:49","StartupUTC":"2020-02-11T06:02:27","Sleep":50,"CfgHolder":4617,"BootCount":799,"BCResetTime":"2020-02-11T10:45:45","SaveCount":23005,"SaveAddress":"F9000"}} 13:12:16 MQT: PintuGerbang/stat/STATUS2 = {"StatusFWR":{"Version":"8.1.0.7(9814468-tasmota)","BuildDateTime":"2020-02-10T23:00:10","Boot":5,"Core":"2_6_1","SDK":"2.2.2-dev(38a443e)","Hardware":"ESP8266EX","CR":"338/699"}} 13:12:16 MQT: PintuGerbang/stat/STATUS3 = {"StatusLOG":{"SerialLog":2,"WebLog":2,"MqttLog":0,"SysLog":0,"LogHost":"domus1","LogPort":514,"SSId":["omahklodranplus",""],"TelePeriod":10,"Resolution":"558180C0","SetOption":["54000009","2805C8000100060000005A00000000000000","00000000","00000000"]}} 13:12:16 MQT: PintuGerbang/stat/STATUS4 = {"StatusMEM":{"ProgramSize":569,"Free":432,"Heap":25,"ProgramFlashSize":1024,"FlashSize":1024,"FlashChipId":"14405E","FlashMode":3,"Features":["00000809","8FDAE397","043683A0","22B617CD","01001BC0","00007881","00000000"],"Drivers":"1,2,3,4,5,6,7,8,9,10,12,16,18,19,20,21,22,24,26,29","Sensors":"1,2,3,4,5,6,7,8,9,10,14,15,17,18,20,22,26,34"}} 13:12:16 MQT: PintuGerbang/stat/STATUS5 = {"StatusNET":{"Hostname":"PintuGerbang-2709","IPAddress":"192.168.1.9","Gateway":"192.168.1.1","Subnetmask":"255.255.255.0","DNSServer":"118.98.44.100","Mac":"5C:CF:7F:79:EA:95","Webserver":2,"WifiConfig":2,"WifiPower":0.0}} 13:12:16 MQT: PintuGerbang/stat/STATUS6 = {"StatusMQT":{"MqttHost":"192.168.1.5","MqttPort":1883,"MqttClientMask":"DVES%06X","MqttClient":"DVES_79EA95","MqttUser":"aspi","MqttCount":1,"MAX_PACKET_SIZE":1000,"KEEPALIVE":30}} 13:12:16 MQT: PintuGerbang/stat/STATUS7 = {"StatusTIM":{"UTC":"Tue Feb 11 06:12:16 2020","Local":"Tue Feb 11 13:12:16 2020","StartDST":"Sun Jan 26 00:00:00 2020","EndDST":"Sun Jan 26 00:00:00 2020","Timezone":99,"Sunrise":"14:05","Sunset":"00:03"}} 13:12:16 MQT: PintuGerbang/stat/STATUS10 = {"StatusSNS":{"Time":"2020-02-11T13:12:16","DHT11":{"Temperature":null,"Humidity":null},"TempUnit":"C"}} 13:12:16 MQT: PintuGerbang/stat/STATUS11 = {"StatusSTS":{"Time":"2020-02-11T13:12:16","Uptime":"0T00:09:49","UptimeSec":589,"Heap":25,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"POWER1":"OFF","POWER2":"OFF","POWER3":"OFF","POWER4":"OFF","Wifi":{"AP":1,"SSId":"omahklodran_plus","BSSId":"50:64:2B:27:A7:77","Channel":9,"RSSI":80,"Signal":-60,"LinkCount":1,"Downtime":"0T00:00:06"}}} 13:12:20 MQT: PintuGerbang/tele/STATE = {"Time":"2020-02-11T13:12:20","Uptime":"0T00:09:53","UptimeSec":593,"Heap":27,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"POWER1":"OFF","POWER2":"OFF","POWER3":"OFF","POWER4":"OFF","Wifi":{"AP":1,"SSId":"omahklodran_plus","BSSId":"50:64:2B:27:A7:77","Channel":9,"RSSI":82,"Signal":-59,"LinkCount":1,"Downtime":"0T00:00:06"}} 13:12:20 MQT: PintuGerbang/tele/SENSOR = {"Time":"2020-02-11T13:12:20","DHT11":{"Temperature":null,"Humidity":null},"TempUnit":"C"}

- [ ] Provide the output of the Console log output when you experience your issue; if applicable:
  _(Please use_ `weblog 4` _for more debug information)_

Console output here:



### TO REPRODUCE
_Steps to reproduce the behavior:_

### EXPECTED BEHAVIOUR
_A clear and concise description of what you expected to happen._

### SCREENSHOTS
_If applicable, add screenshots to help explain your problem._

### ADDITIONAL CONTEXT
_Add any other context about the problem here._

**(Please, remember to close the issue when the problem has been addressed)**
Jason2866 commented 4 years ago

You can flash a firmware with the needed driver via OTA. Easiest ist to go back to the version before. The main reason for jump to 8.1.0.7 was the optimized DHT drivers. Just OTA this file: http://thehackbox.org/tasmota/archive/20200210-180027-7d0577e-tasmota.bin

adharyanto commented 4 years ago

Hallo,

Thanks for the info…

I tried to update again via OTA Url = http://thehackbox.org/tasmota/archive/20200210-180027-7d0577e-tasmota.bin

But after restarting, it keeps on version 8.1.0.7, (not reverted back to 8.1.0.6) ….. and the DHT11 Temp & Humid still = NULL

Regards,

Antonius

From: Jason2866 [mailto:notifications@github.com] Sent: Tuesday, 11 February, 2020 04:39 PM To: arendst/Tasmota Cc: adharyanto; Author Subject: Re: [arendst/Tasmota] Tasmota 8.1.0.7: DHT11 Temp =NULL & Humid = NULL (#7717)

You can flash a firmware with the needed driver via OTA. Easiest ist to go back to the version before. The main reason for jump to 8.1.0.7 was the optimized DHT drivers. Just OTA this file: http://thehackbox.org/tasmota/archive/20200210-180027-7d0577e-tasmota.bin

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/arendst/Tasmota/issues/7717?email_source=notifications&email_token=ALLUYK7RSWTML5QYUTICYFTRCJW2HA5CNFSM4KS263T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELLX6JI#issuecomment-584548133 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ALLUYK4OCURJMHAKJTL7RBDRCJW2HANCNFSM4KS263TQ .

schrej-zz commented 4 years ago

Hi, I have an error, that might be related to this. I also flashed 8.1.0.7. My DHT22 sent Temp and Humidity for about 12h. Afterwards the return value of both was empty. (Not NULL, as seen before with older versions). Additionally, as a difference to former versions, this can be solved by restarting the device. In older versions, I had to cut power off from DHT.

ascillato2 commented 4 years ago

@adharyanto @schrej-zz

Going back to 8.1.0.6 solves your issue?

adharyanto commented 4 years ago

In order to flash back to 8.1.0.6, I have to dismantle the device, open the compartment, and flash manually the esp8266, which is a bit complicated, and I try to avoid this.

I need to do this OTA, but the link given by Jason2866 (http://thehackbox.org/tasmota/archive/20200210-180027-7d0577e-tasmota.bin) did not work, it keeps on version 8.1.0.7 after restarting, and the issue is still there…

If you have some other ways to flash back to 8.1.0.6, (= via OTA =), please inform me….and I will try again…

schrej-zz commented 4 years ago

Just OTA with a tasmota-minimal.bin and then try the bins in http://thehackbox.org/tasmota/archive/ from most recent to older until the issue vanishes.

adharyanto commented 4 years ago

First I tried to update:

then I choose one of the archive (20200207):

It works, and it switches back to 8.1.0.6, and the Temperature & Humidity are back to normal value, (not NULL).

Fyi; I tried also version 8.1.0.8, the result is the same as 8.1.0.7, Temp & Humid = NULL, so I will have to use the 8.1.0.6.

Thanks.

ascillato2 commented 4 years ago

@adharyanto @schrej-zz

Going back to 8.1.0.6 have solved your issue? We need more information so as to properly address this issue.

So far, all my DHTs works very reliable no matter if I use the new or the old driver. I have one of those outside at direct sunlight with a small roof for rain protection with half meter wires and connected to a nodemcu and this one with a harsh environment have never failed in 2 years. With the new driver it still works fine without hunging. So, are you sure that your DHT has enough power and proper wiring?

ascillato2 commented 4 years ago

It works, and it switches back to 8.1.0.6, and the Temperature & Humidity are back to normal value

Ok. Thanks for testing :+1:

ascillato2 commented 4 years ago

@KrzysztofPrzygoda

What do you think? What can be the issue with the new driver? So far we have some DHT that needs the old driver, some others that needs the new driver and some others that works with any driver.

adharyanto commented 4 years ago

Further testing on the archives:

http://thehackbox.org/tasmota/archive/20200210-190030-7339b56-tasmota.bin === (null value = v8.1.0.7)

http://thehackbox.org/tasmota/archive/20200210-180027-7d0577e-tasmota.bin === (null value = v8.1.0.7)

http://thehackbox.org/tasmota/archive/20200210-170025-edadaa2-tasmota.bin === (normal value = v8.1.0.7)

http://thehackbox.org/tasmota/archive/20200207-140026-1a074da-tasmota.bin === (normal value = v8.1.0.6)

So, in my case I can use v8.1.0.6 (any version) OR v8.1.0.7 with specific this version = (20200210-170025-edadaa2) but not the newer…

From: Adrian Scillato [mailto:notifications@github.com] Sent: Thursday, 13 February, 2020 06:39 AM To: arendst/Tasmota Cc: adharyanto; Mention Subject: Re: [arendst/Tasmota] Tasmota 8.1.0.7: DHT11 Temp =NULL & Humid = NULL (#7717)

@KrzysztofPrzygoda https://github.com/KrzysztofPrzygoda

What do you think? What can be the issue with the new driver? So far we have some DHT that needs the old driver, some others that needs the new driver and some others that works with any driver.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/arendst/Tasmota/issues/7717?email_source=notifications&email_token=ALLUYK47A5U4DIE67UNZWOTRCSCB3A5CNFSM4KS263T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOELSZ5FA#issuecomment-585473684 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ALLUYK4X5OACACN22UQ3HODRCSCB3ANCNFSM4KS263TQ .

PabloVogel commented 4 years ago

Sorry for introducing this subject again, but i would like to propose eliminating the high digital writes from the driver. As I explained a high is sustained by the pull up resistor, so the sensor and the esp only asserts low.

The change is very simple the pin is always in input_pull and when a low is required you do pinmode digital write and pinmode again. The dhtprep method is empty because the pin is allways in input pullup.

But I don't know what will happen with other setups. So I think this is a good opportunity to implement this in the new driver.

I've modified the 8.1.0 with those changes and so far so good. What worked before work now and what not still don't

BTW this change is important because it prevents a potentially dangerous situation when the esp output high and dht causing overcurrent. With the the new implementation this is harmless. Both ESPeasy and adafruit don't asserts highs.

ascillato commented 4 years ago

@PabloVogel If you have implemented that change, please do a PR for review.

arendst commented 4 years ago

Pls try lates dev where the DHT driver has been replaced by the ESPEasy version.

Great fun!!!

ascillato commented 4 years ago

So far, this fourth version of the DHT driver also works fine in my devices.

We need the confirmation from the people that have issues with these devices:

@PabloVogel @adharyanto @KrzysztofPrzygoda @schrej-zz @klaus-pittisch @kiwichrish @Magnus-rosenborg @dcbo @Dreamoffice @DasUrmel @DarthKegRaider @retroip @johination

that this new driver in latest Tasmota (http://thehackbox.org/tasmota/) is working fine for them, in order to deprecate older drivers.

klaus-pittisch commented 4 years ago

testing ..... 10x Am2301 with 8.1.0.8

PabloVogel commented 4 years ago

no issues with new driver

adharyanto commented 4 years ago

V8.1.0.8 works fine on my DHT11 device, no issues…

Thank you…

klaus-pittisch commented 4 years ago

11 hours, 10 sensors, no issues

Thank you

OptimusGREEN commented 4 years ago

8.1.0.8 still says null for a dht11 that I have image image

arendst commented 4 years ago

@OptimusGREEN did it ever work?

Check your hardware connections and verify if it's a DHT11 after all.

PabloVogel commented 4 years ago

Wich is the candidate for the release? V4 or V3?. I find V3 much more clearer.

arendst commented 4 years ago

V4 is a lot smaller while providing the same functionality. So V4 it is.

OptimusGREEN commented 4 years ago

@OptimusGREEN did it ever work?

Check your hardware connections and verify if it's a DHT11 after all.

Hi, yes it was working last night on whatever previous version I had. When I woke up this morning it said null and that's when I came across this issue so figured I'd update to the mentioned version in hope that it would fix the problem.

ascillato commented 4 years ago

When I woke up this morning it said null and that's when I came across this issue so figured I'd update...

@OptimusGREEN So, your DHT11 was not working before the update right?

OptimusGREEN commented 4 years ago

@OptimusGREEN So, your DHT11 was not working before the update right?

That's right. It was working then it wasn't so I updated.

ascillato commented 4 years ago

So, may be is not the new driver but a problem in your DHT11. Have you recheched wirings and power supply?

OptimusGREEN commented 4 years ago

not, yet as im out but the wiring hasnt been touched since it was working. I'll get a look once I'm home thanks.

PabloVogel commented 4 years ago

I would like to propose a little tidy up for the v4. As you want to condense the ESPeasy code in one function the problem is breaking out two levels of for loops for which C lacks the appropriates control statements. As K&R said

Nevertheless, there are a few situations where gotos may find a place. The most common is to abandon processing in some deeply nested structure, such as breaking out of two or more loops at once. The break statement cannot be used directly since it only exits from the innermost loop.

  noInterrupts();
  if (!DhtExpectPulse(sensor, LOW) ||
      !DhtExpectPulse(sensor, HIGH) ||
      !DhtExpectPulse(sensor, LOW)) {
     goto DHT_READ_ERROR;
  }

  for (uint32_t i = 0; i < 5; i++) {
    int data=0;
    for (uint32_t j = 0; j < 8; j++) {
      if (!DhtExpectPulse(sensor, HIGH)) goto DHT_READ_ERROR;
      delayMicroseconds(35);                          // Was 30
      if (digitalRead(Dht[sensor].pin)) {
        data |= (1 << (7 - j));
      }
      if (!DhtExpectPulse(sensor, LOW)) goto DHT_READ_ERROR;
    }
    dht_data[i] = data;
  }
  interrupts();

  checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF;
  if (dht_data[4] != checksum) {
    char hex_char[15];
    AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"),
      ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum);
    return false;
  }

  return true;

DHT_READ_ERROR:
  interrupts();
  return false;
}

The AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), level ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); is mvoed inside DhtExpectPulse

arendst commented 4 years ago

Thx for your opinion but:

So again, I think it stays this way.

PabloVogel commented 4 years ago

I respect your preferences but the DhtExpectPulse would not extend the time because addlog is inside an if statement and is only executed on the event of timeout.

    if (micros() > timeout) {
       AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), level ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW);
       return false;
    }

That allows the change form

 level = 0;
 if (!DhtExpectPulse(sensor, level)) { break; }  // Expect LOW

to

 if (!DhtExpectPulse(sensor, LOW)) { break; }  

May I propose another version with breaks and no goto?

arendst commented 4 years ago

Make my day.

PabloVogel commented 4 years ago

It really is the same but as I did promise, here it is.

  bool error=false;
  noInterrupts();
  if (!DhtExpectPulse(sensor, LOW) &&
      !DhtExpectPulse(sensor, HIGH) &&
      !DhtExpectPulse(sensor, LOW)) {

    int data=0;
    for (uint32_t i = 0; i < 5; i++) {
      for (uint32_t j = 0; j < 8; j++) {
        if (!DhtExpectPulse(sensor, HIGH)) {
          error=true;
          break;
        }
        delayMicroseconds(35);                          // Was 30
        if (digitalRead(Dht[sensor].pin)) {
          data |= (1 << (7 - j));
        }
        if (!DhtExpectPulse(sensor, LOW)) {
          error=true;
          break;
        }
      }
      if (error) break;
      dht_data[i] = data;
    }
  }
  interrupts();

  if (error)
    return false;

It compiles but it's not tested as it doesn't really changes anything.

klaus-pittisch commented 4 years ago

2 of 10 DHT22 report NULL after 1 day :( shit

sentiment-bot[bot] commented 4 years ago

Please be sure to review the code of conduct and be respectful of other users. Keep in mind, this repository uses the Contributor Covenant.

klaus-pittisch commented 4 years ago

Unbenannt

mike2nl commented 4 years ago

Another thing which came up while i tested now the v4 dht driver. I have a lot of DHT11 and 22 sensors because some older projects which are no longer used, but testing the whole thing was to now the things in the new driver.

What came is with older or hard used sensors (outside use) the the values can get higher then 100% rel. humidity. I had 2 sensors from 15 (yes 15) which gave me 104 and 106. I see we have the check for: if ((NAN == Dht[sensor].h)

But not for the overshot. Checked some libraries for the DHT sensors and found that some make use of that check. Tested them with an wemos d1 mini and yes the mas values showed was 100. But that is not really a good way. Better would be another type of value. The NAN doesn't show the overshot. What could be done for Tasmota to check this and show it on the web ui, or in jason to message the user that the sensor must be refreshed or use a new one.

Refresh a sensor is not that complicated but with tha costs for that type of sensor we don't need to do it. More for the BME280 and BME680. Working on a document which describe's it.

arendst commented 4 years ago

For those getting NULL reports pls use the latest commit and enable more logging with option 3 and monitor DHT messages like:

12:51:29 DHT: Timeout waiting for start signal low pulse
12:51:29 DHT: Timeout waiting for start signal high pulse
12:51:29 DHT: Invalid NAN reading
12:51:29 DHT: Checksum failure 01 41 41 D7 DF =? 5A
OptimusGREEN commented 4 years ago

ok so my issue turned out to be the fact i was using gpio2 i think. I've switched to gpio3 and all is good.

schrej-zz commented 4 years ago

First feedback: Config: AM2301on GPIO3 Got regular "Timeout waiting for start signal high pulse" After end of serial logging, I got "Timeout waiting for start signal low pulse" once. None of these messages afterwards up to now.

kiwichrish commented 4 years ago

Put this on four test setups, 24hr run time and fine with AM2302's on:

With Aosong branded AM2302 survives multiple restarts (timer relay on reset, resetting it every 15 mins for the last day on both the EPS-01 and WemosD1).

Issue with Asair branded AM2302 is still there, but that's nothing to do with Tasmota / firmware it's the ESP POST serial output crashing the sensor. Per some other comments, don't use GPIO0/2 if you want these sensors 100% reliable.

DHT11 on ESP01/GPIO2 fine for a day as well.

PabloVogel commented 4 years ago

The biggest change in the new driver was the remotion of all digitalWrite(Dht[i].pin, HIGH); statements. Here is a thread on the topic https://forum.arduino.cc/index.php?topic=377413.0.

The data line is half duplex so collisions may occur and output mismatch between MCU and sensor not only can hang the device but also may destroy it or burn a gpio,

The protocol is really simple. The resistor pull-up maintains 3.3 V on the data line, indicating idle status. When the ESP wants a reading it output a single 0V pulse and waits , then the DHT acknowledges (another single low pulse) and report the data.

PabloVogel commented 4 years ago

Sorry to bother again, but after removing the sensor from a sonoff I found the device is still reporting the last value instead of nan, but the logs show that the read fails. Im using 8.1.0.8

18:18:32 DHT: Timeout waiting for start signal low pulse

It seems that the early exit in V5 prevents the code from updating the values

  interrupts();
  if (error) { return false; }

checking V4 the code that updates the readings was in DhtReadTempHum which was merged in DhtRead.

So V4 is safe.

kiwichrish commented 4 years ago

Just noticed there's multiple versions talked about.. My testing was 8.1.0.8, using dht_4 complied using the arduino IDE, really should get with the cool kids and use platformio, but too many projects in the arduino IDE I'm working on. :-)

PabloVogel commented 4 years ago

After revisiting V5 I found that the "==" operator cannot be used against NAN, the correct way to do that is by using the insnan() function. I think the section can be eliminated altogether. If the section is not removed DHTSTRUCT.lastresult must be initalized. The original driver did that.

If you eliminate the max retry counter section the and initalize at the beginning Dht[sensor].t= NAN;Dht[sensor].h= NAN you can keep the code as is.

When a timeout occurs, if another log line is added then the timeout message is lost (PrepLog_P2 ). For example if there is a timeout then isnan is always true so I eliminated that log line (not very good)

Here is what I have and it's working. It preserves the max retries logic I connected the power of a DHT22 to a Gpio configured as relay. So I can turn the sensor on and off remotely

Sorry I don't have a forked repo yet, this code goes after interrupts()

  float temperature = NAN;
  float humidity = NAN;
  if (!error) {
    uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF;
    if (dht_data[4] == checksum) {
      switch (Dht[sensor].type) {
        case GPIO_DHT11:
          humidity = dht_data[0];
          temperature = dht_data[2] + ((float)dht_data[3] * 0.1f);  // Issue #3164
          break;
        case GPIO_DHT22:
        case GPIO_SI7021:
          humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1;
          temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1;
          if (dht_data[2] & 0x80) {
            temperature *= -1;
          }
          break;
      }
    }
    else { //Checksum error
      char hex_char[15];
      AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"),
        ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum);
    }
  } //error

  if (isnan(humidity)) { //Both t and h are numbers or not
    Dht[sensor].lastresult++;
    if (Dht[sensor].lastresult > DHT_MAX_RETRY) {  // Reset after 8 misses
      Dht[sensor].t = NAN;
      Dht[sensor].h = NAN;
    }
    return false;
  }

  if (humidity > 100) { humidity = 100.0; }
  if (humidity < 0) { humidity = 0.1; }
  Dht[sensor].h = ConvertHumidity(humidity);
  Dht[sensor].t = ConvertTemp(temperature);
  Dht[sensor].lastresult = 0;

  return true;
}
arendst commented 4 years ago

Thx. I also found out the lastresult code was failing.

As most of the time I did it my way in the latest commit ;-)

PabloVogel commented 4 years ago

After analyze the code I noticed that when you go input immediately after the low pulse you don't need that tricky delay(50) that has been subject to so much tweaking.

The original version used a digitalWrite(pin,HIGH) so you weren't able to detect the start of the ACK, thus forced to a blind delay

But now you can use an active expectPulse to wait the ACK to begin.

You do

 pinMode(Dht[sensor].pin, INPUT_PULLUP);
  if (!ExpectPulse(Dht[sensor].pin, HIGH)) { //Waiting for ACK
    AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Timeout waiting for DHT ACK"));
    return false;
  }

Instead of

 pinMode(Dht[sensor].pin, INPUT_PULLUP);
  switch (Dht[sensor].type) {
    case GPIO_DHT11:                                    // DHT11
    case GPIO_DHT22:                                    // DHT21, DHT22, AM2301, AM2302, AM2321
      delayMicroseconds(50);
      break;
    case GPIO_SI7021:                                   // iTead SI7021
      delayMicroseconds(20);                            // See: https://github.com/letscontrolit/ESPEasy/issues/1798
      break;
  }

And I also believe by that very reason the adafruit code from the old driver is better than the ESPeasy because it doesn't contain fixed waits.

  for (uint32_t i = 0; i < 80; i += 2) {
    cycles[i]   = ExpectPulse(Dht[sensor].pin, LOW);
    cycles[i+1] = ExpectPulse(Dht[sensor].pin, HIGH);
  }

I'm testing this new implementation and so far no errors.

So the protocol looks like that (with better logging) and only one hard wait (Necesary as is an output)

 if (digitalRead(Dht[sensor].pin) != IDLE) {
    AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Data line BUSY skipping read"));
    return false;
  }

  //One big fat pulse to request for data
  pinMode(Dht[sensor].pin, OUTPUT);
  digitalWrite(Dht[sensor].pin, LOW);
  delay(20);
  pinMode(Dht[sensor].pin, INPUT_PULLUP);

  //Perhaps a little delay here? A pair of microsec.

  if (!ExpectPulse(Dht[sensor].pin, HIGH)) { //Waiting for ACK
    AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Timeout waiting for DHT ACK"));
    return false;
  }

  if (!ExpectPulse(Dht[sensor].pin, LOW)) { //In the ACK
    AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "DHT ACK too long"));
    return false;
  }

  if (!ExpectPulse(Dht[sensor].pin, HIGH)) { //Waiting for response
    AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Timeout waiting for Data transmission"));
    return false;
  }

  noInterrupts();
  for (uint32_t i = 0; i < 80; i += 2) {
    cycles[i]   = ExpectPulse(Dht[sensor].pin, LOW);
    cycles[i+1] = ExpectPulse(Dht[sensor].pin, HIGH);
  }
  interrupts();

As you know the request signal is theoretically not time dependent as the specs say "at least", meaning you ring the doorbell for long enough.

Interrupts are disabled very late, but it hasn't failed (yet) in my setup.

The logging is more useful because if you only get "Timeout waiting for DHT ACK" you know the sensor is hung.

You may have noticed that expectPulse don't return timeout, that is because I think it has two failure case.

So I settle for 0 for error for both cases

int32_t ExpectPulse(uint8_t pin, bool level)
{
  uint32_t count = 0;
  for (uint32_t count=0; count < dht_max_cycles; count++) {
    if (digitalRead(pin) != level) {
      return count; //If count ==  0 then pulse is missing
    }
  }
  return 0; // Timeout
}
EvilForge commented 4 years ago

FYI - been lurking watching this thread and others regarding DHT sensors. Up to v8.1.0.2 I was having NULL after several hours on a self-compiled (arduino IDE) wemos D1 mini with the newer small blue sensor. One was in a greenhouse, the other the attic. Its winter so temps are cool (28 to 75F) here. Humidity ranges from 20-100%. I have tried pull-up resistors on one device with no improvement. The greenhouse is sensor only (analog light, DHT), and the Attic is a DHT and analog voltage sense for a doorbell circuit. Neither control a switch/relay.

For 2 days fw8.1.0.8 is still returning data with no issues. Hassio history graphs show no data dropouts at night when I'd miss seeing it.

I do have another outside sensor (same design, my DIY esp8266 wemos mini with a DHT sensor) as the two misbehaving, that has never failed. All sensors were from the same pack, but the esp's are varied sources (Banggood, Amazon, Ebay).

Anyhow, thanks again to all for helping resolve this. If you need any other data, I can try to get it for you, or even flash backwards if it will help put this one to rest. I have enjoyed Tasmota a lot, and I don't mind giving back some data. :)

PabloVogel commented 4 years ago

For what is worth, I was incorrect about the request timing, it is important and very unreliable. I found that DHT22 can start acknowledging before the pulse ends. So after input_pullup the line may never go HIGH. I still strongly recommend replacing the fixed delay with a wait busy loop.

Also, May I recommend a refactor?

If you break inside a for loop, even at the end, the condition will be true on exit; so you can retest it to determine how it ended:

for(init;cond;expr) {
   if (something) break;
}
if (cond) return false; //break executed

Also instead of a double for, you can use integer division and module

    for (i = 0; i < 5; i++) {
      for (j = 0; j < 8; j++) {

Can be written as

    for (k = 0; k < 40; k++) {
      i=k/8. //Integer division
      j=i%8

So

  bool error = false;
  noInterrupts();
  if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) {
    for (uint32_t i = 0; i < 5; i++) {
      int data = 0;
      for (uint32_t j = 0; j < 8; j++) {
        if (!DhtWaitState(sensor, 1)) {
          error = true;
          break;
        }
        delayMicroseconds(35);                          // Was 30
        if (digitalRead(Dht[sensor].pin)) {
          data |= (1 << (7 - j));
        }
        if (!DhtWaitState(sensor, 0)) {
          error = true;
          break;
        }
      }
      if (error) { break; }
      dht_data[i] = data;
    }
  } else {
    error = true;
  }
  interrupts();
  if (error) { return false; }

Can be expressed like this

  uint32_t i=0;  
  noInterrupts();
  if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) {
    for (i = 0; i < 40; i++) {
        if (!DhtWaitState(sensor, 1)) {
          break;
        }
        delayMicroseconds(35);                          // Was 30
        if (digitalRead(Dht[sensor].pin)) {
          dht_data[i/8] |= (1 << (7 - i%8));
        }
        if (!DhtWaitState(sensor, 0)) {
          break;
        }
    }
  interrupts();
  if (i<40) { return false; }

No double break :)

ascillato2 commented 4 years ago

Closing this issue as it has been solved.

Thanks everyone for helping and testing :+1:


Support Information (Guide)

See Wiki for more information. See FAQ for common questions/answers and links if none of your question is in the list. See Chat for more user experience. See Community for forum. See Code of Conduct