(项目更新为RC-3(2023年3月30日发布)
此项目向您展示如何使用 IoT 中心连接将环境数据从您的 ProjectLab 发送到 Azure!
Meadow.Foundation是一个平台,用于在 Meadow 上使用 .NET 快速轻松地构建连接的事物。它由Wilderness Labs 创建,完全开源并由 Wilderness Labs 社区维护。
在您开始这个项目之前,请确保您的 Meadow 板和开发环境是完全最新的。检查发行说明部分以仔细检查。
在 Visual Studio 2022 for Windows或macOS中创建一个新的Meadow Application项目并将其命名为MeadowAzureIoTHub 。
对于这个项目,搜索并安装以下 NuGet 包:
该项目具有以下结构:
配置文件
让我们介绍 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 。您可以从附件部分下载它们。
注意:确保这些图像中的每一个都在“属性”面板中将“构建操作”设置为“嵌入式资源” 。
源代码
现在我们将介绍此项目中使用的所有 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 秒读取一次室温、湿度和压力。
单击Visual Studio中的“运行”按钮。它应该类似于以下 GIF:
当应用程序运行时,您应该有类似这样的输出:
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 中心概述部分,然后查看传入消息的数量随着时间的推移而增加。
就您可以使用 Meadow.Foundation 做的大量令人兴奋的事情而言,这个项目只是冰山一角。
它带有一个庞大的外设驱动程序库,其中包含适用于最常见传感器和外设的驱动程序。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !