0

I'm trying to read temperature form DHT22 using Raspbian patched with xenomai real time system. I'm following steps of datasheet page 4 using gpio4. . the circuit is correctly wired(I can read with existing codes). but I get false result (temperature about 80000).

my question is, since xenomai is hard real time, did it's sleep function take really the given amount of time???.

the max latency is about 30us (tested in parallel with dd command which take 80% of processor. (/usr/xenomai/bin/latency program)

or did the datasheet not specify exactly the communication (signal) steps???

#define TASK_PRIO  99 /* Highest RT priority */
#define TASK_MODE  0  /* No flags */
#define TASK_STKSZ 0  /* Stack size (use default one) */
#define SIGNALS (0x1|0x4) /* Signals to send */

...

   void read(void *cookie) {
         readDHT();
   }

... main

 int err;
        mlockall(MCL_CURRENT | MCL_FUTURE);

    err = rt_task_create(&task_desc, "MakoudaTask",
    TASK_STKSZ,
    TASK_PRIO,
    TASK_MODE);
    if (!err)
        rt_task_start(&task_desc, &read, NULL);

    if (err) {
        printf(" cant reate task\n");
        return 0;
    }

readDHT22.C

uint64_t readDHT(int pin) {
  // Set GPIO pin to output
      GPIO_DIR_OUTPUT(pin);     // set pin output
      GPIO_CLEAR_PIN(pin);      // Set low
      rt_task_sleep(5000000);                // DHT22 needs min 1-10 ms to signal a startup
      GPIO_SET_PIN(pin);        // Take pin high
      rt_task_sleep(40000);  //sleep 40 us


  /*------------------sensor response --------------------*/

      GPIO_DIR_INPUT(pin);      // set pin input
      rt_task_sleep(80000); //sensor pull low 80 us
      rt_task_sleep(100000); // sensor pull up 80 us
    //  while(GPIO_READ_PIN(pin)); // wait till sensor pull down (ready to transmit data)


   /*--------------------- begin transmit data ------------*/

  uint64_t measure = 0;   // store the 40 bits
  uint8_t i;


      // start to read 40 bits
      for (i = 0; i < 40; i++) {
          while(GPIO_READ_PIN(pin) == 0) // wait till sensor pull up (ready to transmit bit)
              rt_task_sleep(1);  //sleep 1 ns

          rt_task_sleep(40000); //wait 40us if still high then bit = 1 else bit = 0 because output=1 still 70 us
          if (GPIO_READ_PIN(pin))
          {
              measure = measure | (1 << i);
              while(GPIO_READ_PIN(pin)) //wait sensor pull down
                  rt_task_sleep(1000);
          }
      }

  return measure;
}
  • The program code has been updated. Does your question still stand? Can you now read the DHT22 successfully with the edited code as above? – joan Sep 29 '14 at 17:11
  • no it give me false results (temperature 8000 for example) but behavior changed from when just using native sleep. so now my question is does rt_task_sleep really sleep exactly this amount of time (more accurate then native sleep??) since I followed the signal steps in datasheet and got false results and I read that xenomai is hard real time – Makhlouf GHARBI Sep 30 '14 at 12:48
  • I'm not familiar with Xenomai. I doubt it makes rt_task_sleep much more accurate than usleep. In fact your experience seems to suggest there is no difference in your case. Since you don't seem to want a method of reliably reading the DHT22 I'll post one which works reasonably well but not 100% of the time. – joan Sep 30 '14 at 13:04
1

Reading the DHT22 sensor is pretty tough from userland. You have made it even tougher by using usleep to perform timing. Most userland attempts use busy waits to accumulate the bits of the DHT22 message.

enter image description here

The 40 bits message takes about 4ms to complete. A 0 bit is roughly 25 us long, a 1 bit is roughly 75 us long. If any one of the usleeps cause a reschedule the message will be read incorrectly.

I'd dump the usleeps and use busy waits.

Even if you get it to work you'll find that circa 50% of the reads are unsuccessful. Fortunately, for temperature/humidity readings, you can probably live with that error rate.

There are a couple of ways to get low error rates. One technique uses SPI to read the bits. The other uses my pigpio library. There is a Python example here.

Another method of reading the DHT22 which works quite well but isn't 100% reliable.

/*
dht22.c
2014-09-20
Public Domain

This program reads the relative humidity and temperature
from a DHT22 sensor.

gcc -o dht22 dht22.c
sudo ./dht22 [gpio [tries]]

The gpio defaults to 22 and tries to 5.
*/

#define DHT22 22
#define TRIES 5

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#define GPIO_BASE  0x20200000
#define SYST_BASE  0x20003000

#define GPIO_LEN  0xB4
#define SYST_LEN  0x1C

#define GPSET0 7
#define GPSET1 8

#define GPCLR0 10
#define GPCLR1 11

#define GPLEV0 13
#define GPLEV1 14

#define SYST_CS  0
#define SYST_CLO 1
#define SYST_CHI 2

static volatile uint32_t  *gpioReg = MAP_FAILED;
static volatile uint32_t  *systReg = MAP_FAILED;

#define PI_BANK (gpio>>5)
#define PI_BIT  (1<<(gpio&0x1F))

#define PI_OK      0
#define PI_FAILURE 1

/* gpio levels */

#define PI_LOW  0
#define PI_HIGH 1

/* gpio modes. */

#define PI_INPUT  0
#define PI_OUTPUT 1
#define PI_ALT0   4
#define PI_ALT1   5
#define PI_ALT2   6
#define PI_ALT3   7
#define PI_ALT4   3
#define PI_ALT5   2

void gpioSetMode(unsigned gpio, unsigned mode)
{
   int reg, shift;

   reg   =  gpio/10;
   shift = (gpio%10) * 3;

   gpioReg[reg] = (gpioReg[reg] & ~(7<<shift)) | (mode<<shift);
}

int gpioRead(unsigned gpio)
{
   if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1;
   else                                               return 0;
}

void gpioWrite(unsigned gpio, unsigned level)
{
   if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT;
   else            *(gpioReg + GPSET0 + PI_BANK) = PI_BIT;
}

/* Returns the number of microseconds after system boot. Wraps around
   after 1 hour 11 minutes 35 seconds.
*/

uint32_t gpioTick(void) { return systReg[SYST_CLO]; }

/* Map in registers. */

static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len)
{
    return (uint32_t *) mmap(0, len,
       PROT_READ|PROT_WRITE|PROT_EXEC,
       MAP_SHARED|MAP_LOCKED,
       fd, addr);
}

int gpioInitialise(void)
{
   int fd;

   fd = open("/dev/mem", O_RDWR | O_SYNC) ;

   if (fd < 0)
   {
      fprintf(stderr,
         "This program needs root privileges.  Try using sudo\n");
      return -1;
   }

   gpioReg  = initMapMem(fd, GPIO_BASE, GPIO_LEN);
   systReg  = initMapMem(fd, SYST_BASE, SYST_LEN);

   close(fd);

   if ((gpioReg == MAP_FAILED) || (systReg == MAP_FAILED))
   {
      fprintf(stderr, "Bad, mmap failed\n");
      return -1;
   }
   return 0;
}

#define MAXEDGES 85

static int read_DHT22(int gpio)
{
   float temp, rhum;
   int lastLevel = PI_HIGH;
   int bit = 0, byte = 0, i;
   uint32_t tick, edgeLen;
   uint8_t msg[5], checksum;

   /* Pull gpio down for 10 milliseconds. */

   gpioWrite(gpio, PI_LOW);
   gpioSetMode(gpio, PI_OUTPUT);
   usleep(10000);

   /* Force high. */
   gpioWrite(gpio, PI_HIGH);

   // Float high. */
   gpioSetMode(gpio, PI_INPUT);

   for (i=0; i<MAXEDGES; i++)
   {
      tick = gpioTick();

      while (gpioRead(gpio) == lastLevel)
      {
         edgeLen = gpioTick() - tick;
         if (edgeLen > 150) break;
      }

      if (edgeLen > 150) break;

      if (lastLevel) lastLevel = 0; else lastLevel = 1;

      /* Ignore first 3 edges. */
      if ((i >= 4) && (i%2 == 0))
      {
         msg[byte] <<= 1;

         if (edgeLen > 50) msg[byte] |= 1;

         if (++bit > 7)
         {
            bit = 0;
            ++byte;
         }
      }
   }

   checksum = msg[0] + msg[1] + msg[2] + msg[3];

   if ((byte >= 5) && (msg[4] == checksum))
   {
      rhum = (float) ((msg[0]<<8) + msg[1]);
      rhum /= 10.0;

      temp = (float) (((msg[2] & 0x7F) << 8) + msg[3]);
      temp /= 10.0;
      if ((msg[2] & 0x80))  temp *= -1.0;

      printf("%.1f %.1f\n", rhum, temp);
      return 1;
   }
   return 0;
}

int main (int argc, char *argv[])
{
   int DHT22_gpio, tries;
   int i;

   if (argc > 1) DHT22_gpio = atoi(argv[1]);
   else          DHT22_gpio = DHT22;

   if (argc > 2) tries = atoi(argv[2]);
   else          tries = TRIES;

   if (argc > 3)
   {
      fprintf(stderr, "Too many arguments.\n");
      return PI_FAILURE;
   }

   if (gpioInitialise() < 0) return PI_FAILURE;

   setuid(getuid()); /* Drop root privileges. */

   i = 0;

   while (1)
   {
      if (read_DHT22(DHT22_gpio)) break;

      if (i >= tries)
      {
         printf("999.9 999.9\n");
         break;
      }

      ++i;
      usleep(2000000); /* Try again in 2 seconds. */
   }
   return 0;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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