2

This is the C code that I'm using to read the temperature and the humidity from the DHT22 sensor but I have some doubts about how this code works.

#define BCM2708_PERI_BASE        0x20000000
#define GPIO_BASE                (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define MAXTIMINGS 100

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <bcm2835.h>
#include <unistd.h>

int readDHT(int pin);

int main(int argc, char **argv){
  if(getuid() != 0){
    printf("You must be root to execute this program!\n");
    return -1;
  }
  if (!bcm2835_init()) return 1;

  if (argc != 2) {
    printf("usage: %s GPIOpin#\n", argv[0]);
    printf("example: %s 4 - Read from a DHT22 connected to GPIO #4\n", argv[0]);
    return -1;
  }

  int dhtpin = atoi(argv[1]);

  if (dhtpin <= 0){
    printf("Please select a valid GPIO pin #\n");
    return -1;
  }

  printf("Using pin #%d\n", dhtpin);
  readDHT(dhtpin);
  return 0;

}

int readDHT(int pin){
  int bits[250], data[100], bitidx = 0;
  int counter = 0;
  int laststate = HIGH;
  int j=0;

  // Set GPIO pin to output
  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);

  bcm2835_gpio_write(pin, HIGH);
  usleep(500000);  // 500 ms
  bcm2835_gpio_write(pin, LOW);
  usleep(20000);

  bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);

  data[0] = data[1] = data[2] = data[3] = data[4] = 0;

  // wait for pin to drop?
  while (bcm2835_gpio_lev(pin) == 1){
    usleep(1);
  }

  // read data!
  for (int i=0; i< MAXTIMINGS; i++){
    counter = 0;
    while (bcm2835_gpio_lev(pin) == laststate){
      counter++;
      usleep(1);        // overclocking might change this?
      if (counter == 1000) break;
    }
    laststate = bcm2835_gpio_lev(pin);
    if (counter == 1000) break;
    bits[bitidx++] = counter;

    // ignore first 3 transitions
    if ((i>3) && (i%2 == 0)){
      // shove each bit into the storage bytes
      data[j/8] <<= 1;
      if (counter > 200) data[j/8] |= 1;
      j++;
    }
  }

  /* 8b = 8 bits - RH = relative humidity */
  /* DATA = 8b of high RH data + 8b of low RH data + 8b of high temperature data + 8b of low temperature data + 8b CRC */
  printf("Data (%d): 0x%x 0x%x 0x%x 0x%x 0x%x\n", j, data[0], data[1], data[2], data[3], data[4]);

  // check we read 40 bits (8bit x 5) + verify checksum in the last byte
  if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))){
    float temp, hum;
    hum = data[0] * 256 + data[1];
    hum /= 10.0;
    temp = (data[2] & 0x7F)* 256 + data[3];
    temp /= 10.0;

    if (data[2] & 0x80){
      temp *= -1; //questa si avvera con valori di data[2] compresi fra 128 (0x80) e 255 (0xFF). Serve per temp negativa;
    }
    printf("Temp =  %.1f *C, Hum = %.1f \%\n", temp, hum);
    return 0;
  }
  else{
    printf("Data not good, skip\n");
    return -1;
  }
  return 0;
}
  1. What is MAXTIMINGS?
  2. BCM2708_PERI_BASE?
  3. I don't understand what this part of code does: data[j/8] <<= 1; if (counter > 200) data[j/8] |= 1;
  • This code works well when my Motion process (webcam) is not started. When Motion is running (It takes lower than 50% of my CPU). => how can you explain the fact that this code doesn't work anymore (no good data from DHT sensors) ? I have tried to start this code with nice -n -20 without any success Thanks – user8942 Aug 10 '13 at 8:05
4

First question

MAXTIMINGS means how many databits that the sensor sends is going to be read and stored (note that acording to the datasheet, it should send 40 bits in response).

In order to understand this, you have to first understand how DHT22 is sending it's data to the host computer. It uses one wire, where host sends start signal:

bcm2835_gpio_write(pin, HIGH);
usleep(500000);  // 500 ms
bcm2835_gpio_write(pin, LOW);
usleep(20000);

and then your host starts listening. Now the sensor will be transmitting it's data by changing the value from low to high to low and so on. If the bit to be transmitted is 1, the high state of the pin will be longer, if it's 0, it will be shorter.

So what your code is doing is to probe the value in a loop and watch if the signal level changes, counting number of loops that the signal did not change. This way you know for how long it was in the last known state (counter variable). This counter is then used to check if the bit that was send is 1 or 0 (check answer to 3rd question). If the counter value get's to 1000, however, the loop will brake and data reading will be finished.

This should happen before MAXTIMINGS bits where transmitted but this value is a safeguard if something went wrong.

Second question

BCM2708_PERI_BASE is a base address of memory mapped peripherals in BCM2708 family of chips. BCM2835 is part of this family and it is the SoC used in RaspberryPi. So this is an address of memory where hardware registers that are responsible for controlling peripherals (like GPIO) can be found. You can find this information in the datasheet (1.2.3 ARM physical addresses section). 0x200000 is an offset to a specific peripheral of your interest - the GPIO controller.

Third question

The code is quite simple but it is strangely written:

  • data[j/8] will point to the same byte for 8 consecutive values of j
  • in each loop, this byte will be shifted left by one bit
  • if counter is > 200, it first bit will be set to 1 (otherwise it will have value of 0)
  • after 8 such operations, the byte is complete and each bit is either 1 (if counter was >200) or 0

Note: I can't figure out why counter is compared to 200. Acoring to a datasheet, if high state is hold for 70us, it should be considered logical 1. Logical 0 should be transmitted for 26us-28us. The counter is updated each 1us so the threshold of 200 should never be meet.

  • many many thanks :D You were very kind and exhaustive! – polslinux Jun 10 '13 at 21:01
  • is this code replace a driver??? I mean I just have to run this code to get result without kernel module?? – Makhlouf GHARBI May 29 '14 at 10:04
  • 1
    @MAKOUDA: Yes, with this approach you don't need kernel driver. This should work from user space. – Krzysztof Adamski May 29 '14 at 13:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.