练习:使用 .NET IoT 库

已完成

在本单元中,你将使用 .NET IoT 库来编写与洞穴监视器设备交互的代码。 尽管支持 .NET SDK 在 Raspberry Pi 和其他 IoT 板上运行,但建议使用计算机。 通过计算机,你可以使用功能齐全的 IDE 和编辑器,例如 Visual Studio 和 Visual Studio Code。

创建应用

从开发计算机上的命令 shell 完成以下步骤。

  1. 创建一个名为 cheeseave.net 的新控制台应用。

    dotnet new console -o cheesecave.net
    cd ./cheesecave.net
    

    前面的命令:

    • 在名为 cheeseave.net 的文件夹中创建一个新的 .NET 控制台应用。
    • 将当前位置设置为 cheeseave.net 文件夹。
  2. 将 .NET IoT 库添加到项目中。

    dotnet add package System.Device.Gpio --version 2.1.0
    dotnet add package IoT.Device.Bindings --version 2.1.0
    

    前面的命令:

    • System.Device.GpioIot.Device.Bindings 包添加到项目中。
    • 指定为两个包添加版本 2.1.0

添加代码

  1. 使用首选的 IDE 或编辑器,将 Program.cs 的内容替换为以下代码:

    using System;
    using System.Device.Gpio;
    using System.Device.Gpio.Drivers;
    using System.Device.I2c;
    using Iot.Device.Bmxx80;
    using Iot.Device.Bmxx80.ReadResult;
    
    bool _fanOn = false;
    bool _exit = false;
    int _pin = 21;
    
    // Initialize the GPIO controller
    using GpioController gpio = new GpioController();
    
    // Open the GPIO pin for output
    gpio.OpenPin(_pin, PinMode.Output);
    gpio.Write(_pin, PinValue.Low);
    
    // Get a reference to a device on the I2C bus
    var i2cSettings = new I2cConnectionSettings(1, Bme280.DefaultI2cAddress);
    using I2cDevice i2cDevice = I2cDevice.Create(i2cSettings);
    
    // Create a reference to the BME280
    using var bme280 = new Bme280(i2cDevice);
    
    // Write the fan, temperature, and humidity statuses to the console
    WriteStatus();
    
    // Main control loop
    while (!_exit)
    {
        string commandText = Console.ReadLine() ?? string.Empty;
        DoCommand(commandText);
    }
    
    // Close the pin before exit
    gpio.ClosePin(_pin);
    
    // Exit
    return;
    
    void DoCommand(string commandText)
    {
        switch (commandText)
        {
            case "exit":
                Console.WriteLine("Exiting!");
                _exit = true;
                break;
    
            case "fan":
                if (!_fanOn)
                {
                    // Turn on the fan
                    gpio.Write(_pin, PinValue.High);
                    Console.WriteLine("Turned fan on!");
                }
                else 
                {
                    // Turn off the fan
                    gpio.Write(_pin, PinValue.Low);
                    Console.WriteLine("Turned fan off!");
                }
                _fanOn = !_fanOn;
                WriteStatus();
                break;
    
            case "status":
                WriteStatus();
                break;
    
            default:
                Console.WriteLine("Command not recognized! Try again.");
                return;
        }
    }
    
    void WriteStatus()
    {
        // Read the BME280
        Bme280ReadResult output = bme280.Read();
        double temperatureF = output.Temperature?.DegreesFahrenheit ?? double.NaN;
        double humidityPercent = output.Humidity?.Percent ?? double.NaN;
    
        // Print statuses
        Console.WriteLine();
        Console.WriteLine("DEVICE STATUS");
        Console.WriteLine("-------------");
        Console.WriteLine($"Fan: {(_fanOn ? "ON" : "OFF")}");
        Console.WriteLine($"Temperature: {temperatureF:0.#}°F");
        Console.WriteLine($"Relative humidity: {humidityPercent:#.##}%");
        Console.WriteLine();
        Console.WriteLine("Enter command (status/fan/exit):");
    }
    
  2. 检查代码以了解它如何与 GPIO 引脚进行交互。 在上述代码中:

    • 创建了一个 GpioController 实例以用于 GPIO 操作。

    • 通过 gpio.OpenPin() 打开引脚 21。 该引脚通过 PinMode.Output 打开输出。

    • 引脚 21 的值设置为 PinValue.Low。 该值表示引脚可以发出的最低电压量。 为了点亮 LED,PinValue.Low 代表“关”,PinValue.High 代表“开”。 结果,代表风扇的 LED 关闭。

    • 该代码通过写入 PinValue.LowPinValue.High 来打开和关闭风扇/LED。

      提示

      许多真实的中继使用 PinValue.High 表示“关”,使用 PinValue.Low 表示“开”。 这与你在本练习中为代表风扇中继的 LED 实施的操作相反。

    • 在代码退出之前,通过 gpio.ClosePin() 关闭引脚 21。

  3. 检查代码以了解它如何与 BME280 交互。 在上述代码中:

    • 创建了 I2cConnectionSettings 的实例。 第一个构造函数参数 busId 设置为 1,即 Raspberry Pi 上的 I2C 总线 ID。 第二个构造函数参数 deviceAddress 设置为 Bme280.DefaultI2cAddress

      警告

      某些 BME280 分线使用 Bme280.SecondaryI2cAddress 作为设备地址。 如果应用引发 System.IO.IOException: Error 121 performing I2C data transfer.,请尝试改用此值。

    • I2cDevice 的实例是使用 I2cConnectionSettings 对象创建的。

    • Bme280 的实例是使用 I2cDevice 对象创建的。 该对象代表物理 BME280。

    • WriteStatus() 方法中,Bme280ReadResult 对象是通过调用 Bme280.Read() 创建的。

    • Bme280ReadResult 对象包含 TemperatureHumidity 属性。

      • TemperatureHumidity 属性均可为空,这意味着它们可能包含 null。 因此,null 条件运算符 ?. 用于访问其成员。
      • 这些属性本身公开进行自动单位转换的属性,例如 DegreesFahrenheitPercent
      • 在这两种情况下,null 合并运算符 ?? 会检查返回值,如果返回值为 null,则将其替换为 double.NaN

    提示

    如果需要了解 null 安全方面的帮助,请参阅 C# 中的 null 安全

生成应用

运行以下命令以生成应用。

dotnet build

生成完成,没有错误或警告。

在下一单元中,你将应用部署到 Raspberry Pi 并进行测试。