dotnet / iot

This repo includes .NET Core implementations for various IoT boards, chips, displays and PCBs.
MIT License
2.12k stars 574 forks source link

Dht11, Dht22, no result in RaspBerry Pi5 #2266

Closed Woselko closed 2 months ago

Woselko commented 5 months ago

Bug description I have the Raspberry Pi 5 version with 64bit Raspbian version . I'm trying to read temperature from with DHT11 sensor with this example: Link: https://github.com/dotnet/iot/tree/main/src/devices/Dhtxx Code:

using (Dht11 dht = new Dht11(26))
{
    Temperature temperature = default;
    RelativeHumidity humidity = default;
    bool success = dht.TryReadHumidity(out humidity) && dht.TryReadTemperature(out temperature);
    // You can only display temperature and humidity if the read is successful otherwise, this will raise an exception as
    // both temperature and humidity are NAN
    if (success)
    {
        Console.WriteLine($"Temperature: {temperature.DegreesCelsius:F1}\u00B0C, Relative humidity: {humidity.Percent:F1}%");

        // WeatherHelper supports more calculations, such as saturated vapor pressure, actual vapor pressure and absolute humidity.
        Console.WriteLine(
            $"Heat index: {WeatherHelper.CalculateHeatIndex(temperature, humidity).DegreesCelsius:F1}\u00B0C");
        Console.WriteLine(
            $"Dew point: {WeatherHelper.CalculateDewPoint(temperature, humidity).DegreesCelsius:F1}\u00B0C");
    }
    else
    {
        Console.WriteLine("Error reading DHT sensor");
    }
}

I have no succesfull read at all. I've tried to add something like that:

var gpio = new GpioController(PinNumberingScheme.Logical, new LibGpiodDriver(gpioChip: 4));
using (Dht11 dht11 = new Dht11(pin, PinNumberingScheme.Logical, gpio));

ALSO

var gpio = new GpioController(PinNumberingScheme.Logical, new LibGpiodDriver(gpioChip: 0));

But nothing helps me :(.

What is more I know that I have connected everything correctly because my own implementation for DHT11 works Link: https://github.com/Woselko/DotnetRaspBerryPi5Patterns/blob/main/DHT11SensorApp/Dht11Reader.cs Code (Working):

public class Dht11Reader
{
    private const int MaxUnchangeCount = 100;
    private const byte StateInitPullDown = 1;
    private const byte StateInitPullUp = 2;
    private const byte StateDataFirstPullDown = 3;
    private const byte StateDataPullUp = 4;
    private const byte StateDataPullDown = 5;
    private readonly int DhtPin;
    private GpioController _gpio;

    public Dht11Reader(int pinDht, GpioController gpio)
    {
        DhtPin = pinDht;
        _gpio = gpio;
        gpio.OpenPin(DhtPin);
    }

    public (int humidity, int temperature)? ReadDht11()
    {
        _gpio.SetPinMode(DhtPin, PinMode.Output);
        _gpio.Write(DhtPin, PinValue.High);
        Thread.Sleep(50);
        _gpio.Write(DhtPin, PinValue.Low);
        Thread.Sleep(20);
        _gpio.SetPinMode(DhtPin, PinMode.Input);

        int unchangedCount = 0;
        int last = -1;
        List<int> data = new List<int>();

        while (true)
        {
            int current = (int)_gpio.Read(DhtPin);
            data.Add(current);
            if (last != current)
            {
                unchangedCount = 0;
                last = current;
            }
            else
            {
                unchangedCount++;
                if (unchangedCount > MaxUnchangeCount)
                {
                    break;
                }
            }
        }

        byte state = StateInitPullDown;
        List<int> lengths = new List<int>();
        int currentLength = 0;

        foreach (int current in data)
        {
            currentLength++;

            if (state == StateInitPullDown)
            {
                if (current == 0)
                {
                    state = StateInitPullUp;
                }
                else
                {
                    continue;
                }
            }
            if (state == StateInitPullUp)
            {
                if (current == 1)
                {
                    state = StateDataFirstPullDown;
                }
                else
                {
                    continue;
                }
            }
            if (state == StateDataFirstPullDown)
            {
                if (current == 0)
                {
                    state = StateDataPullUp;
                }
                else
                {
                    continue;
                }
            }
            if (state == StateDataPullUp)
            {
                if (current == 1)
                {
                    currentLength = 0;
                    state = StateDataPullDown;
                }
                else
                {
                    continue;
                }
            }
            if (state == StateDataPullDown)
            {
                if (current == 0)
                {
                    lengths.Add(currentLength);
                    state = StateDataPullUp;
                }
                else
                {
                    continue;
                }
            }
        }

        if (lengths.Count != 40)
        {
            // Data not good, skip
            return null;
        }

        int shortestPullUp = lengths.Min();
        int longestPullUp = lengths.Max();
        int halfway = (longestPullUp + shortestPullUp) / 2;

        List<int> bits = new List<int>();
        List<byte> theBytes = new List<byte>();
        byte currentByte = 0;

        foreach (int length in lengths)
        {
            int bit = 0;
            if (length > halfway)
            {
                bit = 1;
            }
            bits.Add(bit);
        }

        for (int i = 0; i < bits.Count; i++)
        {
            currentByte <<= 1;
            if (bits[i] != 0)
            {
                currentByte |= 1;
            }
            else
            {
                currentByte |= 0;
            }
            if ((i + 1) % 8 == 0)
            {
                theBytes.Add(currentByte);
                currentByte = 0;
            }
        }

        byte checksum = (byte)((theBytes[0] + theBytes[1] + theBytes[2] + theBytes[3]) & 0xFF);

        if (theBytes[4] != checksum)
        {
            // Data not good, skip
            return null;
        }

        return (theBytes[0], theBytes[2]);
    }
}

This code was rewritten from another C/C++ example and it works 100%. Why is that? Do we need to implement everything from level zero? Is this somehow connected with my environment or dotnet version? I run this code with net7.0 and net8.0 results are the same.

Im sure that I have correct and working one wire DHT11 sensor. Also I was trying to run examples for other devices like DHT22, Bh1750fvi and many others.

If you need any additional information, I will provide it quickly

I will be grateful for any help from you. I will appreciate it very much.

Expected behavior bool success = dht.TryReadHumidity(out humidity) && dht.TryReadTemperature(out temperature); should return succes

Actual behavior bool success = dht.TryReadHumidity(out humidity) && dht.TryReadTemperature(out temperature); always return false

Versions used -woselko@raspberrypi:~ $ dotnet --version 8.0.100

pgrawehr commented 5 months ago

Hi @Woselko

Thanks for the report. This is likely a timing issue. The DHT read code uses a busy loop for reading the individual bits, much like your implementation. I'm assuming the maximum loopcount (here) is just too small for the RPI5, because that CPU is significantly faster than the RPI4.

I do like your implementation with the state machine. Maybe we should switch to that instead.

Woselko commented 5 months ago

Hi, thanks for your reply! Here is what I tried (just to test in the fastest way this _loopcount field):

        using (Dht11 dht11 = new Dht11(pin, PinNumberingScheme.Logical, gpio))
        {
            Type myClassType = typeof(DhtBase);
            FieldInfo privateIntField = myClassType.GetField("_loopCount", BindingFlags.NonPublic | BindingFlags.Instance);
            privateIntField.SetValue(dht11, (uint)20000);

            Dht(dht11);
        }

of course parameter was changed as on the screen image

But in won't help. I have tested from 10000 to 160000 by increasing every 5000

Is there anything more that I can check?

pgrawehr commented 5 months ago

I guess this needs some logging. It's probably the easiest if you check out the source and ad logs to the Read method to find out where it fails.

I don't have a Pi5 yet, so I cannot try.

Woselko commented 5 months ago

Ok, I will deliver it in several days. Thanks

IamRewt commented 3 months ago

Has there been any developments on this issue?

raffaeler commented 3 months ago

Hi @Woselko, As @pgrawehr wrote, the DHT11/DHT22 are very time sensitive devices and therefore they are not a great choice to verify whether the GPIOs are working correctly. If your goal is to validate the GPIO working correctly on the RPi5, I would strongly suggest to test non time-sensitive devices first.

Generally speaking, I would personally recommend to drive DHTxx devices using a microcontroller. On the RPi you can certainly use them, but you should expect a number of communication errors due to the timings (Linux is not a realtime OS, .NET code gets interrupted from the GC, etc.).

I kindly ask you to verify whether your issue depends on the GPIO or just depends on the DHTxx devices. Thank you

dotnet-policy-service[bot] commented 3 months ago

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.