adafruit / Adafruit-Raspberry-Pi-Python-Code

Adafruit library code for Raspberry Pi
1.43k stars 686 forks source link

Solution for long cable on DHT22/11 #161

Closed internermitarbeiter closed 5 years ago

internermitarbeiter commented 6 years ago

Hello, I recognized, that if I use a cable longer than 3m the Adafriut_DHT can't communicate with the sensor.

The reason is in the pi_2_dht_read function.

Receive sequence long cable: 2 219 724 754 483 236 513 226 513 236 505 236 513 219 513 236 505 682 505 672 631 236 513 218 513 236 478 682 503 680 505 235 511 674 513 664 632 236 512 236 505 236 505 225 513 234 484 236 513 226 513 227 631 682 505 235 513 674 513 228 512 228 505 236 513 214 513 643 612 672 513 236 505 682 505 682 504 681 505 682 504 682 495 670 419 0 0 0 0 0 0 0 Receive sequence short cable: 723 763 503 236 500 245 501 245 503 244 502 244 503 234 503 688 503 668 629 245 486 245 501 690 501 244 503 245 501 242 503 688 503 678 628 244 503 244 490 245 496 235 503 245 501 244 503 243 503 235 627 245 503 688 502 688 501 244 503 678 503 667 503 688 502 236 599 688 503 244 501 245 503 688 500 245 501 689 491 244 493 229 437 0 0 0 0 0 0 0 0 0

You see, that the beginning is a bit noisy (Offset +2). I think it's better to search from the end. I modify the "pi_2_dht_read(…)" and it works fine now.

By.

StaticDet5 commented 6 years ago

That is brilliant!! So simple, and I missed it. I've been thinking about this for years, and you're absolutely right, I had a cable much longer than 3 meters!

Nicely done!

On Fri, Oct 19, 2018 at 3:19 PM internermitarbeiter < notifications@github.com> wrote:

Hello, I recognized, that if I use a cable longer than 3m the Adafriut_DHT can't communicate with the sensor.

The reason is in the pi_2_dht_read function.

Receive sequence long cable: 2 219 724 754 483 236 513 226 513 236 505 236 513 219 513 236 505 682 505 672 631 236 513 218 513 236 478 682 503 680 505 235 511 674 513 664 632 236 512 236 505 236 505 225 513 234 484 236 513 226 513 227 631 682 505 235 513 674 513 228 512 228 505 236 513 214 513 643 612 672 513 236 505 682 505 682 504 681 505 682 504 682 495 670 419 0 0 0 0 0 0 0 Receive sequence short cable: 723 763 503 236 500 245 501 245 503 244 502 244 503 234 503 688 503 668 629 245 486 245 501 690 501 244 503 245 501 242 503 688 503 678 628 244 503 244 490 245 496 235 503 245 501 244 503 243 503 235 627 245 503 688 502 688 501 244 503 678 503 667 503 688 502 236 599 688 503 244 501 245 503 688 500 245 501 689 491 244 493 229 437 0 0 0 0 0 0 0 0 0

You see, that the beginning is a bit noisy (Offset +2). I think it's better to search from the end. I modify the "pi_2_dht_read(…)" and it works fine now.

By.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/issues/161, or mute the thread https://github.com/notifications/unsubscribe-auth/AAllZBiOmA8EYdyqOBFC6OJsqIquJwJxks5umiW5gaJpZM4XxRwg .

gormleymark commented 5 years ago

@internermitarbeiter How did you modify the code?

internermitarbeiter commented 5 years ago

You have to modify the pi_2_dht_read.c file and then compile it again.

The modified code: (not optimized, but works since ocotober without errors)

int pi_2_dht_read(int type, int pin, float humidity, float temperature) { // Validate humidity and temperature arguments and set them to zero. if (humidity == NULL || temperature == NULL) { return DHT_ERROR_ARGUMENT; } temperature = 0.0f; humidity = 0.0f;

// Initialize GPIO library. if (pi_2_mmio_init() < 0) { return DHT_ERROR_GPIO; }

// Store the count that each DHT bit pulse is low and high. // Make sure array is initialized to start at zero. int pulseCounts[DHT_PULSES*2+10] = {0};

// Set pin to output. pi_2_mmio_set_output(pin);

// Bump up process priority and change scheduler to try to try to make process more 'real time'. set_max_priority();

// Set pin high for ~500 milliseconds. pi_2_mmio_set_high(pin); sleep_milliseconds(500);

// The next calls are timing critical and care should be taken // to ensure no unnecssary work is done below.

// Set pin low for ~20 milliseconds. pi_2_mmio_set_low(pin); busy_wait_milliseconds(20);

// Set pin at input. pi_2_mmio_set_input(pin); // Need a very short delay before reading pins or else value is sometimes still low. for (volatile int i = 0; i < 50; ++i) { }

// Wait for DHT to pull pin low. uint32_t count = 0; while (pi_2_mmio_input(pin)) { if (++count >= DHT_MAXCOUNT) { // Timeout waiting for response. set_default_priority(); printf("Timeout in Protocol Pin not low.\n"); return DHT_ERROR_TIMEOUT; } }

// Record pulse widths for the expected result bits. for (int i=0; i < DHT_PULSES*2+10; i+=2) { // Count how long pin is low and store in pulseCounts[i] while (!pi_2_mmio_input(pin)) { if (++pulseCounts[i] >= DHT_MAXCOUNT) {
break; } } // Count how long pin is high and store in pulseCounts[i+1] while (pi_2_mmio_input(pin)) { if (++pulseCounts[i+1] >= DHT_MAXCOUNT) {
pulseCounts[i+1] = 0; break; } } }

// Drop back to normal priority.

set_default_priority();

printf("\nSequence: \n"); for (int i=0; i < DHT_PULSES*2+10; i++) {
printf("%i ",pulseCounts[i]); }

// normalize the data for (int i=DHT_PULSES2+10-1; i > 0; i--) { if (pulseCounts[i] > 0) { int temp[DHT_PULSES2+10] = {0};

     memcpy(temp, pulseCounts, (DHT_PULSES*2+10)*sizeof(int));
     memset(pulseCounts, 0, (DHT_PULSES*2+10)*sizeof(int));

     for (int j=DHT_PULSES*2; j>=0; j--)
     {
        pulseCounts[j] =  temp[i]   ;
        i--; 
     }
     break; 
 }   

}

printf("\Identify 28us or 70us\n"); for (int i=3; i < DHT_PULSES*2; i+=2) {
printf("%-4i ",pulseCounts[i]); }

printf("\Identify the pause 50us\n"); for (int i=2; i < DHT_PULSES*2; i+=2) {
printf("%-4i ",pulseCounts[i]); }

printf("\n");

// Compute the average low pulse width to use as a 50 microsecond reference threshold. // Ignore the first two readings because they are a constant 80 microsecond pulse. uint32_t threshold = 0; for (int i=2; i < DHT_PULSES*2; i+=2) { threshold += pulseCounts[i]; } threshold /= DHT_PULSES-1; printf("Threshold:%i\n",threshold);

// Interpret each high pulse as a 0 or 1 by comparing it to the 50us reference. // If the count is less than 50us it must be a ~28us 0 pulse, and if it's higher // then it must be a ~70us 1 pulse. uint8_t data[5] = {0}; for (int i=3; i < DHT_PULSES*2; i+=2) { int index = (i-3)/16; data[index] <<= 1; if (pulseCounts[i] >= threshold) { // One bit for long pulse. data[index] |= 1; } // Else zero bit for short pulse. }

// Useful debug info: // printf("Data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3], data[4]);

// Verify checksum of received data. if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { if (type == DHT11) { // Get humidity and temp for DHT11 sensor. humidity = (float)data[0]; temperature = (float)data[2]; } else if (type == DHT22) { // Calculate humidity and temp for DHT22 sensor. humidity = (data[0] 256 + data[1]) / 10.0f; temperature = ((data[2] & 0x7F) 256 + data[3]) / 10.0f; if (data[2] & 0x80) { temperature = -1.0f; } } return DHT_SUCCESS; } else { printf("Checksummen Fehler !\n") ;
return DHT_ERROR_CHECKSUM; } }

etatus commented 5 years ago

Wow, thank you very much @internermitarbeiter!! It worked like a charm for me!

The code you shared had some typos (PULSES2 instead PULSES*2 in some lines).

Here is the fixed code for pi_2_dht_read.c (I commented your printf debug lines to get a clean output):

// Copyright (c) 2014 Adafruit Industries
// Author: Tony DiCola

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include <stdbool.h>
#include <stdlib.h>

#include "pi_2_dht_read.h"
#include "pi_2_mmio.h"

// This is the only processor specific magic value, the maximum amount of time to
// spin in a loop before bailing out and considering the read a timeout.  This should
// be a high value, but if you're running on a much faster platform than a Raspberry
// Pi or Beaglebone Black then it might need to be increased.
#define DHT_MAXCOUNT 32000

// Number of bit pulses to expect from the DHT.  Note that this is 41 because
// the first pulse is a constant 50 microsecond pulse, with 40 pulses to represent
// the data afterwards.
#define DHT_PULSES 41

int pi_2_dht_read(int type, int pin, float* humidity, float* temperature) {
// Validate humidity and temperature arguments and set them to zero.
    if (humidity == NULL || temperature == NULL) {
        return DHT_ERROR_ARGUMENT;
    }
    *temperature = 0.0f;
    *humidity = 0.0f;

// Initialize GPIO library.
    if (pi_2_mmio_init() < 0) {
        return DHT_ERROR_GPIO;
    }

// Store the count that each DHT bit pulse is low and high.
// Make sure array is initialized to start at zero.
    int pulseCounts[DHT_PULSES*2+10] = {0};

// Set pin to output.
    pi_2_mmio_set_output(pin);

// Bump up process priority and change scheduler to try to try to make process more 'real time'.
    set_max_priority();

// Set pin high for ~500 milliseconds.
    pi_2_mmio_set_high(pin);
    sleep_milliseconds(500);

// The next calls are timing critical and care should be taken
// to ensure no unnecssary work is done below.

// Set pin low for ~20 milliseconds.
    pi_2_mmio_set_low(pin);
    busy_wait_milliseconds(20);

// Set pin at input.
    pi_2_mmio_set_input(pin);
// Need a very short delay before reading pins or else value is sometimes still low.
    for (volatile int i = 0; i < 50; ++i) {
    }

// Wait for DHT to pull pin low.
    uint32_t count = 0;
    while (pi_2_mmio_input(pin))
    {
        if (++count >= DHT_MAXCOUNT)
        {
// Timeout waiting for response.
            set_default_priority();
            //printf("Timeout in Protocol Pin not low.\n");
            return DHT_ERROR_TIMEOUT;
        }
    }

// Record pulse widths for the expected result bits.
    for (int i=0; i < DHT_PULSES*2+10; i+=2)
    {
// Count how long pin is low and store in pulseCounts[i]
        while (!pi_2_mmio_input(pin))
        {
            if (++pulseCounts[i] >= DHT_MAXCOUNT)
            {
                break;
            }
        }
// Count how long pin is high and store in pulseCounts[i+1]
        while (pi_2_mmio_input(pin))
        {
            if (++pulseCounts[i+1] >= DHT_MAXCOUNT)
            {
                pulseCounts[i+1] = 0;
                break;
            }
        }
    }

// Drop back to normal priority.
    set_default_priority();
    /*
    printf("\nSequence: \n");
    for (int i=0; i < DHT_PULSES*2+10; i++)
    {
        printf("%i ",pulseCounts[i]);
    }
    */
// normalize the data
    for (int i=DHT_PULSES*2+10-1; i > 0; i--)
    {
        if (pulseCounts[i] > 0)
        {
            int temp[DHT_PULSES*2+10] = {0};

            memcpy(temp, pulseCounts, (DHT_PULSES*2+10)*sizeof(int));
            memset(pulseCounts, 0, (DHT_PULSES*2+10)*sizeof(int));

            for (int j=DHT_PULSES*2; j>=0; j--)
            {
                pulseCounts[j] =  temp[i]   ;
                i--;
            }
            break;
        }
    }
    /*
    printf("\Identify 28us or 70us\n");
    for (int i=3; i < DHT_PULSES*2; i+=2)
    {
        printf("%-4i ",pulseCounts[i]);
    }

    printf("\Identify the pause 50us\n");
    for (int i=2; i < DHT_PULSES*2; i+=2)
    {
        printf("%-4i ",pulseCounts[i]);
    }

    printf("\n");
    */
// Compute the average low pulse width to use as a 50 microsecond reference threshold.
// Ignore the first two readings because they are a constant 80 microsecond pulse.
    uint32_t threshold = 0;
    for (int i=2; i < DHT_PULSES*2; i+=2) {
        threshold += pulseCounts[i];
    }
    threshold /= DHT_PULSES-1;
    //printf("Threshold:%i\n",threshold);

// Interpret each high pulse as a 0 or 1 by comparing it to the 50us reference.
// If the count is less than 50us it must be a ~28us 0 pulse, and if it's higher
// then it must be a ~70us 1 pulse.
    uint8_t data[5] = {0};
    for (int i=3; i < DHT_PULSES*2; i+=2) {
        int index = (i-3)/16;
        data[index] <<= 1;
        if (pulseCounts[i] >= threshold) {
// One bit for long pulse.
            data[index] |= 1;
        }
// Else zero bit for short pulse.
    }

// Useful debug info:
// printf("Data: 0x%x 0x%x 0x%x 0x%x 0x%x\n", data[0], data[1], data[2], data[3], data[4]);

// Verify checksum of received data.
    if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
        if (type == DHT11) {
// Get humidity and temp for DHT11 sensor.
            *humidity = (float)data[0];
            *temperature = (float)data[2];
        }
        else if (type == DHT22) {
// Calculate humidity and temp for DHT22 sensor.
            *humidity = (data[0] * 256 + data[1]) / 10.0f;
            *temperature = ((data[2] & 0x7F) * 256 + data[3]) / 10.0f;
            if (data[2] & 0x80) {
                *temperature *= -1.0f;
            }
        }
        return DHT_SUCCESS;
    }
    else {
        //printf("Checksummen Fehler !\n") ;
        return DHT_ERROR_CHECKSUM;
    }
}

For pi_dht_read.c is the same code, but replacing "pi2" by "pi_" in all ocurrences in the code.

dmetcalfe92 commented 5 years ago

Can confirm this has fixed my problems with a lengthy DHT11 wire on RPI 3. Thankyou internermitarbeiter, and thankyou etanus for providing the complete code!

ladyada commented 5 years ago

Thank you for the Issue! This library has been deprecated in favor of our python3 Blinka library. We have replaced all of the libraries that use this repo with CircuitPython libraries that are Python3 compatible, and support a wide variety of single board/linux computers!

Visit https://circuitpython.org/blinka for more information

CircuitPython has support for almost 200 different drivers, and a as well as FT232H support for Mac/Win/Linux!