From e4972c4f8e4d11c49946d797cedf3d129665d6c8 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Fri, 19 Apr 2024 13:21:07 -0500 Subject: [PATCH] More clean-up and refactoing of power and sensor telemetry --- Source/Clima_Demo/Clima_Demo.csproj | 1 + Source/Clima_Demo/MeadowApp.cs | 52 +----- Source/Meadow.Clima.sln | 15 ++ .../Meadow.Clima/Constants/CloudEventIds.cs | 1 + .../Controllers/CloudController.cs | 81 ++++++++- .../Controllers/NetworkController.cs | 54 ++++++ .../Controllers/NotificationController.cs | 45 ++++- .../Controllers/PowerController.cs | 81 ++++++++- .../Controllers/SensorController.cs | 19 ++ Source/Meadow.Clima/MainController.cs | 167 ++++++++++++++++++ Source/Meadow.Clima/Models/PowerData.cs | 25 +++ Source/Meadow.Clima/Models/SensorData.cs | 50 ++++++ 12 files changed, 524 insertions(+), 67 deletions(-) create mode 100644 Source/Meadow.Clima/Controllers/NetworkController.cs create mode 100644 Source/Meadow.Clima/MainController.cs create mode 100644 Source/Meadow.Clima/Models/PowerData.cs create mode 100644 Source/Meadow.Clima/Models/SensorData.cs diff --git a/Source/Clima_Demo/Clima_Demo.csproj b/Source/Clima_Demo/Clima_Demo.csproj index 0907a6f..fe32342 100644 --- a/Source/Clima_Demo/Clima_Demo.csproj +++ b/Source/Clima_Demo/Clima_Demo.csproj @@ -5,6 +5,7 @@ Library App 10 + enable diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index 1f91efd..599a554 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -8,67 +8,23 @@ namespace Clima_Demo; public class MeadowApp : App { - private IClimaHardware clima; - private NotificationController notificationController; - private SensorController sensorController; - private PowerController powerController; - private LocationController locationController; + private MainController mainController; public MeadowApp() { - Resolver.Services.Add(new CloudController()); + mainController = new MainController(); } public override void OnBootFromCrash(IEnumerable crashReports) { - Resolver.Services.Get()?.LogAppStartupAfterCrash(); + mainController.LogAppStartupAfterCrash(crashReports); } public override Task Initialize() { - Resolver.Log.LogLevel = Meadow.Logging.LogLevel.Information; - - Resolver.Log.Info("Initialize hardware..."); - - clima = Clima.Create(); - - notificationController = new NotificationController(clima.RgbLed); - Resolver.Services.Add(notificationController); - - notificationController.Starting(); - - Resolver.Services.Get()?.LogAppStartup(clima.RevisionString); - Resolver.Log.Info($"Running on Clima Hardware {clima.RevisionString}"); - - sensorController = new SensorController(clima); - powerController = new PowerController(clima); - locationController = new LocationController(clima); - var wifi = Device.NetworkAdapters.Primary(); - wifi.NetworkConnected += OnNetworkConnected; - wifi.NetworkDisconnected += OnNetworkDisconnected; - - if (wifi.IsConnected) - { - notificationController.NetworkConnected(); - } - else - { - notificationController.NetworkDisconnected(); - } - - Resolver.Log.Info("Initialization complete"); + mainController.Initialize(Clima.Create(), wifi); return Task.CompletedTask; } - - private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionEventArgs args) - { - notificationController.NetworkDisconnected(); - } - - private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args) - { - notificationController.NetworkConnected(); - } } \ No newline at end of file diff --git a/Source/Meadow.Clima.sln b/Source/Meadow.Clima.sln index 2812725..3201975 100644 --- a/Source/Meadow.Clima.sln +++ b/Source/Meadow.Clima.sln @@ -58,6 +58,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Clima_SQLite_Demo", "Additi EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CommonContracts", "Additional Samples\CommonContracts\CommonContracts.shproj", "{567267B3-ED96-4FEA-B555-2EE203372EA4}" 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -488,6 +490,18 @@ Global {494082D7-2C48-45A6-8FF7-DD553D27BC4A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {494082D7-2C48-45A6-8FF7-DD553D27BC4A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {494082D7-2C48-45A6-8FF7-DD553D27BC4A}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|iPhone.Build.0 = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|Any CPU.Build.0 = Release|Any CPU + {6300EAB4-806F-4C18-8FE0-57C45A2C0C58}.Release|iPhone.ActiveCfg = Release|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -517,6 +531,7 @@ Global {0B2E742C-9C97-4CE1-8B2A-1390CB3F1B03} = {4AB0FC09-05D2-4F55-9C2D-13C133456E2F} {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CA61E123-F783-4CB3-8EB2-099EE930ADD4} diff --git a/Source/Meadow.Clima/Constants/CloudEventIds.cs b/Source/Meadow.Clima/Constants/CloudEventIds.cs index ff15aba..503bd56 100644 --- a/Source/Meadow.Clima/Constants/CloudEventIds.cs +++ b/Source/Meadow.Clima/Constants/CloudEventIds.cs @@ -3,5 +3,6 @@ public enum CloudEventIds { DeviceStarted = 100, + Telemetry = 110, BootFromCrash = 200 } diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index 060a58d..2c25e32 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -1,6 +1,7 @@ using Meadow; using Meadow.Cloud; using System; +using System.Linq; namespace Clima_Demo; @@ -8,15 +9,43 @@ public class CloudController { public void LogAppStartupAfterCrash() { - LogEvent(CloudEventIds.DeviceStarted, $"Device restarted after crash"); + SendEvent(CloudEventIds.DeviceStarted, $"Device restarted after crash"); } public void LogAppStartup(string hardwareRevision) { - LogEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {hardwareRevision})"); + SendEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {hardwareRevision})"); } - private void LogEvent(CloudEventIds eventId, string message) + public void LogWarning(string message) + { + SendLog(message, "warning"); + } + + public void LogMessage(string message) + { + SendLog(message, "information"); + } + + public void LogTelemetry(SensorData sensorData, PowerData powerData) + { + var measurements = sensorData + .AsTelemetryDictionary() + .Concat(powerData.AsTelemetryDictionary()) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + var cloudEvent = new CloudEvent + { + Description = "Clima Telemetry", + Timestamp = DateTime.UtcNow, + EventId = (int)CloudEventIds.Telemetry, + Measurements = measurements + }; + + SendEvent(cloudEvent); + } + + private void SendLog(string message, string severity) { if (Resolver.MeadowCloudService == null) { @@ -30,14 +59,50 @@ private void LogEvent(CloudEventIds eventId, string message) return; } - Resolver.Log.Info($"Sending cloud event"); + Resolver.Log.Info($"Sending cloud log"); - Resolver.MeadowCloudService.SendEvent( - new CloudEvent + Resolver.MeadowCloudService.SendLog( + new CloudLog { - EventId = (int)eventId, - Description = message, + Message = message, Timestamp = DateTime.UtcNow, + Severity = severity }); } + + private void SendEvent(CloudEventIds eventId, string message) + { + SendEvent(new CloudEvent + { + EventId = (int)eventId, + Description = message, + Timestamp = DateTime.UtcNow, + }); + } + + private void SendEvent(CloudEvent cloudEvent) + { + if (Resolver.MeadowCloudService == null) + { + Resolver.Log.Warn($"CLOUD SERVICE IS NULL"); + return; + } + + if (!Resolver.MeadowCloudService.IsEnabled) + { + Resolver.Log.Warn($"CLOUD INTEGRATION IS DISABLED"); + return; + } + + Resolver.Log.Info($"Sending cloud event"); + + try + { + Resolver.MeadowCloudService.SendEvent(cloudEvent); + } + catch (Exception ex) + { + Resolver.Log.Warn($"Failed to send cloud event: {ex.Message}"); + } + } } diff --git a/Source/Meadow.Clima/Controllers/NetworkController.cs b/Source/Meadow.Clima/Controllers/NetworkController.cs new file mode 100644 index 0000000..89616d2 --- /dev/null +++ b/Source/Meadow.Clima/Controllers/NetworkController.cs @@ -0,0 +1,54 @@ +using Meadow.Hardware; +using System; +using System.Threading; + +namespace Meadow.Devices; + +public class NetworkController +{ + public event EventHandler? ConnectionStateChanged; + public event EventHandler? NetworkDown; + + private readonly INetworkAdapter networkAdapter; + private DateTimeOffset? lastDown; + private Timer downEventTimer; + + public bool IsConnected => networkAdapter.IsConnected; + public TimeSpan DownTime => lastDown == null ? TimeSpan.Zero : DateTime.UtcNow - lastDown.Value; + public TimeSpan DownEventPeriod { get; } = TimeSpan.FromSeconds(30); + + public NetworkController(INetworkAdapter networkAdapter) + { + this.networkAdapter = networkAdapter; + + networkAdapter.NetworkConnected += OnNetworkConnected; + networkAdapter.NetworkDisconnected += OnNetworkDisconnected; + + downEventTimer = new Timer(DownEventTimerProc, null, -1, -1); + } + + private void DownEventTimerProc(object _) + { + if (networkAdapter.IsConnected) + { + downEventTimer.Change(-1, -1); + return; + } + + NetworkDown?.Invoke(this, DownTime); + downEventTimer.Change(DownEventPeriod, TimeSpan.FromMilliseconds(-1)); + } + + private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionEventArgs args) + { + lastDown = DateTimeOffset.UtcNow; + downEventTimer.Change(DownEventPeriod, TimeSpan.FromMilliseconds(-1)); + ConnectionStateChanged?.Invoke(this, false); + } + + private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args) + { + lastDown = null; + ConnectionStateChanged?.Invoke(this, true); + } +} diff --git a/Source/Meadow.Clima/Controllers/NotificationController.cs b/Source/Meadow.Clima/Controllers/NotificationController.cs index fbdb61f..a958daa 100644 --- a/Source/Meadow.Clima/Controllers/NotificationController.cs +++ b/Source/Meadow.Clima/Controllers/NotificationController.cs @@ -1,31 +1,58 @@ -using Meadow; -using Meadow.Peripherals.Leds; +using Meadow.Peripherals.Leds; +using System; namespace Clima_Demo; public class NotificationController { + [Flags] + public enum Warnings + { + None = 0, + NetworkDisconnected = 1 << 0, + SolarLoadLow = 1 << 1, + BatteryLow = 1 << 2, + } + private readonly IRgbPwmLed? rgbLed; + private Warnings activeWarnings = Warnings.None; public NotificationController(IRgbPwmLed? rgbLed) { this.rgbLed = rgbLed; } - public void Starting() + public void SystemStarting() { rgbLed?.SetColor(RgbLedColors.Red); } - public void NetworkConnected() + public void SystemUp() + { + ReportWarnings(); + } + + public void SetWarning(Warnings warning) + { + activeWarnings |= warning; + ReportWarnings(); + } + + public void ClearWarning(Warnings warning) { - Resolver.Log.Info("Network connected"); - rgbLed?.SetColor(RgbLedColors.Green); + activeWarnings &= ~warning; + ReportWarnings(); } - public void NetworkDisconnected() + private void ReportWarnings() { - Resolver.Log.Info("Network disconnected"); - rgbLed?.SetColor(RgbLedColors.Yellow); + if (activeWarnings != Warnings.None) + { + rgbLed?.SetColor(RgbLedColors.Yellow); + } + else + { + rgbLed?.SetColor(RgbLedColors.Green); + } } } diff --git a/Source/Meadow.Clima/Controllers/PowerController.cs b/Source/Meadow.Clima/Controllers/PowerController.cs index 998ef09..04e7550 100644 --- a/Source/Meadow.Clima/Controllers/PowerController.cs +++ b/Source/Meadow.Clima/Controllers/PowerController.cs @@ -2,15 +2,35 @@ using Meadow.Devices; using Meadow.Units; using System; +using System.Threading.Tasks; namespace Clima_Demo; public class PowerController { + private readonly IClimaHardware clima; + + public event EventHandler? SolarVoltageWarning; + public event EventHandler? BatteryVoltageWarning; + + private bool inBatteryWarningState = false; + private bool inSolarWarningState = false; + public bool LogPowerData { get; set; } = false; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); + public Voltage LowBatteryWarningLevel { get; } = 3.3.Volts(); + public Voltage LowSolarWarningLevel { get; } = 3.0.Volts(); + public Voltage WarningDeadband { get; } = 0.25.Volts(); + + public PowerController( + IClimaHardware clima) + { + this.clima = clima; + + Initialize(); + } - public PowerController(IClimaHardware clima) + private void Initialize() { if (clima.SolarVoltageInput is { } solarVoltage) { @@ -25,13 +45,70 @@ public PowerController(IClimaHardware clima) } } + public async Task GetPowerData() + { + return new PowerData + { + BatteryVoltage = clima.BatteryVoltageInput?.Voltage ?? null, + SolarVoltage = clima.SolarVoltageInput?.Voltage ?? null, + }; + } + private void SolarVoltageUpdated(object sender, IChangeResult e) { Resolver.Log.InfoIf(LogPowerData, $"Solar Voltage: {e.New.Volts:0.#} volts"); + + if (e.New < LowSolarWarningLevel) + { + if (!inSolarWarningState) + { + SolarVoltageWarning?.Invoke(this, true); + + inSolarWarningState = true; + } + } + else + { + if (inSolarWarningState) + { + var resetVoltage = LowBatteryWarningLevel + WarningDeadband; + + if (e.New > resetVoltage) + { + SolarVoltageWarning?.Invoke(this, false); + + inSolarWarningState = false; + } + } + } } private void BatteryVoltageUpdated(object sender, IChangeResult e) { - Resolver.Log.InfoIf(LogPowerData, $"Battery Voltage: {e.New.Volts:0.#} volts"); + Resolver.Log.InfoIf(LogPowerData, $"Battery Voltage: {e.New.Volts:0.#} volts"); + + if (e.New < LowBatteryWarningLevel) + { + if (!inBatteryWarningState) + { + BatteryVoltageWarning?.Invoke(this, true); + + inBatteryWarningState = true; + } + } + else + { + if (inBatteryWarningState) + { + var resetVoltage = LowBatteryWarningLevel + WarningDeadband; + + if (e.New > resetVoltage) + { + BatteryVoltageWarning?.Invoke(this, false); + + inBatteryWarningState = false; + } + } + } } } diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs index f67c47b..ecb1454 100644 --- a/Source/Meadow.Clima/Controllers/SensorController.cs +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -2,16 +2,21 @@ using Meadow.Devices; using Meadow.Units; using System; +using System.Threading.Tasks; namespace Clima_Demo; public class SensorController { + private IClimaHardware hardware; + public bool LogSensorData { get; set; } = false; public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); public SensorController(IClimaHardware clima) { + hardware = clima; + if (clima.TemperatureSensor is { } temperatureSensor) { temperatureSensor.Updated += TemperatureUpdated; @@ -55,6 +60,20 @@ public SensorController(IClimaHardware clima) } } + public async 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, + }; + } + private void TemperatureUpdated(object sender, IChangeResult e) { Resolver.Log.InfoIf(LogSensorData, $"Temperature: {e.New.Celsius:0.#}C"); diff --git a/Source/Meadow.Clima/MainController.cs b/Source/Meadow.Clima/MainController.cs new file mode 100644 index 0000000..a8dc7ab --- /dev/null +++ b/Source/Meadow.Clima/MainController.cs @@ -0,0 +1,167 @@ +using Clima_Demo; +using Meadow.Hardware; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Meadow.Devices; + +public class MainController +{ + private IClimaHardware hardware; + private NotificationController notificationController; + private SensorController sensorController; + private PowerController powerController; + private LocationController locationController; + private NetworkController? networkController; + private CloudController cloudController; + private Timer TelemetryTimer; + + public TimeSpan TelemetryPublicationPeriod { get; } = TimeSpan.FromMinutes(1); + + public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) + { + this.hardware = hardware; + + Resolver.Log.Info("Initialize hardware..."); + + notificationController = new NotificationController(hardware.RgbLed); + Resolver.Services.Add(notificationController); + + notificationController.SystemStarting(); + + cloudController = new CloudController(); + + Resolver.Services.Get()?.LogAppStartup(hardware.RevisionString); + Resolver.Log.Info($"Running on Clima Hardware {hardware.RevisionString}"); + + sensorController = new SensorController(hardware); + + powerController = new PowerController(hardware); + powerController.SolarVoltageWarning += OnSolarVoltageWarning; + powerController.BatteryVoltageWarning += OnBatteryVoltageWarning; + + locationController = new LocationController(hardware); + + if (networkAdapter == null) + { + Resolver.Log.Error("No network adapter found!"); + } + else + { + networkController = new NetworkController(networkAdapter); + networkController.ConnectionStateChanged += OnNetworkConnectionStateChanged; + networkController.NetworkDown += OnNetworkStillDown; + + if (!networkController.IsConnected) + { + Resolver.Log.Info("Network is down"); + notificationController.SetWarning(NotificationController.Warnings.NetworkDisconnected); + } + } + + notificationController.SystemUp(); + cloudController.LogAppStartup(hardware.RevisionString); + + TelemetryTimer = new Timer(TelemetryTimerProc, null, 0, -1); + + return Task.CompletedTask; + } + + private async void TelemetryTimerProc(object _) + { + Resolver.Log.Info($"Collecting telemetry"); + + try + { + cloudController.LogTelemetry( + await sensorController.GetSensorData(), + await powerController.GetPowerData()); + } + catch (Exception ex) + { + Resolver.Log.Warn($"Failed to log telemetry: {ex.Message}"); + } + + TelemetryTimer.Change(TelemetryPublicationPeriod, TimeSpan.FromMilliseconds(-1)); + } + + 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? + } + + private void OnNetworkConnectionStateChanged(object sender, bool e) + { + if (e) + { + Resolver.Log.Info($"Network connected"); + notificationController.ClearWarning(NotificationController.Warnings.NetworkDisconnected); + } + else + { + Resolver.Log.Info($"Network disconnected"); + notificationController.SetWarning(NotificationController.Warnings.NetworkDisconnected); + } + } + + private void OnBatteryVoltageWarning(object sender, bool e) + { + if (e) + { + var message = $"Battery voltage dropped below {powerController.LowBatteryWarningLevel.Volts:N1}"; + Resolver.Log.Warn(message); + + notificationController.SetWarning(NotificationController.Warnings.BatteryLow); + cloudController.LogWarning(message); + } + else + { + var message = $"Battery voltage is back above minimum"; + Resolver.Log.Info(message); + + notificationController.ClearWarning(NotificationController.Warnings.BatteryLow); + cloudController.LogMessage(message); + } + } + + private void OnSolarVoltageWarning(object sender, bool e) + { + if (e) + { + var message = $"Solar voltage dropped below {powerController.LowSolarWarningLevel.Volts:N1}"; + Resolver.Log.Warn(message); + + notificationController.SetWarning(NotificationController.Warnings.SolarLoadLow); + cloudController.LogWarning(message); + } + else + { + var message = $"Solar voltage is back above minimum"; + Resolver.Log.Info(message); + + notificationController.ClearWarning(NotificationController.Warnings.SolarLoadLow); + cloudController.LogMessage(message); + } + } + + public Task Run() + { + return Task.CompletedTask; + } + + public void LogAppStartupAfterCrash(IEnumerable crashReports) + { + // the cloud service's health reporter will log this for us automatically, so no need to manually do so + Resolver.Log.Warn("Boot after crash!"); + + foreach (var report in crashReports) + { + Resolver.Log.Info(report); + } + } + +} diff --git a/Source/Meadow.Clima/Models/PowerData.cs b/Source/Meadow.Clima/Models/PowerData.cs new file mode 100644 index 0000000..2eca7b0 --- /dev/null +++ b/Source/Meadow.Clima/Models/PowerData.cs @@ -0,0 +1,25 @@ +using Meadow.Units; +using System.Collections.Generic; + +namespace Clima_Demo; + +public record PowerData +{ + public Voltage? SolarVoltage { get; set; } + public Voltage? BatteryVoltage { get; set; } + + public Dictionary AsTelemetryDictionary() + { + var d = new Dictionary(); + if (SolarVoltage != null) + { + d.Add(nameof(SolarVoltage), SolarVoltage.Value.Volts); + } + if (BatteryVoltage != null) + { + d.Add(nameof(BatteryVoltage), BatteryVoltage.Value.Volts); + } + + return d; + } +} diff --git a/Source/Meadow.Clima/Models/SensorData.cs b/Source/Meadow.Clima/Models/SensorData.cs new file mode 100644 index 0000000..03c36dd --- /dev/null +++ b/Source/Meadow.Clima/Models/SensorData.cs @@ -0,0 +1,50 @@ +using Meadow.Units; +using System.Collections.Generic; + +namespace Clima_Demo; + +public record SensorData +{ + public Temperature? Temperature { get; set; } + public Pressure? Pressure { get; set; } + public RelativeHumidity? Humidity { get; set; } + public Concentration? Co2Level { get; set; } + public Speed? WindSpeed { get; set; } + public Azimuth? WindDirection { get; set; } + public Length? Rain { get; set; } + + public Dictionary AsTelemetryDictionary() + { + var d = new Dictionary(); + if (Temperature != null) + { + d.Add(nameof(Temperature), Temperature.Value.Celsius); + } + if (Pressure != null) + { + d.Add(nameof(Pressure), Pressure.Value.Bar); + } + if (Humidity != null) + { + d.Add(nameof(Humidity), Humidity.Value.Percent); + } + if (Co2Level != null) + { + d.Add(nameof(Co2Level), Co2Level.Value.PartsPerMillion); + } + if (WindSpeed != null) + { + d.Add(nameof(WindSpeed), WindSpeed.Value.KilometersPerHour); + } + if (WindDirection != null) + { + d.Add(nameof(WindDirection), WindDirection.Value.DecimalDegrees); + } + if (Rain != null) + { + d.Add(nameof(Rain), Rain.Value.Centimeters); + } + + return d; + } +}