DHT22 - Working well and giving timeout error after 24-48-72 hours

Dear all,

First, thanks to everyone that will help/give advice/...
I've already looked intensively on this forum and on Internet but I coulnd't find the solution to my current issue.

I'm finshing a ventilation project for my house.
Hardware
I used an Arduino Mega (ATmega2560). This arduino collects information from

  • 7 DHT 22 sensors with 4.7k pullup and 100nF capacitor between GND and 5V
  • 3 presence sensors
  • 2 DS18B20 sensors (1-Wire)
  • 1 dust sensor
  • 1 Air Quality sensor (Analogic)
  • 1 3.5" TFT touch screen Kuman

The arduino pilots 19 relays :

  • 1 for turning on/off the TFT (5V)
  • 4 for turning on/off blowers (220V) - 2 blowers - 2speed each
    -14 for opening/closing 7 valves (220V)

The arduino is fed by a dedicated 5V-5A power supply
The relay are fed by a separated 5V-2A power supply

First Issue with DHT22
At the beginning, I had a first issue with the DHT22. At the start-up of the arduino, they were giving correct values. But, as soon as one 220V relay was switching-off, I lost the communication with DHT22 sensors. I solve the problem by connecting capacitors between the 2 pins of the relay (0.1 microF-630V for the blowers and 0.047 microF-630V for the valve).
This solved the issue. (I suppose taht, even with different power supply, the voltage surge (220--> 300V) when the relay was switching could influence arduino voltage).

Description of current issue
Now, everything is working fine but, it remains one issue : DHT22 stop responding after 24-72 hours, sometimes more. And, not all stopped responding at the same time.
Another point is that I don't need to reboot the arduino to have them worked again. If I unplug the sensor for a second and that I plug it again, it starts working again.
I have thought of putting the supply of the DHT22 sensors through a transistor and to restart the supply when the error is occuring but I would prefer to find more elegant solution.

Code used for DHT
I tried with many different library.
Now, I switched to DHTNEW RobTillart Library.
I see that when the sensor stopped responding, it is due to the fact tha it never goes HIGH in this part of the library code :
// SENSOR STAYS LOW for ~80 us => or TIMEOUT
if (_waitFor(HIGH, 90)) return DHTLIB_ERROR_TIMEOUT_A;

// SENSOR STAYS HIGH for ~80 us => or TIMEOUT
if (_waitFor(LOW, 90)) return DHTLIB_ERROR_TIMEOUT_B;

I have 7 sensors and I request temperature every 3 second to 1 of them. So, each sensor has to respond every 21 second.
I do not use the "delay" function for this because the loop should still be working for the interface but I use the millis() function to determine when to read the sensor.

Here is the code used for reading one sensor.
// Lecture d'un capteur DHT22 Température et Humidité avec la librairie dhtnew de Rob Tillaart
void readDHT22(DHTNEW mySensor, float* temperature, float* humidity)
{
float tempe;
float hum;
static uint16_t dht_delay = 1000;
uint8_t type = 22;
mySensor.setType(type);
// READ DATA
//uint32_t start = micros();
int chk = mySensor.read();
//uint32_t stop = micros();

switch (chk)
{
case DHTLIB_OK:
Serial.print("OK,\t");
dht_delay -= 10;
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.print("Checksum error,\t");
dht_delay += 10;
break;
case DHTLIB_ERROR_TIMEOUT_A:
Serial.print("Time out error A,\t");
dht_delay += 10;
break;
case DHTLIB_ERROR_TIMEOUT_B:
Serial.print("Time out error B,\t");
dht_delay += 10;
break;
case DHTLIB_ERROR_TIMEOUT_C:
Serial.print("Time out error C,\t");
dht_delay += 10;
break;
case DHTLIB_ERROR_TIMEOUT_D:
Serial.print("Time out error D,\t");
dht_delay += 10;
break;
case DHTLIB_ERROR_SENSOR_NOT_READY:
Serial.print("Sensor not Ready Error,\t");
dht_delay += 10;
break;
default:
Serial.print("Unknown error,\t");
dht_delay += 10;
break;
}

dht_delay = constrain(dht_delay, 100, 5000);
// Record DATA
hum = 0.1*(10mySensor.getHumidity());
tempe = 0.1
(10*mySensor.getTemperature());

*humidity = hum;
*temperature = tempe;
//uint32_t duration = stop - start;
delay(dht_delay);
}

And, this code is launched in this function that manage all sensors :

// Fonction de lecture de tous les DHT22
void readAllDHT22(){ // Modification de la fonction pour des mesures en cascade
static unsigned long long previousMillis = 0;
static unsigned long long storeMillis = 0;
static int count = 0;
int nbMeas = 7;
int interval = nbMeas + 1;
if (previousMillis + DelayMeas/interval < supMillis and count == 0){
readDHT22(T_HUM_SDBp, &temp_SDBp, &hum_SDBp);
count ++;
previousMillis = supMillis;
}
else if (previousMillis + DelayMeas/interval < supMillis and count == 1)
{readDHT22(T_HUM_SDBe, &temp_SDBe, &hum_SDBe); count ++; previousMillis = supMillis;}
else if (previousMillis + DelayMeas/interval < supMillis and count == 2)
{readDHT22(T_HUM_WC, &temp_WC, &hum_WC); count ++; previousMillis = supMillis;}
else if (previousMillis + DelayMeas/interval < supMillis and count == 3)
{readDHT22(T_HUM_Chamb, &temp_Chamb, &hum_Chamb); count ++; previousMillis = supMillis;}
else if (previousMillis + DelayMeas/interval < supMillis and count == 4)
{readDHT22(T_HUM_Sdj, &temp_Sdj, &hum_Sdj); count ++; previousMillis = supMillis;}
else if (previousMillis + DelayMeas/interval < supMillis and count == 5)
{readDHT22(T_HUM_Outside, &temp_Outside, &hum_Outside); count ++; previousMillis = supMillis;}
else if (previousMillis + DelayMeas/interval < supMillis and count == 6)
{readDHT22(T_HUM_Hall, &temp_Hall, &hum_Hall); count = 0; previousMillis = supMillis;}
}

Thanks a lot for your help,

Laurent

On this forum, in the right-bottom of the text there is More/Modify. Use "Modify" to change your post.
Please put your sketch between code-tags. The </> button is for code tags.

They are [ code ] and [ / code ] (but without the spaces).

This is the Blink Without Delay: https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay.

I'm afraid you are overthinking it.
I don't understand what the calculation of the delay() in readDHT22() is for.
If you don't get a good value from a DHT sensor, then don't use that and try again 21 seconds later. You could make an error counter and after three consecutive errors, then something is wrong.

This:

if (previousMillis  + DelayMeas/interval < supMillis and count == 0){

should be replaced by a good millis() timer.

unsigned long currentMillis = millis();

if( currentMillis - previousMillis >= 3000)
{
  previousMillis = currentMillis;

  if( count == 0)
  {
    ...
  }
  else if( count == 1)
  {
    ...
  }

  count++;
  if( count > MAX_SENSORS)
  {
    count = 0;
  }
}

The first optimization would be to rename 'count' into 'index' and use a table with all your sensors. Then you can use the 'index' as a index of the table and you don't need all those if-else-if anymore.

Please show the full sketch.
There is even a website for that: https://snippets-r-us.com/.

I can not spot the problem at the moment. How long are the wires to the DHT sensors ? Are those wires next to wires with mains ? Maybe we need a schematic, links to all your modules and photos. How is your grounding ? Are all GND connected and how ?

You can power such sensors straight off a pin, they need very little current. Makes a reset easier.

Thanks Koepel and wvmarle for these fast answers

Full code is below (but quite long : 2400 lines).

  1. Idea of vwmarle is good but I keep it for later. Up to now, my cabling is done such as all my sensors (DHT, presence, dust, air quality) are powered from the same PCB card. If I want to supply DHT separately, I need to modify the PCB. I would keep this option if I do not find any other solution.

  2. blink without delay
    I actually use the blink without delay. In my previous code extract, the supMillis variable is compute at the begininng of the loop() function with the superMillis() function (derivate of Millis() function to avoid to reach maximum value of millis()). I did this to avoid calling millis() fuction too much.
    The dht_delay variable in the readDHT22() fucntion comes from the example of Rob Tillaart. but, I already tried to remove it --> no change. If I understood well it is there just in case the sensor is not responding fast enough. Practically, having it at 100ms or 5000ms doesn't change anything (I didn't try to remove it yet but, I already tried other libraries without delay function).
    And, with both configurations (delay or not), as soon as the sensor starts not responding, it never responds (even if ignoring the error and trying another reading later on).

Your index idea is good, it'll make things clearer. I'll look for the change.

  1. Wires for DHT sensors varies from 3m to 10m. I used RJ45/cat6 or phone cables (but same issue in both case). In most configuration, the RJ45 cable is used for the 5V, the GND, one DHT22, one presence sensor and one led (I attached manual sketch with connexion of 1 sensor as an example...if it is not clear, I can try to make something better).
    The connexion between power supply/arduino/sensor PCB is done with 18AWG cables.
    GND are all connected (also with 18AWG cables).
    And, for the sensor PCB, I used all the back face to put the GND.

Thanks again for the time you spend to help me.

Laurent

Projet_Ventilation_2.1.ino (93.5 KB)

Sketch.pdf (247 KB)

Three things:

  1. Usage of this forum

  2. Fix the millis() in the sketch

  3. Fix the GND. Try to improve the wiring.

Please show a full sketch between code tags. Or was it too long for the forum ?
Please show a picture as a picture (jpg or png file). When it is attached, then it will often automatically be added into the post at the end.

Why did you decide to make a 64-bit millis ?
I think it is not fail safe and it does not solve something.
The millis() will rollover in about 50 days. If you need a timer that takes longer than 50 days, then I suggest to use a RTC and test for a certain day of the month or a certain year or something like that.
You also introduce a rollover problem by using your 64-bit millis wrong. So I see two problems with it.

The Blink Without Delay does not have a rollover problem !
It uses math to keep the software-millis-timer running perfectly, even during a rollover.

You have a buffer of 21 bytes in the print_unsigned_long_long() function. That is so scary :o You really counted the number of bytes. The size of 21 is good, but I prefer 30 or 40.
Well, I think you have to remove that function anyway.

This is probably not the cause of the problem, but I can not guarantee that it will not cause the burst of reading the DHT sensors. You have to fix it anyway :smiley_cat:

The DHT signal itself is only a good signal when it is together with a good GND wire. The GND wire is the most important of all.
So you need the DHT signal + GND from the Arduino board (you may add 5V for power). Put those in a cable and it will work.

The picture does not tell where the GND of the power supply is connected.
You might have to rewire that. Keep the GND currents away from the GND for the sensors.
If you could see all the current peaks and voltage peaks and inductive coupling of your project, then you would be surprised.

The display uses current, that should be no problem.
The inputs of the relays could be a optocoupler with a led. Suppose each input takes 20mA, that is 19*20mA = 380mA
That current might be too much for the Arduino Mega board and the microcontroller.

Is there a 4k7 pullup at the end of the cable ? The most common way is to have 4k7 at the Arduino board (for 1-Wire and DHT signal).
I know about problems with cables for I2C and 1-Wire, but I don't know about DHT sensors with a cable. Is there a good explanation online ?

Do you need the DHT sensors for the humidity ? That is not reliable. There are better sensors for that. Is it possible to replace the DHT sensors with DS18B20 sensors ?

I don't know how other wires in the cable have influence the DHT signal. You could try a few things. Perhaps using the other wire of the twisted pair for GND or leave it open.

If everything fails, can you add another cable ? Then bypass everything. Connect the GND, 5V, and DHT signal directly from the Arduino board to the cable and at the end of the cable the sensor. Put a 4k7 at the Arduino side of the cable.

Hello Koepel,

Thanks again.
To Answer your questions :

  1. Yes, the code was too long (2400 lines) and I got an error from the forum interface to post it.

  2. I didt it 64-bit in order not to have to reset the system every 50 days. But, you're right, I have also an RTC connected (I use it to turn on/off ventilation according to time) and I will use it and switch back to millis() function.
    For the print_unsigned_long_long() function, I use it only for debugging purposes. I'll remove it (no need if I don't use anymore 64-bit millis)

  3. I use DHT for humidity and temperature. Where I needed only temperature, I used DS18B20. I don't need to have very precise values : +/- 5-10% is already enough (bathroom ventilation starts when it goes above 60%....not really an issue if it is 70 or 50% in reality). Up to now, I haven't seen strange behavior of the sensors (when taking a shower, humidity is going up, when the ventilation starts, it goes down). I you think DHT22 are not reliable enough for my usage, I'm ready to try other sensor.

GND wiring : I go through a PCB to supply the arduino and the sensors with the same power supply (see attached image... Sorry, not possible to put it as it is. I receive error messages : The message exceeds the maximum allowed length (9000 characters).). Is it enough ? I paid attention to have very big tracks on +5V and GND to have no potential difference.

Keep the GND currents away from the GND for the sensors.

Sorry, I m not sure to understand. I use another power supply for the relays but I connected all GND. I could use separate GND, Is that what you mean ? In this case, how the relay board coud tell how is the signal coming from the arduino as they don't have the same GND.

Is there a 4k7 pullup at the end of the cable ?

Yes, I put it very close to the sensor. If it is better to have it on Arduino side, I can move it. I'll try. I ended up with this configuration because it was practically easier.

I'll also try one full twisted pair of the cable only for GND.

If it is not working, I can try putting one sensor very close to the Arduino and see what happens.

To sum up, here are the actions I'll do :

Software

  1. stop using 64-bit millis and switch to normal millis() fuction. As a second step, I'll use RTC information to go through 50 days limitation.
  2. remove print_unsigned_long_long() function
  3. use index for the different temperature measurements

Hardware

  1. Make trial with one sensor to move the 4.7k pull-up close to the arduino
  2. Make a trial with one sensor to use full twisted pair of RJ45 cable
  3. Make trial with one sensor to use a wire of a free twisted pair and let the other free
  4. Make a trial with one sensor connected very close to the Arduino and with direct connection to +5V and GND of the Arduino.

--> I'll do that this week and give news next weekend about the results of the actions.

Pending Questions from my side

  1. Common GND between 220 V relays, sensors and Arduino ? Do I need to keep it or may I separate the 220 V relay GND from the 2 other ?
  2. Usage of DHT22 for my application ?

Again, thanks a lot,

Laurent

It is hard for me to understand that picture.

If your relay boards have optocouplers for the inputs, then perhaps the relay boards have a separated GND for the input and another one for the relay coils ? Or are they connected on the relay board ?

Shall I try to explain about grounding in a different way ?

The Arduino board has a microcontroller with GND pins. Inside the microcontroller, that GND is the GND that it uses for the incoming signals. As soon as you connect a wire to the GND pin, then it will be a GND-via-a-wire. That is no longer a good GND.

The GND pin of the relay coil is connected via the relay module pcb, to a connector, to a wire, and finally also to the Arduino GND.
When you would measure the difference between the GND pin of the microcontroller and the GND pin of the relay coil, then you would be surprised how much noise and pulses and peaks there are. And that is only the voltage noise.

When a relay is activated, then there is current through the relay coil. That current flows into the GND, and to a GND wire. Every GND wire with current has a electromagnetic coupling to nearby wires and vice versa.

Even the Arduino + display shield requires some current. So the GND wire from the Arduino to the power supply has also a lot of noise.

Solution: Think of the DHT and 1-Wire bus as a combination of GND + signal. If one of them is noisy, then the signal is noisy. Connect the sensors with the signal and GND directly to the Arduino board, that is as close as possible to the microcontroller. At least keep the GND to the sensor separated from any GND that goes to things that needs power.

The millis() can be used to make a clock that is as accurate as the resonator or X-tal of the Arduino board. You could use millis() to make something that triggers once per day (or hour or minute). With that you can activate something every 100 days or so. That is the normal way to make very long delays. If done right, then it will run in theory forever without any limit by the maximum value of a variable.

unsigned long previousMillis;
unsigned long oneDay = 1000UL * 60UL * 60UL * 24UL;   // one day in milliseconds
int dayCounter = 0;

void setup()
{
  Serial.begin( 9600);
}

void loop()
{
  unsigned long currentMillis = millis();

  if( currentMillis - previousMillis >= oneDay)
  {
    previousMillis += oneDay;   // special way to keep in sync with time

    dayCounter++;
    if( dayCounter >= 100)
    {
      Serial.println( "Another hundred days have passed");
      dayCounter = 0;
    }
  }
}

Is the project really going wrong exactly at 24, 48 or 72 hours ? Then it must be something else. If you can first fix millis(), then I will look at the sketch once more.

Next time I will try to make a shorter post :wink:

Hello,

Thanks again for your explanation. I think I now fully understand GND challenge. I'll simplify my GND connexion and put the sensors GND directly connected to the Arduino.
(When my system will work, I intend to make a full PCB to plug the arduino and avoid all these wires).

Concerning the relays GND : the GND of the relay coils is common with the input one on the board. I use taht kind of relay board : https://www.amazon.fr/Elegoo-Optocoupleur-8-Channel-Arduino-Raspberry/dp/B06XL1F53G?th=1
I'll look for relay boards with separated GND.
In order to see if the problem is coming from the relays, I unpluged them. I'll see if datas are ok or not.

Thanks for the millis() point, I'll implement it.

For the 24-48-72 hours, it is not exactly at these time that it goes wrong. It was only to give order of magnitude and shows that there are some variability. And, all the sensors do not go down at the same time. Buyt, after 3 days, most of the time, all are down.

Thanks again,

Laurent

The 49-day limit of millis means you can not use it for a delay time longer than 49 days (unless you add a second counter, that keeps track of how often it has rolled over).

For any other period, it will work just fine - provided you store the starting time in a 32-bit int - even when millis() rolls over. That's thanks to the magic of integer math, which is what gets broken when you use a 64-bit int instead of a 32-bit one!

Hello,

Some feedback after testing the different ideas/suggestions you gave.

  1. GND relays --> on the relays board, there are 2 GND pins : one next to the input pins and the other one next to the +5V for the relay coils. On the PCB, there are connected. Due tot this configuration, I had in mind that GND was required on both side of the opto-coupler. In fact, and thanks to your comments, the GND on the input side is not required (current is directly going from 5V arduino to the pins). So, I disconnected GND on the input side.

  2. Millis() function : I implemented your suggestion : switch to normal millis() function.

  3. GND wiring : I took the GND directly from to Arduino for the sensors (and not from the poser supply as previously)

  4. Pullup resistor : I removed the pullup resistor of one DHT22 (resistor that I put close to the sensor and not close to the Arduino) and i put a new one (4.7k) close to the Arduino

  5. I used a full twisted pair for the data pin of one sensor.

All these actions did not fully solve my problem. But, I noticed that, one DHT22 gave measures 5 days in a row (which never happend in the past). So, it seems a bit more stable. Th "succcessful" sensor was the closest to the Arduino (3-4m) and didn't go down up to now. It was not the one on which I made trials. As a first guest, I should say that better stability was given by action 1) and 3).

For the most problematic sensors (going down quite fast : 2-3 hours), I replaced the 4.7k resistor by a 1k resistor...And, it is now working for 24hours....Let's see.
Do you see any issue using so low pullup resistor ?

Thanks again,

Laurent

laurent1982:
All these actions did not fully solve my problem.

Ouch ! Then the DHT22 is not meant to be used with long cables. I wrote before that I don't know the specific things about the DHT22 with long cables.

I looked into more than 10 datasheets, and it is very confusing. Some older datasheets have 1k pullup, but that has been changed to 4k7 or 5k1. Some datasheets suggest that 8mA is possible, that is with a pullup resistor of 625 Ω.
I think that 1k is okay.
What about self-heating ? When you communicate a lot with the DHT22, then it uses that current of 5mA a lot (with 1k pullup), that might heat up the sensor.

There are a few ways to use the twisted pair:

  • The signal on one and nothing on the other.
  • The signal on one and GND on the other.
  • The signal on one and GND on the other and connect the GND only at the Arduino and leave it open at the sensor. There has to an other wire for the power with VCC and GND.

Hello,

You're right DHT22 datasheet are quite confusing. And, in addition to that, my sensors (which look like the basics DHT22 with 4 pins) seem to have in-built resistor of 4.7k between pin 1 (VCC) and pin 2 (data)...So, maybe no pullup wiring needed...to be tested.

I'll try you suggested way of twisted pair usage.

Thanks,

Laurent

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.