Article Index

Extracting The Data

Now we have the data in the buffer as zeros and ones. All that remains is to decode it into temperature and humidity readings. But first we will convert the bits into five bytes of data. The simplest way of doing this is to write a function that will pack eight bits into an uint:

uint8_t getByte(int b,int buf[]){
 int i;
 uint8_t result=0;
 b=(b-1)*8+1;
 for(i=b;i<=b+7;i++){
  result= result<<1;
  result=result | buf[i];
 }
 return result;
}

The b parameter can be set to the byte that you want to extract from the array. For example, if b=2 then the for loop runs from i=9 to i=16 i.e. the second byte stored in the array.  

Notice that we skip the first bit because this just signals the start of the data. 

The bit manipulation in the for loop is a fairly standard shift left and or the least significant bit into the result. 

Using this function getting the five bytes is trivial:

    int byte1 = getByte(1, buf);
    int byte2 = getByte(2, buf);
    int byte3 = getByte(3, buf);
    int byte4 = getByte(4, buf);
    int byte5 = getByte(5, buf);

The first two bytes are the humidity measurement, the second two the temperature and the final byte is the checksum.  

The reason for the cast to int rather than keeping the data as bytes is that it makes computing the checksum easier.

The checksum is just the sum of the first four bytes reduced to eight bits and we can test it using:

printf("Checksum %hho %d \n",byte5,(byte1+byte2+byte3+byte4) & 0xFF);

If the two values are different there has been a transmission error.  The addition of the bytes is done as a full integer and then it is reduced back to a single byte by the and operation. 

If there is a checksum error. the simplest thing to do is get another reading from the device. However notice that you shouldn't read the device more than once every 2 seconds. 

The humidity and temperature data are also easy to reconstruct as they are transmitted high byte first and times 10 the actual value.

The humidity data is easy:

float humidity= (float) (byte1<<8 |byte2)/10.0;
printf("Humidity= %f \n",humidity);

The temperature data is slightly more difficult in that the top most bit is used to indicate a negative temperature. This means we have to test for the most significant bit and flip the sign of the temperature if it is set:

float temperature;
int neg=byte3 & 0x80;
byte3=byte3 & 0x7F;
temperature= (float) (byte3<<8 |byte4)/10.0;
if(neg>0)temperature=-temperature;
printf("Temperature= %f \n",temperature);

This complete the data processing.

A main program to call the measuring function is just:

int main(int argc, char** argv) {
 const struct sched_param priority = {5};
 sched_setscheduler(0, SCHED_FIFO, &priority);
 mlockall(MCL_CURRENT | MCL_FUTURE);
 if (!bcm2835_init())
     return 1;
 bcm2835_gpio_fsel(RPI_GPIO_P1_07, BCM2835_GPIO_FSEL_INPT);
 bcm2835_delayMicroseconds(1000);

 GetDHT22data(RPI_GPIO_P1_07);
 return 0;
}

You can, of course modify the GetDHT22data to not print any results and to return the temperature and humidity. You need to add a test on the checksum to take the measurement again if there is an error but you need to keep in mind the 2 second maximum reading rate. Using a static variable you could even slow down the rate that the GetDHT22 function was used to more than 2 seconds per call.

The point is that we have a simple and reliable program that reads the DHT22 without the need for drivers or a third party library. Decoding protocols of this sort is easy and makes your program self contained.