×

通过IoT中心将环境数据从ProjectLab发送到Azure

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2023-06-13

王伟

分享资料个

描述

(项目更新为RC-3(2023年3月30日发布)

此项目向您展示如何使用 IoT 中心连接将环境数据从您的 ProjectLab 发送到 Azure!

Meadow.Foundation是一个平台,用于在 Meadow 上使用 .NET 快速轻松地构建连接的事物。它由Wilderness Labs 创建,完全开源并由 Wilderness Labs 社区维护。

在您开始这个项目之前,请确保您的 Meadow 板和开发环境是完全最新的。检查发行说明部分以仔细检查。

第 1 步 - 创建 Meadow 应用程序项目

在 Visual Studio 2022 for WindowsmacOS中创建一个新的Meadow Application项目并将其命名为MeadowAzureIoTHub

第 2 步 - 添加所需的 NuGet 包

对于这个项目,搜索并安装以下 NuGet 包:

第 3 步 - 为 MeadowAzureIoTHub 编写代码

该项目具有以下结构:

pYYBAGSBN0CAfK5OAAA6YQ3MXxE464.jpg
 

配置文件

让我们介绍 Meadow 开机时自动使用的所有三个配置文件。

app.config.yaml

# Uncomment additional options as needed.
# To learn more about these config options, including custom application configuration settings, check out the Application Settings Configuration documentation.
# http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/Application_Settings_Configuration/

Lifecycle:
    # Control whether Meadow will restart when an unhandled app exception occurs. Combine with Lifecycle > AppFailureRestartDelaySeconds to control restart timing.
    RestartOnAppFailure: false
    # When app set to restart automatically on app failure, 
    # AppFailureRestartDelaySeconds: 15

# Adjust the level of logging detail.
Logging:
    LogLevel:
        Default: "Information"

meadow.config.yaml

meadow.config.yaml 文件可用于设置通用板和系统配置设置。这包括操作系统和设备级别的设置,例如设备名称、默认网络行为和网络设置。

# Uncommented these options as needed.
# To learn more about these config options, check out the OS & Device Configuration documentation.
# http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/OS_Device_Configuration/

Device:
    # Name of the device on the network.
    Name: MeadowAzureIoTHub

    # Uncomment if SD card hardware present on this hardware (e.g., Core-Compute module with SD add-on)? Optional; default value is `false`.
    SdStorageSupported: true

# Control how the ESP coprocessor will start and operate.
Coprocessor:
    # Should the ESP32 automatically attempt to connect to an access point at startup?
    # If set to true, wifi.config.yaml credentials must be stored in the device.
    AutomaticallyStartNetwork: true

    # Should the ESP32 automatically reconnect to the configured access point?
    AutomaticallyReconnect: true

    # Maximum number of retry attempts for connections etc. before an error code is returned.
    MaximumRetryCount: 7

# Network configuration.
Network:
    # Which interface should be used?
    DefaultInterface: WiFi

    # Automatically attempt to get the time at startup?
    GetNetworkTimeAtStartup: true

    # Time synchronization period in seconds.
    NtpRefreshPeriodSeconds: 600

    # Name of the NTP servers.
    NtpServers:
        - 0.pool.ntp.org
        - 1.pool.ntp.org
        - 2.pool.ntp.org
        - 3.pool.ntp.org

    # IP addresses of the DNS servers.
    DnsServers:
        - 1.1.1.1
        - 8.8.8.8

wifi.config.yaml

wifi.config.yaml 文件可用于设置默认 Wi-Fi 接入点和该接入点的密码。

# To enable automatically connecting to a default network, make sure to enable the Coprocessor > AutomaticallyStartNetwork value in meadow.config.yaml.
Credentials:
    Ssid: SSID
    Password: PASSWORD

注意:确保每个文件的“属性”窗口中将“复制到输出目录”设置“始终” 。

图片资产

在此项目中,我们将使用一些图像和图标来指示我们Meadow何时加入我们的 WiFi 网络以及何时通过 IoT Hub 将数据发送到 Azure 您可以从附件部分下载它们。

poYBAGSBN0OAfIzYAAAXlqEaH1Y157.jpg
 

注意:确保这些图像中的每一个都“属性”面板中将“构建操作”设置为“嵌入式资源”

源代码

现在我们将介绍此项目中使用的所有 C# 类。

Secrets.cs

public class Secrets
{
    ///     /// Name of the Azure IoT Hub created
    /// summary>
    public const string HUB_NAME = "HUB_NAME";

    ///     /// Name of the Azure IoT Hub created
    /// summary>
    public const string DEVICE_ID = "DEVICE_ID";

    ///     /// example "SharedAccessSignature sr=MeadowIoTHub ..... "
    /// summary>
    public const string SAS_TOKEN = "SharedAccessSignature...";
}

在此类中,您需要根据 Azure IoT 中心配置设置适当的值。您可以按照这篇博文一步一步地操作,其中会向您展示如何做到这一点。

IotHubManager.cs

public class IotHubManager
{
    private const string HubName = Secrets.HUB_NAME;
    private const string SasToken = Secrets.SAS_TOKEN;
    private const string DeviceId = Secrets.DEVICE_ID;

    private Connection connection;
    private SenderLink sender;

    public IotHubManager() { }

    public async Task Initialize()
    {
        string hostName = HubName + ".azure-devices.net";
        string userName = DeviceId + "@sas." + HubName;
        string senderAddress = "devices/" + DeviceId + "/messages/events";

        Resolver.Log.Info("Create connection factory...");
        var factory = new ConnectionFactory();

        Resolver.Log.Info("Create connection ...");
        connection = await factory.CreateAsync(new Address(hostName, 5671, userName, SasToken));

        Resolver.Log.Info("Create session ...");
        var session = new Session(connection);

        Resolver.Log.Info("Create SenderLink ...");
        sender = new SenderLink(session, "send-link", senderAddress);
    }

    public Task SendEnvironmentalReading((Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure, Resistance? GasResistance) reading)
    {
        try
        {
            Resolver.Log.Info("Create payload");
            string messagePayload = $"{{\"Temperature\":{reading.Temperature.Value.Celsius}," +
                $"\"Humidity\":{reading.Humidity.Value.Percent}," +
                $"\"Pressure\":{reading.Pressure.Value.Millibar}}}";

            Resolver.Log.Info("Create message");
            var message = new Message(Encoding.UTF8.GetBytes(messagePayload));
            message.ApplicationProperties = new Amqp.Framing.ApplicationProperties();

            Resolver.Log.Info("Send message");
            sender.Send(message, null, null);

            Resolver.Log.Info($"*** DATA SENT - Temperature - {reading.Temperature.Value.Celsius}, Humidity - {reading.Humidity.Value.Percent}, Pressure - {reading.Pressure.Value.Millibar} ***");
        }
        catch (Exception ex)
        {
            Resolver.Log.Info($"-- D2C Error - {ex.Message} --");
        }

        return Task.CompletedTask;
    }
}

此类用于与 Azure IoT 中心建立连接并通过 HTU21D 传感器发送环境读数。

DisplayController.cs

public class DisplayController
{
    private static readonly Lazy instance =
        new Lazy(() => new DisplayController());
    public static DisplayController Instance => instance.Value;

    static Color backgroundColor = Color.FromHex("#23ABE3");
    static Color foregroundColor = Color.Black;

    CancellationTokenSource token;

    protected BufferRgb888 imgConnecting, imgConnected, imgRefreshing, imgRefreshed;
    protected MicroGraphics graphics;

    private DisplayController() { }

    public void Initialize(IGraphicsDisplay display)
    {
        imgConnected = LoadJpeg("img_wifi_connected.jpg");
        imgConnecting = LoadJpeg("img_wifi_connecting.jpg");
        imgRefreshing = LoadJpeg("img_refreshing.jpg");
        imgRefreshed = LoadJpeg("img_refreshed.jpg");

        graphics = new MicroGraphics(display)
        {
            CurrentFont = new Font12x16(),
            Stroke = 3,
        };

        graphics.Clear(true);
    }

    BufferRgb888 LoadJpeg(string fileName)
    {
        var jpgData = LoadResource(fileName);
        var decoder = new JpegDecoder();
        decoder.DecodeJpeg(jpgData);
        return new BufferRgb888(decoder.Width, decoder.Height, decoder.GetImageData());
    }

    protected void DrawBackground()
    {
        var logo = LoadJpeg("img_meadow.jpg");

        graphics.Clear(backgroundColor);

        graphics.DrawBuffer(
            x: graphics.Width / 2 - logo.Width / 2,
            y: 63,
            buffer: logo);

        graphics.DrawText(graphics.Width / 2, 160, "Azure IoT Hub", Color.Black, alignmentH: HorizontalAlignment.Center);
    }

    protected byte[] LoadResource(string filename)
    {
        var assembly = Assembly.GetExecutingAssembly();
        var resourceName = $"MeadowAzureIoTHub.{filename}";

        using Stream stream = assembly.GetManifestResourceStream(resourceName);
        using var ms = new MemoryStream();
        stream.CopyTo(ms);
        return ms.ToArray();
    }

    public void ShowSplashScreen()
    {
        DrawBackground();

        graphics.Show();
    }

    public async Task ShowConnectingAnimation()
    {
        token = new CancellationTokenSource();

        bool alternateImg = false;
        while (!token.IsCancellationRequested)
        {
            alternateImg = !alternateImg;

            graphics.DrawBuffer(204, 6, alternateImg ? imgConnecting : imgConnected);
            graphics.Show();

            await Task.Delay(500);
        }
    }

    public void ShowConnected()
    {
        token.Cancel();
        graphics.DrawBuffer(204, 6, imgConnected);

        graphics.DrawBuffer(6, 6, imgRefreshed);

        graphics.DrawRectangle(0, 32, 240, 208, backgroundColor, true);

        graphics.DrawCircle(120, 75, 50, foregroundColor);
        graphics.DrawText(120, 59, "Temp", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.DrawCircle(62, 177, 50, foregroundColor);
        graphics.DrawText(62, 161, "Pres", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.DrawCircle(178, 177, 50, foregroundColor);
        graphics.DrawText(178, 161, "Hum", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.Show();
    }

    public async Task StartSyncCompletedAnimation((Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure, Resistance? GasResistance) reading)
    {
        graphics.DrawBuffer(6, 6, imgRefreshing);
        graphics.Show();
        await Task.Delay(TimeSpan.FromSeconds(1));

        graphics.DrawRectangle(75, 78, 90, 16, backgroundColor, true);
        graphics.DrawText(120, 78, $"{reading.Temperature.Value.Celsius:N1}°C", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.DrawRectangle(17, 180, 90, 16, backgroundColor, true);
        graphics.DrawText(62, 180, $"{reading.Pressure.Value.StandardAtmosphere:N1}atm", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.DrawRectangle(133, 180, 90, 16, backgroundColor, true);
        graphics.DrawText(178, 180, $"{reading.Humidity.Value.Percent:N2}%", foregroundColor, alignmentH: HorizontalAlignment.Center);

        graphics.DrawBuffer(6, 6, imgRefreshed);
        graphics.Show();
    }
}

此类使用MicroGraphics在 ST7789 显示屏中绘制所有 UI,例如在应用程序启动时显示启动画面、更新温度/湿度值,以及 WiFi 和同步指示器以了解消息何时发送到 IoT 中心。

MeadowApp.cs

public class MeadowApp : App
{
    RgbPwmLed onboardLed;
    IProjectLabHardware projectLab;
    IotHubManager amqpController;

    public override Task Initialize()
    {
        onboardLed = new RgbPwmLed(
            Device.Pins.OnboardLedRed,
            Device.Pins.OnboardLedGreen,
            Device.Pins.OnboardLedBlue);
        onboardLed.SetColor(Color.Red);

        try
        {
            amqpController = new IotHubManager();

            var wifi = Device.NetworkAdapters.Primary();
            wifi.NetworkConnected += NetworkConnected;

            projectLab = ProjectLab.Create();
            projectLab.EnvironmentalSensor.Updated += EnvironmentalSensorUpdated;

            DisplayController.Instance.Initialize(projectLab.Display);
            DisplayController.Instance.ShowSplashScreen();
            DisplayController.Instance.ShowConnectingAnimation();
        }
        catch (Exception ex)
        {
            Resolver.Log.Error($"Failed to Connect: {ex.Message}");
        }

        return Task.CompletedTask;
    }

    private async void NetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args)
    {
        DisplayController.Instance.ShowConnected();

        await amqpController.Initialize();

        projectLab.EnvironmentalSensor.StartUpdating(TimeSpan.FromSeconds(15));

        onboardLed.SetColor(Color.Green);
    }

    private async void EnvironmentalSensorUpdated(object sender, IChangeResult<(Meadow.Units.Temperature? Temperature, Meadow.Units.RelativeHumidity? Humidity, Meadow.Units.Pressure? Pressure, Meadow.Units.Resistance? GasResistance)> e)
    {
        await amqpController.SendEnvironmentalReading(e.New);
        await DisplayController.Instance.StartSyncCompletedAnimation(e.New);
    }
}

MeadowApp主类初始化 ProjectLab 上的显示和环境传感器,一旦 Meadow 加入 WiFi 网络并与 Azure 建立连接,它将开始每 15 秒读取一次室温、湿度和压力。

第 5 步 - 运行项目

单击Visual Studio中的“运行”按钮它应该类似于以下 GIF:

MeadowAzureIoTHub 项目正在运行...
 

当应用程序运行时,您应该有类似这样的输出:

Create connection factory...
Create connection ...
Create session ...
Create SenderLink ...
Create payload
Create message
Send message
*** DATA SENT - Temperature - 27.9073605400976, Humidity - 29.4871487326418, Pressure - 1020.90879412913 ***
Create payload
Create message
Send message
*** DATA SENT - Temperature - 28.0586979364511, Humidity - 29.1871445300884, Pressure - 1020.8916192437 ***
Create payload
Create message
Send message
*** DATA SENT - Temperature - 28.179768034583, Humidity - 28.9781536413303, Pressure - 1020.90573418024 ***
...

这可确保您已建立与Azure的连接,并且该应用每 15 秒向IoT 中心发送一次数据

您还可以转到Azure门户,打开IoT 中心概述部分,然后查看传入消息的数量随着时间的推移而增加。

 

poYBAGSBN0eAVqaSAAEfGWUvbc4799.png
IoT 中心传入消息
 

查看 Meadow.Foundation!

就您可以使用 Meadow.Foundation 做的大量令人兴奋的事情而言,这个项目只是冰山一角。

它带有一个庞大的外设驱动程序库,其中包含适用于最常见传感器和外设的驱动程序。

  • 它带有一个庞大的外设驱动程序库,其中包含适用于最常见传感器和外设的驱动程序。
  • 外设驱动程序封装了核心逻辑并公开了一个简单、干净、现代的 API。
  • 该项目得到了不断发展的社区的支持,该社区不断致力于构建酷炫的互联事物,并且总是乐于帮助新来者和讨论新项目。

参考


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !