Emulating DHT11 Temperature and Humidity Sensor for test purposes

Hey guys, I am new to this Forum.

I am trying to implement an emulator for an DHT11 (test purposes).

Currently I am facing the following problem:
The data-array right calculated but I have trouble with the time (Interrupt). In this Data array (DHT11_value_array) I am calculating the timing sequence which should be sent out later by an ISR. I have connected an Oscilloscope to the defined Pin but the amount of pulses sent out by the ISR varies widely.

I hope anybody can help me - thanks!

#define DHT_PIN 18
#define TRIGGER 20

void DHT11_data(float temp, float hum); // converts DHT11 data

hw_timer_t *Timer0 = NULL;
void IRAM_ATTR onTimer0(); // function prototype ISR

u_int8_t DHT11_value_array[83] = {0};

u_int32_t low_sig_time = 0;
u_int8_t high_sig_time = 0;

void setup()
{
  setCpuFrequencyMhz(240); // set CPU frequency (MHz)

  pinMode(DHT_PIN, INPUT_PULLUP);
  pinMode(TRIGGER, OUTPUT);

  Timer0 = timerBegin(0, 80, true);              // timerBegin(timer number, CLK divider, counter mode) 12,5 ns * 80 = 1 us
  timerAttachInterrupt(Timer0, &onTimer0, true); // timerAttachInterrupt(ISR-function, time in us (-> CLK divider), edge)
}

void loop()
{
  low_sig_time = pulseIn(DHT_PIN, LOW);   // should be ~ 18 ms = 18000 us
  high_sig_time = pulseIn(DHT_PIN, HIGH); // should be ~ 20 to 40 us

  if (low_sig_time > 17500 && low_sig_time < 18500 &&
      high_sig_time > 19 && high_sig_time < 41) // check if requested conditions are met
  {
    DHT11_data(25.1, 60.6);    // calculate data-array with given values -> temp 25,1°C; hum60,6%
  }
  digitalWrite(TRIGGER, HIGH); // Trigger signal for osci
  DHT11_data(25.1, 60.6);      // test signal - no secound ESP available
  delay(1);
  digitalWrite(TRIGGER, LOW);  // Trigger signal for osci
  delay(5);
}

void DHT11_data(float temp, float hum)
{
  u_int64_t DHT11_values = (u_int64_t)((u_int64_t)(hum * 100) << (3 * 8)); // shift humidity-data & concatenate
  DHT11_values |= (u_int32_t)(((u_int32_t)(temp * 100)) << 8);             // shift temperature-data & concatenate
  DHT11_values |= (u_int8_t)((u_int16_t)(hum + temp) & 0xFF);              // mask the last 8 bits of checksum & concatenate

  DHT11_value_array[0] = 80;  // start sequenc time (LOW)
  DHT11_value_array[1] = 80;  // start sequenc time (HIGH)

  DHT11_value_array[82] = 50; // stop sequenc time (LOW)

  for (u_int8_t i = 82; i > 1; i -= 2)
  {
    DHT11_value_array[i] = 50;

    if ((DHT11_values & (1 << ((i - 2) / 2))) >= 1)
    {
      DHT11_value_array[i + 1] = 70; // logic 1
    }
    else
    {
      DHT11_value_array[i + 1] = 27; // logic 0
    }
  }

  pinMode(DHT_PIN, OUTPUT);                            // change  DHT_PIN to OUTPUT
  digitalWrite(DHT_PIN, LOW);                          // initial level
  timerAlarmWrite(Timer0, DHT11_value_array[0], true); // set CC Register
  timerAlarmEnable(Timer0);                            // Enable Timer
}

void IRAM_ATTR onTimer0()
{
  static u_int8_t cnt = 1;

  if (cnt == 1)
  {
    digitalWrite(DHT_PIN, HIGH);
    timerAlarmWrite(Timer0, DHT11_value_array[cnt], true); // set CC Register
    digitalWrite(TRIGGER, LOW);
  }
  else if (cnt > 1 && cnt < 82)
  {
    digitalWrite(DHT_PIN, cnt % 2);
    timerAlarmWrite(Timer0, DHT11_value_array[cnt], true); // set CC Register
  }

  if (cnt >= 82) // reset sequenc
  {
    cnt = 0;
    timerAlarmDisable(Timer0); // Disable Timer
    pinMode(DHT_PIN, INPUT_PULLUP);
  }

  cnt++;
}

In the scope screenshots you can see that the amount of pulses varies.

Attempting to do I/O within an interrupt routine often fails, sometimes catastrophically.

There is no good reason to use interrupts for this purpose, so try another approach.

What do you mean? Why isn't it a good attention?

How would you implement it?

Interrupts are turned off within an interrupt service routine, so any activity that makes use of interrupts is blocked. Second, interrupts can corrupt global variables while they are being accessed by the main program. Third, function calls like digitalWrite() may not be thread safe, and likewise give unexpected results. Finally, overly long ISRs can delay vital processes that require other interrupts, leading to system crashes.

For something as simple as emulating a slow sensor like the DHT11, I would use delayMicroseconds() to time pulses in a function that generates the output stream.

If you insist on using a timer interrupt, have the interrupt simply set a flag that informs the state machine in loop() to take the next action.

I see your point but this sensor is one of many sensors which should be implemented in this code, so I try to keep the void loop() as clean as possible. Would it help to toggle the pin on a register level in order to prevent that the ISR takes too long?

For this implementation I do not want to use a delay-function due to the named reason above.

It's a good idea to implement the DHT without a time because I will need them at a later point anyway but how is it possible to implement this?

Not possible using your present interrupt approach, but it will be a good learning experience.

Did you overlook the second paragraph of post #4?

Thanks for your help! I thought about this problem really long, but I haven't found another solution....

What do you mean by "use delayMicroseconds() to ->time pulses<- in a function"? How could I use the delay-function without slowing down the rest of the code in the loop-section?

(And yes, I read the hole post #4)

Example of using delayMicroseconds() to generate a pulse.

digitalWrite(pin, HIGH);
delayMicroseconds(ON_TIME);
digitalWrite(pin,LOW);
delayMicroseconds(OFF_TIME);

Good luck with your project.

I know hot it generally works. My question was: how it works without slowing down the rest of the code in the main loop.

What "rest of code"? This is your stated goal:

I am trying to implement an emulator for an DHT11 (test purposes).

This is rather trivial, and does not require interrupts to implement.

Good luck with whatever else you plan to do.

That’s true - that’s the goal for this test-section. The code is part of a CI-Framework for automated tests...
I can't use delay for this implementation. -> any other recommendations?

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