Article Index

The Electronics

Exactly how you build the circuit is a matter of preference. The basic layout can be seen below.

.

It is very easy to create this circuit using a prototyping board and some jumper wires. You can also put the resistor close to the DHT22 to make a sensor package connected to the Pi using three cables.

The Software

With the hardware shown above connected to the Pi the first thing that we need to do is establish that the system is working - just a little.

The simplest way to do this is to pull the line down for 1ms and see if the device responds with a stream of pulses. These can be seen on a logic analyzer or an oscilloscope - both are indispensable tools. 

If you don't have access to either tool then you will just have to skip to the next stage and see if you can read in some data. 

The simplest program that will do the job is: 

void GetDHT22data(uint8_t pin) {
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
    bcm2835_gpio_write(pin, LOW);
    bcm2835_delayMicroseconds(1000);
    bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
    return;
}

Setting the line initially high, to ensure that it is configured as an output, we then set it low, wait for around 1000 microseconds and then change its direction to input by reading the data.

There is no need to set the lines pull up mode because it is the only device driving the line until it releases the line by changing direction to input. When a line is in input mode it is high impedance and this is why we need an external pull up resistor in the circuit. 

As long as the circuit has been correctly assembled and you have a working device you should see something like:

 

 

Reading The Data

With preliminary flight checks complete it is time to read the 40-bit data stream.

The first thing to do is wait for the low that the device sends before the start bit:

 int i;
 for (i = 1; i < 2000; i++) {
   if (bcm2835_gpio_lev(pin)==0)break;            
 };

Next we can start to read in the bit stream.

When doing this there are two things to keep in mind.

The first is that it is only the time the line is high that matters and you need to measure just this accurately - you don't care so much about how long the line is low for.

The second is that it is usually better to collect the bits and only later process them and extract the data.

To this end it is usually a good idea to save the data in a buffer. 

uint64_tbuf[41];
int j;
 for(j=0;j<41;j++){
  for(i=1;i<2000;i++){
   if(bcm2835_gpio_lev(pin)==1)break;
  };
  t=bcm2835_st_read();
  for(i=1;i<2000;i++){
   if(bcm2835_gpio_lev(pin)==0) break;
  }
  buf[j]=(bcm2835_st_read()-t);  
}

 

You should be able to see how this works.

The outer for loop, indexed on j,  repeats to read in all 41 bits, 40 data bits and the initial start bit.

The first inner loop waits for the line to go high. The final loop count gives us a measure of how long the line has been low. This is of no interest because the device keeps the line low for the same length of time for a zero or a one.

Next the second for loop waits for the line to go low. The final count i is now proportional to the time the line was high. For machines that don't have a microsecond clock this would be enough to decode the data. In this case we can use the low order 32 bits of the Pi's clock as returned by cm2835_st_read(). We just need the difference between the time the clock when high and returned to low to work out if the data is a one or a zero.

If you add 

for(j=0;j<=40;j++){
 printf("%d %d \n",j,buf[j]);
}

to the end of the program you will be able to see the counts and you should quickly be able to work out the value half way between the long one pulses and the short zero pulses.

Examining the data reveals that  short pulses returned 26 to 27 microseconds  and long returned 73 to 74. Thus the threshold half way between the two is approximately 50.

With a threshold of 50  we can classify the pulses into long and short and store one and zero in the buffer. 

int buf[41];
int j;
for(j=0;j<41;j++){
 for(i=1;i<2000;i++){
  if(bcm2835_gpio_lev(pin)==1)break;
 };
 t=bcm2835_st_read();
 for(i=1;i<2000;i++){
  if(bcm2835_gpio_lev(pin)==0) break;
 }
 buf[j]=(bcm2835_st_read()-t)>50;
}

You can afford to include this extra processing in the data collection loop because it happens while the line is low and we aren't interested in measuring this time accurately.  If we were then it would be a good idea to put all processing off until the loop had finished. Notice that now we can use an integer buffer because we aren't storing the 64 bit time in it just the result of the conditional expression which is zero or one. 

There is a small bug in this program as it stands but probably one that we can live with. The time difference will be wrong if the clock rolls over to zero. You can fix this by doing the arithmetic mod the size of the clock or you can simply put up with the fact that you will get a checksum error - very rarely.