From a4898d9d131c8026dff9deeb4a3e1cd43230a5d6 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Tue, 14 May 2024 13:24:12 -0500 Subject: [PATCH 1/8] added wifi scan reporting --- .../Controllers/NetworkController.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Source/Meadow.Clima/Controllers/NetworkController.cs b/Source/Meadow.Clima/Controllers/NetworkController.cs index 89616d2..2acdf1a 100644 --- a/Source/Meadow.Clima/Controllers/NetworkController.cs +++ b/Source/Meadow.Clima/Controllers/NetworkController.cs @@ -1,6 +1,7 @@ using Meadow.Hardware; using System; using System.Threading; +using System.Threading.Tasks; namespace Meadow.Devices; @@ -19,6 +20,16 @@ public class NetworkController public NetworkController(INetworkAdapter networkAdapter) { + if (networkAdapter is IWiFiNetworkAdapter wifi) + { + if (wifi.IsConnected) + { + _ = ReportWiFiScan(wifi); + } + + // TODO: make this configurable + wifi.SetAntenna(AntennaType.External); + } this.networkAdapter = networkAdapter; networkAdapter.NetworkConnected += OnNetworkConnected; @@ -46,8 +57,38 @@ private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionE ConnectionStateChanged?.Invoke(this, false); } + private async Task ReportWiFiScan(IWiFiNetworkAdapter wifi) + { + var networks = await wifi.Scan(); + + Resolver.Log.Info("WiFi Scan Results"); + if (networks.Count == 0) + { + Resolver.Log.Info("No networks found"); + } + else + { + foreach (var network in networks) + { + if (string.IsNullOrEmpty(network.Ssid)) + { + Resolver.Log.Info($"[no ssid]: {network.SignalDbStrength}dB"); + } + else + { + Resolver.Log.Info($"{network.Ssid}: {network.SignalDbStrength}dB"); + } + } + } + } + private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args) { + if (sender is IWiFiNetworkAdapter wifi) + { + _ = ReportWiFiScan(wifi); + } + lastDown = null; ConnectionStateChanged?.Invoke(this, true); } From 22c323a950e24017b6e6db05c5a258f0e066e1bc Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Tue, 14 May 2024 13:24:49 -0500 Subject: [PATCH 2/8] handle system errors --- Source/Clima_Demo/MeadowApp.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index 599a554..82fecdc 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -1,5 +1,6 @@ using Meadow; using Meadow.Devices; +using Meadow.Devices.Esp32.MessagePayloads; using Meadow.Hardware; using System.Collections.Generic; using System.Threading.Tasks; @@ -25,6 +26,29 @@ public override Task Initialize() var wifi = Device.NetworkAdapters.Primary(); mainController.Initialize(Clima.Create(), wifi); + Device.PlatformOS.MeadowSystemError += OnMeadowSystemError; return Task.CompletedTask; } + + private void OnMeadowSystemError(object sender, MeadowSystemErrorInfo e) + { + Resolver.Log.Error($"App has detected a system error: {e.Message}"); + if (e is Esp32SystemErrorInfo esp) + { + Resolver.Log.Error($"ESP function: {esp.Function}"); + Resolver.Log.Error($"ESP status code: {esp.StatusCode}"); + } + if (e.Exception != null) + { + Resolver.Log.Error($"Exception: {e.Exception.Message}"); + Resolver.Log.Error($"ErrorNumber: {e.ErrorNumber}"); + Resolver.Log.Error($"HResult: {e.Exception.HResult}"); + + if (e.Exception.InnerException != null) + { + Resolver.Log.Error($"InnerException: {e.Exception.InnerException.Message}"); + Resolver.Log.Error($"HResult: {e.Exception.InnerException.HResult}"); + } + } + } } \ No newline at end of file From 18509ba25b8e05b18051e1293fee9cc141987fc7 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Tue, 14 May 2024 13:26:33 -0500 Subject: [PATCH 3/8] state notifications --- .../Controllers/NotificationController.cs | 37 ++++++++++++++++--- Source/Meadow.Clima/MainController.cs | 37 ++++++++++++++++--- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/Source/Meadow.Clima/Controllers/NotificationController.cs b/Source/Meadow.Clima/Controllers/NotificationController.cs index a958daa..c58fdbe 100644 --- a/Source/Meadow.Clima/Controllers/NotificationController.cs +++ b/Source/Meadow.Clima/Controllers/NotificationController.cs @@ -14,6 +14,16 @@ public enum Warnings BatteryLow = 1 << 2, } + public enum SystemStatus + { + Starting, + SearchingForNetwork, + NetworkConnected, + ConnectingToCloud, + Connected, + + } + private readonly IRgbPwmLed? rgbLed; private Warnings activeWarnings = Warnings.None; @@ -22,14 +32,29 @@ public NotificationController(IRgbPwmLed? rgbLed) this.rgbLed = rgbLed; } - public void SystemStarting() + public void SetSystemStatus(SystemStatus status) { - rgbLed?.SetColor(RgbLedColors.Red); - } + switch (status) + { + case SystemStatus.Starting: + rgbLed?.SetColor(RgbLedColors.Red); + break; + case SystemStatus.SearchingForNetwork: + rgbLed?.StartBlink(RgbLedColors.Red); + break; + case SystemStatus.NetworkConnected: + rgbLed?.StopAnimation(); + rgbLed?.SetColor(RgbLedColors.Magenta); + break; + case SystemStatus.ConnectingToCloud: + rgbLed?.StartBlink(RgbLedColors.Cyan); + break; + case SystemStatus.Connected: + rgbLed?.StopAnimation(); + rgbLed?.SetColor(RgbLedColors.Green); + break; - public void SystemUp() - { - ReportWarnings(); + } } public void SetWarning(Warnings warning) diff --git a/Source/Meadow.Clima/MainController.cs b/Source/Meadow.Clima/MainController.cs index a8dc7ab..bfb517e 100644 --- a/Source/Meadow.Clima/MainController.cs +++ b/Source/Meadow.Clima/MainController.cs @@ -29,7 +29,7 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) notificationController = new NotificationController(hardware.RgbLed); Resolver.Services.Add(notificationController); - notificationController.SystemStarting(); + notificationController.SetSystemStatus(NotificationController.SystemStatus.Starting); cloudController = new CloudController(); @@ -56,12 +56,20 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) if (!networkController.IsConnected) { + notificationController.SetSystemStatus(NotificationController.SystemStatus.SearchingForNetwork); Resolver.Log.Info("Network is down"); - notificationController.SetWarning(NotificationController.Warnings.NetworkDisconnected); + } + else + { + notificationController.SetSystemStatus(NotificationController.SystemStatus.NetworkConnected); + if (Resolver.MeadowCloudService.ConnectionState == CloudConnectionState.Connecting) + { + notificationController.SetSystemStatus(NotificationController.SystemStatus.ConnectingToCloud); + } } } - notificationController.SystemUp(); + Resolver.MeadowCloudService.ConnectionStateChanged += OnMeadowCloudServiceConnectionStateChanged; cloudController.LogAppStartup(hardware.RevisionString); TelemetryTimer = new Timer(TelemetryTimerProc, null, 0, -1); @@ -69,6 +77,19 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) return Task.CompletedTask; } + private void OnMeadowCloudServiceConnectionStateChanged(object sender, CloudConnectionState e) + { + switch (e) + { + case CloudConnectionState.Connected: + notificationController.SetSystemStatus(NotificationController.SystemStatus.Connected); + break; + default: + notificationController.SetSystemStatus(NotificationController.SystemStatus.ConnectingToCloud); + break; + } + } + private async void TelemetryTimerProc(object _) { Resolver.Log.Info($"Collecting telemetry"); @@ -92,6 +113,11 @@ private void OnNetworkStillDown(object sender, System.TimeSpan e) Resolver.Log.Info($"Network has been down for {e.TotalSeconds:N0} seconds"); // TODO: after some period, should we force-restart the device? + if (e.TotalMinutes > 5) + { + Resolver.Log.Info($"Network Connection timeout. Resetting the device."); + Resolver.Device.PlatformOS.Reset(); + } } private void OnNetworkConnectionStateChanged(object sender, bool e) @@ -100,6 +126,7 @@ private void OnNetworkConnectionStateChanged(object sender, bool e) { Resolver.Log.Info($"Network connected"); notificationController.ClearWarning(NotificationController.Warnings.NetworkDisconnected); + notificationController.SetSystemStatus(NotificationController.SystemStatus.NetworkConnected); } else { @@ -115,7 +142,7 @@ private void OnBatteryVoltageWarning(object sender, bool e) var message = $"Battery voltage dropped below {powerController.LowBatteryWarningLevel.Volts:N1}"; Resolver.Log.Warn(message); - notificationController.SetWarning(NotificationController.Warnings.BatteryLow); + //notificationController.SetWarning(NotificationController.Warnings.BatteryLow); cloudController.LogWarning(message); } else @@ -135,7 +162,7 @@ private void OnSolarVoltageWarning(object sender, bool e) var message = $"Solar voltage dropped below {powerController.LowSolarWarningLevel.Volts:N1}"; Resolver.Log.Warn(message); - notificationController.SetWarning(NotificationController.Warnings.SolarLoadLow); + //notificationController.SetWarning(NotificationController.Warnings.SolarLoadLow); cloudController.LogWarning(message); } else From a762d493cccfb5d301d37b2c7a60ae2732b15102 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Tue, 14 May 2024 13:26:50 -0500 Subject: [PATCH 4/8] caching wind direction data --- Source/Meadow.Clima/Controllers/SensorController.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs index ecb1454..2d6634a 100644 --- a/Source/Meadow.Clima/Controllers/SensorController.cs +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -9,8 +9,9 @@ namespace Clima_Demo; public class SensorController { private IClimaHardware hardware; + private CircularBuffer windVaneBuffer = new CircularBuffer(12); - public bool LogSensorData { get; set; } = false; + public bool LogSensorData { get; set; } = true; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); public SensorController(IClimaHardware clima) @@ -44,7 +45,7 @@ public SensorController(IClimaHardware clima) if (clima.WindVane is { } windVane) { windVane.Updated += WindvaneUpdated; - windVane.StartUpdating(UpdateInterval); + windVane.StartUpdating(TimeSpan.FromSeconds(2)); } if (clima.RainGauge is { } rainGuage) @@ -106,6 +107,8 @@ private void RainGuageUpdated(object sender, IChangeResult e) private void WindvaneUpdated(object sender, IChangeResult e) { - Resolver.Log.InfoIf(LogSensorData, $"Wind Vane: {e.New.Compass16PointCardinalName} ({e.New.Radians:0.#} radians)"); + windVaneBuffer.Append(e.New); + + Resolver.Log.InfoIf(LogSensorData, $"Wind Vane: {e.New.DecimalDegrees} (mean: {windVaneBuffer.Mean().DecimalDegrees})"); } } From 79ab512d2154f72eaed857acd1623ee980274e09 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Wed, 15 May 2024 16:42:42 -0500 Subject: [PATCH 5/8] added optional light sensor --- Source/Meadow.Clima.sln | 15 +++++++++ Source/Meadow.Clima/Clima.cs | 4 ++- .../Controllers/SensorController.cs | 8 ++++- .../Hardware/ClimaHardwareBase.cs | 6 ++++ .../Meadow.Clima/Hardware/ClimaHardwareV4.cs | 33 ++++++++++++++++--- .../Meadow.Clima/Hardware/IClimaHardware.cs | 3 ++ Source/Meadow.Clima/Meadow.Clima.csproj | 3 +- Source/Meadow.Clima/Models/SensorData.cs | 5 +++ 8 files changed, 70 insertions(+), 7 deletions(-) diff --git a/Source/Meadow.Clima.sln b/Source/Meadow.Clima.sln index 3201975..902740e 100644 --- a/Source/Meadow.Clima.sln +++ b/Source/Meadow.Clima.sln @@ -60,6 +60,8 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CommonContracts", "Addition EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serialization.MicroJson", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Libraries_and_Frameworks\Serialization.MicroJson\Driver\Serialization.MicroJson.csproj", "{6300EAB4-806F-4C18-8FE0-57C45A2C0C58}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sensors.Light.Veml7700", "..\..\Meadow.Foundation\Source\Meadow.Foundation.Peripherals\Sensors.Light.Veml7700\Driver\Sensors.Light.Veml7700.csproj", "{C5925D96-F9F4-4F42-AC8D-97E464252A4D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -502,6 +504,18 @@ Global {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|iPhone.Build.0 = Release|Any CPU {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|iPhone.Build.0 = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|Any CPU.Build.0 = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|iPhone.ActiveCfg = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|iPhone.Build.0 = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {C5925D96-F9F4-4F42-AC8D-97E464252A4D}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -532,6 +546,7 @@ Global {494082D7-2C48-45A6-8FF7-DD553D27BC4A} = {4AB0FC09-05D2-4F55-9C2D-13C133456E2F} {567267B3-ED96-4FEA-B555-2EE203372EA4} = {4AB0FC09-05D2-4F55-9C2D-13C133456E2F} {6300EAB4-806F-4C18-8FE0-57C45A2C0C58} = {2889A476-F914-49E8-9F97-4CC6CA34A901} + {C5925D96-F9F4-4F42-AC8D-97E464252A4D} = {2889A476-F914-49E8-9F97-4CC6CA34A901} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CA61E123-F783-4CB3-8EB2-099EE930ADD4} diff --git a/Source/Meadow.Clima/Clima.cs b/Source/Meadow.Clima/Clima.cs index eb35032..f7b7a26 100644 --- a/Source/Meadow.Clima/Clima.cs +++ b/Source/Meadow.Clima/Clima.cs @@ -63,7 +63,9 @@ public static IClimaHardware Create() logger?.Info("Failed to instantiate version MCP23008"); } - if (version > 4) + logger?.Info($"MCP Version: {version}"); + + if (version >= 4) { logger?.Info("Instantiating Clima v4 specific hardware"); hardware = new ClimaHardwareV4(ccm, i2cBus, mcpVersion!); diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs index 2d6634a..2871e39 100644 --- a/Source/Meadow.Clima/Controllers/SensorController.cs +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -11,7 +11,7 @@ public class SensorController private IClimaHardware hardware; private CircularBuffer windVaneBuffer = new CircularBuffer(12); - public bool LogSensorData { get; set; } = true; + public bool LogSensorData { get; set; } = false; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); public SensorController(IClimaHardware clima) @@ -59,6 +59,11 @@ public SensorController(IClimaHardware clima) anemometer.Updated += AnemometerUpdated; anemometer.StartUpdating(UpdateInterval); } + + if (clima.LightSensor is { } lightSensor) + { + lightSensor.StartUpdating(UpdateInterval); + } } public async Task GetSensorData() @@ -72,6 +77,7 @@ public async Task GetSensorData() WindSpeed = hardware.Anemometer?.WindSpeed ?? null, WindDirection = hardware.WindVane?.WindAzimuth ?? null, Rain = hardware.RainGauge?.RainDepth ?? null, + Light = hardware.LightSensor?.Illuminance ?? null, }; } diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs index 6105568..24d5fcf 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs @@ -6,6 +6,7 @@ using Meadow.Peripherals.Sensors; using Meadow.Peripherals.Sensors.Atmospheric; using Meadow.Peripherals.Sensors.Environmental; +using Meadow.Peripherals.Sensors.Light; using Meadow.Peripherals.Sensors.Weather; using System; @@ -63,6 +64,9 @@ public abstract class ClimaHardwareBase : IClimaHardware /// public IAnemometer? Anemometer => GetAnemometer(); + /// + public ILightSensor? LightSensor => GetLightSensor(); + /// public IAnalogInputPort? SolarVoltageInput { get; protected set; } @@ -155,6 +159,8 @@ public IConnector?[] Connectors return _gasResistanceSensor; } + protected virtual ILightSensor? GetLightSensor() => null; + /// /// Get the Wind Vane on the Clima board /// diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs index a68165f..685aeff 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs @@ -1,5 +1,7 @@ using Meadow.Foundation.ICs.IOExpanders; +using Meadow.Foundation.Sensors.Light; using Meadow.Hardware; +using Meadow.Peripherals.Sensors.Light; namespace Meadow.Devices; @@ -8,6 +10,9 @@ namespace Meadow.Devices; /// public class ClimaHardwareV4 : ClimaHardwareV3 { + private ILightSensor? _lightSensor; + private bool _firstLightQuery = true; + /// public override string RevisionString => "v4.x"; @@ -18,7 +23,7 @@ public class ClimaHardwareV4 : ClimaHardwareV3 /// The I2C bus /// The Mcp23008 used to read version information public ClimaHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) - : base(device, i2cBus, mcpVersion) + : base(device, i2cBus, mcpVersion) { } @@ -27,12 +32,32 @@ public ClimaHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp230 Logger?.Trace("Creating Qwiic I2C connector"); return new I2cConnector( - nameof(Qwiic), + nameof(Qwiic), new PinMapping { - new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.I2C1_SCL), - new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.I2C1_SDA), + new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.I2C1_SCL), + new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.I2C1_SDA), }, new I2cBusMapping(_device, 1)); } + + protected override ILightSensor? GetLightSensor() + { + if (_lightSensor == null && _firstLightQuery) + { + try + { + Logger?.Trace("Creating Light sensor"); + _lightSensor = new Veml7700(_device.CreateI2cBus()); + } + catch + { + Logger?.Warn("Light sensor not found on I2C bus"); + } + } + + _firstLightQuery = false; + + return _lightSensor; + } } \ No newline at end of file diff --git a/Source/Meadow.Clima/Hardware/IClimaHardware.cs b/Source/Meadow.Clima/Hardware/IClimaHardware.cs index 2d9d42c..c0c6a71 100644 --- a/Source/Meadow.Clima/Hardware/IClimaHardware.cs +++ b/Source/Meadow.Clima/Hardware/IClimaHardware.cs @@ -4,6 +4,7 @@ using Meadow.Peripherals.Sensors; using Meadow.Peripherals.Sensors.Atmospheric; using Meadow.Peripherals.Sensors.Environmental; +using Meadow.Peripherals.Sensors.Light; using Meadow.Peripherals.Sensors.Weather; namespace Meadow.Devices; @@ -18,6 +19,8 @@ public interface IClimaHardware /// public II2cBus I2cBus { get; } + public ILightSensor? LightSensor { get; } + /// /// Gets the ITemperatureSensor on the Clima board /// diff --git a/Source/Meadow.Clima/Meadow.Clima.csproj b/Source/Meadow.Clima/Meadow.Clima.csproj index 5946c38..a3f28e2 100644 --- a/Source/Meadow.Clima/Meadow.Clima.csproj +++ b/Source/Meadow.Clima/Meadow.Clima.csproj @@ -1,4 +1,4 @@ - + 1.11.0 Wilderness Labs, Inc @@ -27,6 +27,7 @@ + diff --git a/Source/Meadow.Clima/Models/SensorData.cs b/Source/Meadow.Clima/Models/SensorData.cs index 03c36dd..78044e6 100644 --- a/Source/Meadow.Clima/Models/SensorData.cs +++ b/Source/Meadow.Clima/Models/SensorData.cs @@ -12,6 +12,7 @@ public record SensorData public Speed? WindSpeed { get; set; } public Azimuth? WindDirection { get; set; } public Length? Rain { get; set; } + public Illuminance? Light { get; set; } public Dictionary AsTelemetryDictionary() { @@ -44,6 +45,10 @@ public Dictionary AsTelemetryDictionary() { d.Add(nameof(Rain), Rain.Value.Centimeters); } + if (Light != null) + { + d.Add(nameof(Light), Light.Value.Lux); + } return d; } From 37cd3ea10cdf0e8ed4bb3f6d91389a8d4bc56e8e Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Mon, 20 May 2024 12:22:55 -0500 Subject: [PATCH 6/8] attempting to improve power consumption --- Source/Clima_Demo/meadow.config.yaml | 6 +- Source/Clima_Demo/readme.md | 27 ++++++++ .../Controllers/CloudController.cs | 12 ++++ .../Controllers/NetworkController.cs | 30 +++++++++ .../Controllers/NotificationController.cs | 8 +++ .../Controllers/PowerController.cs | 9 +++ Source/Meadow.Clima/MainController.cs | 62 +++++++++++++++++-- 7 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 Source/Clima_Demo/readme.md diff --git a/Source/Clima_Demo/meadow.config.yaml b/Source/Clima_Demo/meadow.config.yaml index ae6dfe8..8b632ab 100644 --- a/Source/Clima_Demo/meadow.config.yaml +++ b/Source/Clima_Demo/meadow.config.yaml @@ -2,9 +2,9 @@ Name: Clima Coprocessor: - AutomaticallyStartNetwork: true - AutomaticallyReconnect: true - MaximumRetryCount: 7 + AutomaticallyStartNetwork: false + AutomaticallyReconnect: false + MaximumRetryCount: 0 Network: DefaultInterface: WiFi diff --git a/Source/Clima_Demo/readme.md b/Source/Clima_Demo/readme.md new file mode 100644 index 0000000..f9c48f5 --- /dev/null +++ b/Source/Clima_Demo/readme.md @@ -0,0 +1,27 @@ +```mermaid +flowchart TD +%% Nodes + A("Boot") + B{"Connect to Cloud"} + C("Deliver Data") + D("Shutdown network") + E("Device Sleep") + F("Device Wake") + G("Collect Telemetry") + H{{"`tick++ % pubcount`"}} + + ne_0("== 0") + eq_0("!= 0") + +%% Edge connections between nodes + A --> B + B --> yes --> C + B --> no --> E + C --> D + D --> E + E -.-> F + F --> G + G --> H + H --> eq_0 --> E + H --> ne_0 --> B +``` \ No newline at end of file diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index 2c25e32..605f837 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -2,11 +2,23 @@ using Meadow.Cloud; using System; using System.Linq; +using System.Threading.Tasks; namespace Clima_Demo; public class CloudController { + public async Task WaitForDataToSend() + { + // TODO: add a timeout here + while (Resolver.MeadowCloudService.QueueCount > 0) + { + Resolver.Log.Info($"Waiting for {Resolver.MeadowCloudService.QueueCount} items to be delivered..."); + await Task.Delay(1000); + } + Resolver.Log.Info($"All cloud data has been sent"); + } + public void LogAppStartupAfterCrash() { SendEvent(CloudEventIds.DeviceStarted, $"Device restarted after crash"); diff --git a/Source/Meadow.Clima/Controllers/NetworkController.cs b/Source/Meadow.Clima/Controllers/NetworkController.cs index 2acdf1a..58c02c8 100644 --- a/Source/Meadow.Clima/Controllers/NetworkController.cs +++ b/Source/Meadow.Clima/Controllers/NetworkController.cs @@ -38,6 +38,36 @@ public NetworkController(INetworkAdapter networkAdapter) downEventTimer = new Timer(DownEventTimerProc, null, -1, -1); } + public async Task ConnectToCloud() + { + if (networkAdapter is IWiFiNetworkAdapter wifi) + { + Resolver.Log.Info("Connecting to network..."); + await wifi.Connect("interwebs", "1234567890"); + } + + Resolver.Log.Info($"Connecting to network {(networkAdapter.IsConnected ? "succeeded" : "FAILED")}"); + + return networkAdapter.IsConnected; + } + + public async Task ShutdownNetwork() + { + if (networkAdapter is IWiFiNetworkAdapter wifi) + { + Resolver.Log.Info("Disconnecting network..."); + try + { + await wifi.Disconnect(true); + Resolver.Log.Info("Network disconnected"); + } + catch (Exception ex) + { + Resolver.Log.Info($"Network disconnect failed: {ex.Message}"); + } + } + } + private void DownEventTimerProc(object _) { if (networkAdapter.IsConnected) diff --git a/Source/Meadow.Clima/Controllers/NotificationController.cs b/Source/Meadow.Clima/Controllers/NotificationController.cs index c58fdbe..242b646 100644 --- a/Source/Meadow.Clima/Controllers/NotificationController.cs +++ b/Source/Meadow.Clima/Controllers/NotificationController.cs @@ -16,6 +16,7 @@ public enum Warnings public enum SystemStatus { + LowPower, Starting, SearchingForNetwork, NetworkConnected, @@ -36,6 +37,13 @@ public void SetSystemStatus(SystemStatus status) { switch (status) { + case SystemStatus.LowPower: + if (rgbLed != null) + { + rgbLed.StopAnimation(); + rgbLed.IsOn = false; + } + break; case SystemStatus.Starting: rgbLed?.SetColor(RgbLedColors.Red); break; diff --git a/Source/Meadow.Clima/Controllers/PowerController.cs b/Source/Meadow.Clima/Controllers/PowerController.cs index 04e7550..8e3d5f7 100644 --- a/Source/Meadow.Clima/Controllers/PowerController.cs +++ b/Source/Meadow.Clima/Controllers/PowerController.cs @@ -54,6 +54,15 @@ public async Task GetPowerData() }; } + public void TimedSleep(TimeSpan duration) + { + Resolver.Log.Info("Going to sleep..."); + + Resolver.Device.PlatformOS.Sleep(duration); + + Resolver.Log.Info("PowerController completed sleep"); + } + private void SolarVoltageUpdated(object sender, IChangeResult e) { Resolver.Log.InfoIf(LogPowerData, $"Solar Voltage: {e.New.Volts:0.#} volts"); diff --git a/Source/Meadow.Clima/MainController.cs b/Source/Meadow.Clima/MainController.cs index bfb517e..b6e7400 100644 --- a/Source/Meadow.Clima/MainController.cs +++ b/Source/Meadow.Clima/MainController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using static Clima_Demo.NotificationController; namespace Meadow.Devices; @@ -16,7 +17,7 @@ public class MainController private LocationController locationController; private NetworkController? networkController; private CloudController cloudController; - private Timer TelemetryTimer; + private Timer SystemTimer; public TimeSpan TelemetryPublicationPeriod { get; } = TimeSpan.FromMinutes(1); @@ -72,11 +73,59 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) Resolver.MeadowCloudService.ConnectionStateChanged += OnMeadowCloudServiceConnectionStateChanged; cloudController.LogAppStartup(hardware.RevisionString); - TelemetryTimer = new Timer(TelemetryTimerProc, null, 0, -1); + Resolver.Device.PlatformOS.AfterWake += PlatformOS_AfterWake; + + _ = SystemPreSleepStateProc(); return Task.CompletedTask; } + private void PlatformOS_AfterWake(object sender, WakeSource e) + { + Resolver.Log.Info("PlatformOS_AfterWake"); + SystemPostWakeStateProc(); + } + + private async Task SystemPreSleepStateProc() + { + // connect to cloud + if (networkController != null) + { + notificationController.SetSystemStatus(SystemStatus.SearchingForNetwork); + var connected = await networkController.ConnectToCloud(); + if (connected) + { + if (cloudController != null) + { + await cloudController.WaitForDataToSend(); + } + + await networkController.ShutdownNetwork(); + } + } + + notificationController.SetSystemStatus(SystemStatus.LowPower); + powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + } + + private int tick; + private const int SensorReadPeriodSeconds = 10; + private const int PublicationPeriodMinutes = 5; + + private void SystemPostWakeStateProc() + { + // collect data + + if (++tick % PublicationPeriodMinutes * 60 / SensorReadPeriodSeconds == 0) + { + SystemPreSleepStateProc(); + } + else + { + powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + } + } + private void OnMeadowCloudServiceConnectionStateChanged(object sender, CloudConnectionState e) { switch (e) @@ -90,12 +139,17 @@ private void OnMeadowCloudServiceConnectionStateChanged(object sender, CloudConn } } - private async void TelemetryTimerProc(object _) + private async void SystemTimerProc(object _) { + tick++; + + // collect telemetry every tick Resolver.Log.Info($"Collecting telemetry"); + try { + // publish telemetry to the cloud every N ticks cloudController.LogTelemetry( await sensorController.GetSensorData(), await powerController.GetPowerData()); @@ -105,7 +159,7 @@ await sensorController.GetSensorData(), Resolver.Log.Warn($"Failed to log telemetry: {ex.Message}"); } - TelemetryTimer.Change(TelemetryPublicationPeriod, TimeSpan.FromMilliseconds(-1)); + SystemTimer.Change(TelemetryPublicationPeriod, TimeSpan.FromMilliseconds(-1)); } private void OnNetworkStillDown(object sender, System.TimeSpan e) From d6ce95d23d7fef5e96c08d34fb5433271fa8eb1f Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Wed, 5 Jun 2024 14:50:23 -0500 Subject: [PATCH 7/8] added error handling. adjusted sensor report time intervals --- Source/Clima_Demo/MeadowApp.cs | 37 +++++++++++--- .../Controllers/CloudController.cs | 2 +- .../Controllers/NetworkController.cs | 7 ++- .../Controllers/SensorController.cs | 28 ++++++++--- Source/Meadow.Clima/MainController.cs | 50 +++++++++++++------ 5 files changed, 91 insertions(+), 33 deletions(-) diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index 82fecdc..d3a6b35 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -2,7 +2,6 @@ using Meadow.Devices; using Meadow.Devices.Esp32.MessagePayloads; using Meadow.Hardware; -using System.Collections.Generic; using System.Threading.Tasks; namespace Clima_Demo; @@ -16,20 +15,44 @@ public MeadowApp() mainController = new MainController(); } - public override void OnBootFromCrash(IEnumerable crashReports) - { - mainController.LogAppStartupAfterCrash(crashReports); - } - public override Task Initialize() { + var reliabilityService = Resolver.Services.Get(); + reliabilityService!.MeadowSystemError += OnMeadowSystemError; + if (reliabilityService.LastBootWasFromCrash) + { + mainController.LogAppStartupAfterCrash(reliabilityService.GetCrashData()); + reliabilityService.ClearCrashData(); + } + var wifi = Device.NetworkAdapters.Primary(); mainController.Initialize(Clima.Create(), wifi); - Device.PlatformOS.MeadowSystemError += OnMeadowSystemError; return Task.CompletedTask; } + private void OnMeadowSystemError(MeadowSystemErrorInfo error, bool recommendReset, out bool forceReset) + { + if (error is Esp32SystemErrorInfo espError) + { + Resolver.Log.Warn($"The ESP32 has had an error ({espError.StatusCode})."); + } + else + { + Resolver.Log.Info($"We've had a system error: {error}"); + } + + if (recommendReset) + { + Resolver.Log.Warn($"Meadow is recommending a device reset"); + } + + forceReset = recommendReset; + + // override the reset recommendation + //forceReset = false; + } + private void OnMeadowSystemError(object sender, MeadowSystemErrorInfo e) { Resolver.Log.Error($"App has detected a system error: {e.Message}"); diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index 605f837..eed99bc 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -13,7 +13,7 @@ public async Task WaitForDataToSend() // TODO: add a timeout here while (Resolver.MeadowCloudService.QueueCount > 0) { - Resolver.Log.Info($"Waiting for {Resolver.MeadowCloudService.QueueCount} items to be delivered..."); + // Resolver.Log.Info($"Waiting for {Resolver.MeadowCloudService.QueueCount} items to be delivered..."); await Task.Delay(1000); } Resolver.Log.Info($"All cloud data has been sent"); diff --git a/Source/Meadow.Clima/Controllers/NetworkController.cs b/Source/Meadow.Clima/Controllers/NetworkController.cs index 58c02c8..3e1b74f 100644 --- a/Source/Meadow.Clima/Controllers/NetworkController.cs +++ b/Source/Meadow.Clima/Controllers/NetworkController.cs @@ -42,8 +42,11 @@ public async Task ConnectToCloud() { if (networkAdapter is IWiFiNetworkAdapter wifi) { - Resolver.Log.Info("Connecting to network..."); - await wifi.Connect("interwebs", "1234567890"); + if (!wifi.IsConnected) + { + Resolver.Log.Info("Connecting to network..."); + await wifi.Connect("interwebs", "1234567890"); + } } Resolver.Log.Info($"Connecting to network {(networkAdapter.IsConnected ? "succeeded" : "FAILED")}"); diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs index 2871e39..cae8c14 100644 --- a/Source/Meadow.Clima/Controllers/SensorController.cs +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -10,8 +10,9 @@ public class SensorController { private IClimaHardware hardware; private CircularBuffer windVaneBuffer = new CircularBuffer(12); + private Length? startupRainValue; - public bool LogSensorData { get; set; } = false; + private bool LogSensorData { get; set; } = false; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); public SensorController(IClimaHardware clima) @@ -21,37 +22,48 @@ public SensorController(IClimaHardware clima) if (clima.TemperatureSensor is { } temperatureSensor) { temperatureSensor.Updated += TemperatureUpdated; + // atmospheric temp is slow to change + temperatureSensor.StartUpdating(TimeSpan.FromSeconds(15)); temperatureSensor.StartUpdating(UpdateInterval); } if (clima.BarometricPressureSensor is { } pressureSensor) { pressureSensor.Updated += PressureUpdated; - pressureSensor.StartUpdating(UpdateInterval); + // barometric pressure is slow to change + pressureSensor.StartUpdating(TimeSpan.FromMinutes(1)); } if (clima.HumiditySensor is { } humiditySensor) { humiditySensor.Updated += HumidityUpdated; - humiditySensor.StartUpdating(UpdateInterval); + // humidity is slow to change + humiditySensor.StartUpdating(TimeSpan.FromMinutes(1)); } if (clima.CO2ConcentrationSensor is { } co2Sensor) { co2Sensor.Updated += Co2Updated; - co2Sensor.StartUpdating(UpdateInterval); + // CO2 levels are slow to change + co2Sensor.StartUpdating(TimeSpan.FromMinutes(5)); } if (clima.WindVane is { } windVane) { windVane.Updated += WindvaneUpdated; - windVane.StartUpdating(TimeSpan.FromSeconds(2)); + windVane.StartUpdating(TimeSpan.FromSeconds(1)); } if (clima.RainGauge is { } rainGuage) { - rainGuage.Updated += RainGuageUpdated; - rainGuage.StartUpdating(UpdateInterval); + rainGuage.Updated += RainGaugeUpdated; + + // TODO: if we're restarting, we need to rehydrate today's totals already collected + // startupRainValue = rainGuage.Read().Result; + // Resolver.Log.Info($"Startup rain value: {startupRainValue}"); + + // rain does not change frequently + rainGuage.StartUpdating(TimeSpan.FromMinutes(5)); } if (clima.Anemometer is { } anemometer) @@ -106,7 +118,7 @@ private void AnemometerUpdated(object sender, IChangeResult e) Resolver.Log.InfoIf(LogSensorData, $"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); } - private void RainGuageUpdated(object sender, IChangeResult e) + private void RainGaugeUpdated(object sender, IChangeResult e) { Resolver.Log.InfoIf(LogSensorData, $"Rain Gauge: {e.New.Millimeters:0.#} mm"); } diff --git a/Source/Meadow.Clima/MainController.cs b/Source/Meadow.Clima/MainController.cs index b6e7400..0a0a08d 100644 --- a/Source/Meadow.Clima/MainController.cs +++ b/Source/Meadow.Clima/MainController.cs @@ -17,7 +17,11 @@ public class MainController private LocationController locationController; private NetworkController? networkController; private CloudController cloudController; - private Timer SystemTimer; + private int tick; + private const int SensorReadPeriodSeconds = 10; + private const int PublicationPeriodMinutes = 1; + private bool lowPowerMode = false; + private Timer sleepSimulationTimer; public TimeSpan TelemetryPublicationPeriod { get; } = TimeSpan.FromMinutes(1); @@ -75,6 +79,11 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) Resolver.Device.PlatformOS.AfterWake += PlatformOS_AfterWake; + if (!lowPowerMode) + { + sleepSimulationTimer = new Timer((_) => PlatformOS_AfterWake(null, WakeSource.Unknown), null, -1, -1); + } + _ = SystemPreSleepStateProc(); return Task.CompletedTask; @@ -88,6 +97,8 @@ private void PlatformOS_AfterWake(object sender, WakeSource e) private async Task SystemPreSleepStateProc() { + await CollectTelemetry(); + // connect to cloud if (networkController != null) { @@ -100,29 +111,43 @@ private async Task SystemPreSleepStateProc() await cloudController.WaitForDataToSend(); } - await networkController.ShutdownNetwork(); + if (lowPowerMode) + { + await networkController.ShutdownNetwork(); + } } } notificationController.SetSystemStatus(SystemStatus.LowPower); - powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + if (lowPowerMode) + { + powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + } + else + { + Resolver.Log.Info("Simulating sleep"); + sleepSimulationTimer.Change(TimeSpan.FromSeconds(SensorReadPeriodSeconds), TimeSpan.FromMilliseconds(-1)); + } } - private int tick; - private const int SensorReadPeriodSeconds = 10; - private const int PublicationPeriodMinutes = 5; - private void SystemPostWakeStateProc() { // collect data if (++tick % PublicationPeriodMinutes * 60 / SensorReadPeriodSeconds == 0) { - SystemPreSleepStateProc(); + _ = SystemPreSleepStateProc(); } else { - powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + if (lowPowerMode) + { + powerController.TimedSleep(TimeSpan.FromSeconds(SensorReadPeriodSeconds)); + } + else + { + sleepSimulationTimer.Change(TimeSpan.FromSeconds(SensorReadPeriodSeconds), TimeSpan.FromMilliseconds(-1)); + } } } @@ -139,14 +164,11 @@ private void OnMeadowCloudServiceConnectionStateChanged(object sender, CloudConn } } - private async void SystemTimerProc(object _) + private async Task CollectTelemetry() { - tick++; - // collect telemetry every tick Resolver.Log.Info($"Collecting telemetry"); - try { // publish telemetry to the cloud every N ticks @@ -158,8 +180,6 @@ await sensorController.GetSensorData(), { Resolver.Log.Warn($"Failed to log telemetry: {ex.Message}"); } - - SystemTimer.Change(TelemetryPublicationPeriod, TimeSpan.FromMilliseconds(-1)); } private void OnNetworkStillDown(object sender, System.TimeSpan e) From 74ef1b6fae0d8503681b9cfbf564eac97a682f9e Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 6 Jun 2024 17:05:45 -0500 Subject: [PATCH 8/8] Only send data to cloud that has been collected since last send --- .../Controllers/SensorController.cs | 57 +++++++++++++++---- Source/Meadow.Clima/Models/SensorData.cs | 27 ++++++++- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs index cae8c14..a94cfc2 100644 --- a/Source/Meadow.Clima/Controllers/SensorController.cs +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -11,12 +11,14 @@ public class SensorController private IClimaHardware hardware; private CircularBuffer windVaneBuffer = new CircularBuffer(12); private Length? startupRainValue; + private SensorData latestData; private bool LogSensorData { get; set; } = false; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); public SensorController(IClimaHardware clima) { + latestData = new SensorData(); hardware = clima; if (clima.TemperatureSensor is { } temperatureSensor) @@ -78,48 +80,74 @@ public SensorController(IClimaHardware clima) } } - public async Task GetSensorData() + public Task GetSensorData() { - return new SensorData - { - Temperature = hardware.TemperatureSensor?.Temperature ?? null, - Pressure = hardware.BarometricPressureSensor?.Pressure ?? null, - Humidity = hardware.HumiditySensor?.Humidity ?? null, - Co2Level = hardware.CO2ConcentrationSensor?.CO2Concentration ?? null, - WindSpeed = hardware.Anemometer?.WindSpeed ?? null, - WindDirection = hardware.WindVane?.WindAzimuth ?? null, - Rain = hardware.RainGauge?.RainDepth ?? null, - Light = hardware.LightSensor?.Illuminance ?? null, - }; + lock (latestData) + { + var data = latestData.Copy(); + + latestData.Clear(); + + return Task.FromResult(data); + } } private void TemperatureUpdated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.Temperature = e.New; + } + Resolver.Log.InfoIf(LogSensorData, $"Temperature: {e.New.Celsius:0.#}C"); } private void PressureUpdated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.Pressure = e.New; + } + Resolver.Log.InfoIf(LogSensorData, $"Pressure: {e.New.Millibar:0.#}mbar"); } private void HumidityUpdated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.Humidity = e.New; + } + Resolver.Log.InfoIf(LogSensorData, $"Humidity: {e.New.Percent:0.#}%"); } private void Co2Updated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.Co2Level = e.New; + } Resolver.Log.InfoIf(LogSensorData, $"CO2: {e.New.PartsPerMillion:0.#}ppm"); } private void AnemometerUpdated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.WindSpeed = e.New; + } + Resolver.Log.InfoIf(LogSensorData, $"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); } private void RainGaugeUpdated(object sender, IChangeResult e) { + lock (latestData) + { + latestData.Rain = e.New; + } + Resolver.Log.InfoIf(LogSensorData, $"Rain Gauge: {e.New.Millimeters:0.#} mm"); } @@ -127,6 +155,11 @@ private void WindvaneUpdated(object sender, IChangeResult e) { windVaneBuffer.Append(e.New); + lock (latestData) + { + latestData.WindDirection = windVaneBuffer.Mean(); + } + Resolver.Log.InfoIf(LogSensorData, $"Wind Vane: {e.New.DecimalDegrees} (mean: {windVaneBuffer.Mean().DecimalDegrees})"); } } diff --git a/Source/Meadow.Clima/Models/SensorData.cs b/Source/Meadow.Clima/Models/SensorData.cs index 78044e6..15378e3 100644 --- a/Source/Meadow.Clima/Models/SensorData.cs +++ b/Source/Meadow.Clima/Models/SensorData.cs @@ -3,7 +3,7 @@ namespace Clima_Demo; -public record SensorData +public class SensorData { public Temperature? Temperature { get; set; } public Pressure? Pressure { get; set; } @@ -14,6 +14,31 @@ public record SensorData public Length? Rain { get; set; } public Illuminance? Light { get; set; } + public void Clear() + { + Co2Level = null; + Temperature = null; + Pressure = null; + WindSpeed = null; + WindDirection = null; + Rain = null; + Light = null; + } + + public SensorData Copy() + { + return new SensorData + { + Co2Level = Co2Level, + Temperature = Temperature, + Pressure = Pressure, + WindSpeed = WindSpeed, + WindDirection = WindDirection, + Rain = Rain, + Light = Light, + }; + } + public Dictionary AsTelemetryDictionary() { var d = new Dictionary();