Reading DHT11 Temperature and Humidity Sensor Data with C# in .NET 5

Development environment: VS2019 + .NET 5 Console Application.

To access Raspberry Pi GPIO data with C#, install the System.Device.Gpio package from NuGet. The DHT11 temperature and humidity sensor connects to the Raspberry Pi via GPIO pins, and its data protocol can be obtained with the sensor documentation. In .NET 5, DHT11 sensor data is retrieved through GpioController operations on GPIO pins.

The reference documentation for this implementation is based on GPIO controller encapsulation for convenient sensor data retrieval.

Additional resources include real-time indoor temperature and humidity monitoring with Raspberry Pi, which provides more insight into the device and sensor.

Reviewing Arduino demos for the DHT11 sensor can help understand basic GPIO operations.

Important note: In the C# code for reading sensor data, setting the pin to low must use Thread.Sleep, while setting it to high must use the WaitMicroseconds method. Swapping these operations or using asynchronous await Task.Delay will not correctly retrieve temperature and humidity data. However, await Task.Delay can be used for timed data reading. This discrepancy may be due to differences between C# GPIO pause mechanisms and OS-level pausing.

When publishing the software, since Raspberry Pi runs Linux (such as Raspberry Pi OS or Ubuntu), select "Portable" as the target runtime, or choose a specific runtime if needed.

1. TemperatureHumiditySensor.cs


/// <summary>
/// Temperature and humidity sensor class
/// </summary>
public class TemperatureHumiditySensor
{
    public Action<string> ErrorOccurred = (message) => { };
    public Action DataAvailable = (temperature, humidity) => { };

    public int ReadInterval
    {
        set => _readInterval = value < _minReadInterval ? _minReadInterval : value;
        get => _readInterval;
    }

    private readonly int _pin; // Sensor pin

    private readonly byte[] _sensorData = {0, 0, 0, 0, 0}; // Sensor data buffer

    private int _readInterval = _minReadInterval; // Data reading interval in ms
    private static readonly int _minReadInterval = 2000; // Minimum data reading interval in ms

    private readonly GpioController _gpioController;
    private readonly CancellationTokenSource _cancellationSource;

    public TemperatureHumiditySensor(int pin)
    {
        _pin = pin;
        _cancellationSource = new CancellationTokenSource();
        _gpioController = new GpioController(PinNumberingScheme.Board);
    }

    public Tuple Initialize()
    {
        try
        {
            if (_gpioController.IsPinOpen(_pin))
            {
                return new Tuple(false, $"Pin already open: {_pin}");
            }

            // Open the pin
            _gpioController.OpenPin(_pin);

            // Start the data reading task
            Task.Run(async () => { await ProcessReadingsAsync(_cancellationSource.Token); });

            return new Tuple(true, string.Empty);
        }
        catch (Exception ex)
        {
            return new Tuple(false, ex.Message);
        }
    }

    public void Shutdown()
    {
        _cancellationSource.Dispose();
    }

    private async Task ProcessReadingsAsync(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            try
            {
                await Task.Delay(ReadInterval, token);
                if (token.IsCancellationRequested)
                {
                    break;
                }

                Console.WriteLine($"{DateTime.Now: hh:mm:ss.fff} Reading data.");

                if (ReadSensorData())
                {
                    var humidity = _sensorData[0] + _sensorData[1] / 100f; // Humidity (%)
                    var temperature = _sensorData[2] + _sensorData[3] / 100f; // Temperature (°C)
                    DataAvailable?.Invoke(temperature, humidity);
                }
                else
                {
                    Console.WriteLine($"{DateTime.Now: hh:mm:ss.fff} No data received.");
                }
            }
            catch (Exception ex)
            {
                ErrorOccurred?.Invoke(ex.Message);
                break;
            }
        }

        _gpioController.ClosePin(_pin);
    }

    private bool ReadSensorData()
    {
        for (var i = 0; i < _sensorData.Length; i++)
        {
            _sensorData[i] = 0;
        }

        // Important: Must use Thread.Sleep(20) and WaitMicroseconds(40) first,
        // then use WaitMicroseconds(1) in the loop, otherwise timing won't work correctly.
        _gpioController.SetPinMode(_pin, PinMode.Output);
        _gpioController.Write(_pin, PinValue.Low);
        Thread.Sleep(20);
        _gpioController.Write(_pin, PinValue.High);
        WaitMicroseconds(40);
        _gpioController.SetPinMode(_pin, PinMode.Input);

        var previousState = PinValue.High;
        var bitCount = 0;

        for (var i = 0; i < 85; i++)
        {
            int counter = 0;
            while (_gpioController.Read(_pin) == previousState)
            {
                counter++;
                WaitMicroseconds(1);
                if (counter == 255)
                {
                    break;
                }
            }

            previousState = _gpioController.Read(_pin);
            if (counter == 255)
            {
                break;
            }

            if ((i >= 4) && (i % 2 == 0))
            {
                _sensorData[bitCount / 8] <<= 1;
                if (counter > 16)
                {
                    _sensorData[bitCount / 8] |= 1;
                }

                bitCount++;
            }
        }

        return bitCount >= 40 &&
               _sensorData[4] == ((_sensorData[0] + _sensorData[1] + _sensorData[2] + _sensorData[3]) & 0xFF);
    }

    private static void WaitMicroseconds(int microseconds)
    {
        var endTime = DateTime.UtcNow.Ticks + microseconds * 10;
        while (DateTime.UtcNow.Ticks < endTime)
        {
            // Busy wait
        }
    }
}

2. Main Program


static void Main(string[] args)
{
    try
    {
        var sensor = new TemperatureHumiditySensor(7) {ReadInterval = 10000};
        sensor.ErrorOccurred += (message) => { Console.WriteLine($"{DateTime.Now: hh:mm:ss.fff} Error: {message}."); };
        sensor.DataAvailable += (temperature, humidity) =>
        {
            Console.WriteLine(
                $"{DateTime.Now: hh:mm:ss.fff} Reading: Temperature - {temperature}°C, Humidity - {humidity}%.");
        };
        sensor.Initialize();

        Console.ReadKey();

        sensor.Shutdown();
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Main error: {ex.Message}");
    }

    Console.WriteLine("Program exiting");
    Console.ReadKey();
}

Tags: C# .NET 5 Raspberry Pi DHT11 gpio

Posted on Thu, 14 May 2026 10:51:56 +0000 by cobaltblue