使用C#在.Net5中获取DTH11温湿度传感器数据

本文开发环境为:VS2019 + .Net5 Console App。

使用C#获取树莓派GPIO数据需要安装System.Device.Gpio包,在NuGet中搜索即可找到;DTH11温湿度传感器通过GPIO接口连接在树莓派相关引脚,其数据协议可在购买传感器时获取。在.Net5中获取DTH11温湿度传感器数据,是通过GpioController操作GPIO引脚实现的。

本文的参考文档为《c# 树莓派GPIO读取DTH11温湿度传感器数据》。主要是对GpioController的操作进行了封装,方便获取传感器数据。

另外,可以参考《用树莓派实时监测室内温湿度》,增加对树莓派和DTH11温湿度传感器的认识。

参考DTH11温湿度传感器的Arduino demo,可以了解一些GPIO操作。

需要注意的是,读取传感器数据的C#代码中,将状态设置为低电平时必须使用Thread.Sleep暂停程序,将状态设置为高电平时必须使用WaitMicroseconds方法,将其顺序交换或者使用异步的await Task.Delay都不能正确获取温湿度数据;但是在定时读取数据时,可以使用await Task.Delay语句暂停指定时间。这种情况可能的原因是,C#在操作GPIO时的暂停机制和操作系统级别的暂停机制不太一样。

最后,在发布软件的时候,由于树莓派运行Linux系统(如Raspberry Pi OS或Ubuntu),目标运行时选择“可移植的”即可,当然也可以选择具体的目标运行时。

1、DHT11.cs

/// <summary>
/// 温湿度传感器类
/// </summary>
// ReSharper disable once InconsistentNaming
public class DHT11
{
    public Action<string> ErrorOccured = (message) => { };
    public Action<float, float> DataReceived = (temperature, humidity) => { };

    public int ReadDataInterval
    {
        set => _readDataInterval = value < _minReadDataInterval ? _minReadDataInterval : value;
        get => _readDataInterval;
    }

    private readonly int _pin; //传感器针脚

    private readonly byte[] _dhtData = {0, 0, 0, 0, 0}; //传感器数据

    private int _readDataInterval = _minReadDataInterval; //获取传感器数据时间间隔(ms)
    private static readonly int _minReadDataInterval = 2000; //获取传感器数据最小时间间隔(ms)

    private readonly GpioController _gpio;
    private readonly CancellationTokenSource _tokenSource;

    public DHT11(int pin)
    {
        _pin = pin;
        _tokenSource = new CancellationTokenSource();
        _gpio = new GpioController(PinNumberingScheme.Board);
    }

    public Tuple<bool, string> Start()
    {
        try
        {
            if (_gpio.IsPinOpen(_pin))
            {
                return new Tuple<bool, string>(false, $"PinOpen: {_pin}");
            }

            //开启针脚
            _gpio.OpenPin(_pin);

            //启动取数据线程
            Task.Run(async () => { await DoWorkAsync(_tokenSource.Token); });

            return new Tuple<bool, string>(true, string.Empty);
        }
        catch (Exception e)
        {
            return new Tuple<bool, string>(false, e.Message);
        }
    }

    public void Stop()
    {
        _tokenSource.Dispose();
    }

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

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

                if (ReadData())
                {
                    var humidity = _dhtData[0] + _dhtData[1] / 100f; //湿度(%)
                    var temperature = _dhtData[2] + _dhtData[3] / 100f; //湿度(℃)
                    DataReceived?.Invoke(temperature, humidity);
                }
                else
                {
                    Console.WriteLine($"{DateTime.Now: hh:mm:ss.fff} Receive no data.");
                }
            }
            catch (Exception e)
            {
                ErrorOccured?.Invoke(e.Message);
                break;
            }
        }

        _gpio.ClosePin(_pin);
    }

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

        //注意:必须先Thread.Sleep(20)、WaitMicroseconds(40),
        //然后在循环中WaitMicroseconds(1),否则计时无法生效。
        _gpio.SetPinMode(_pin, PinMode.Output);
        _gpio.Write(_pin, PinValue.Low);
        Thread.Sleep(20);
        _gpio.Write(_pin, PinValue.High);
        WaitMicroseconds(40);
        _gpio.SetPinMode(_pin, PinMode.Input);

        var lastState = PinValue.High;
        var j = 0;

        for (var i = 0; i < 85; i++)
        {
            int count = 0;
            while (_gpio.Read(_pin) == lastState)
            {
                count++;
                WaitMicroseconds(1);
                if (count == 255)
                {
                    break;
                }
            }

            lastState = _gpio.Read(_pin);
            if (count == 255)
            {
                break;
            }

            if ((i >= 4) && (i % 2 == 0))
            {
                _dhtData[j / 8] <<= 1;
                if (count > 16)
                {
                    _dhtData[j / 8] |= 1;
                }

                j++;
            }
        }

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

    private static void WaitMicroseconds(int microseconds)
    {
        var until = DateTime.UtcNow.Ticks + microseconds * 10;
        while (DateTime.UtcNow.Ticks < until)
        {
            //Do nothing
        }
    }
}

2、Main

static void Main(string[] args)
{
    try
    {
        var dht = new DHT11(7) {ReadDataInterval = 10000};
        dht.ErrorOccured += (message) => { Console.WriteLine($"{DateTime.Now: hh:mm:ss.fff} ErrorOccured: {message}."); };
        dht.DataReceived += (temperature, humidity) =>
        {
            Console.WriteLine(
                $"{DateTime.Now: hh:mm:ss.fff} DataReceived: 温度 - {temperature}℃, 湿度 - {humidity}%.");
        };
        dht.Start();

        Console.ReadKey();

        dht.Stop();
    }
    catch (Exception e)
    {
        Console.WriteLine($"Main: {e.Message}");
    }

    Console.WriteLine("Main exit");
    Console.ReadKey();
}
posted @ 2022-07-07 21:05  xhubobo  阅读(792)  评论(0编辑  收藏  举报