From a4818a76131b8b62957d6bb70473371ce0eebe07 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Sun, 9 Jun 2024 10:42:51 -0500 Subject: [PATCH 1/2] record device name and position when position data arrives --- .../Controllers/CloudController.cs | 22 ++++++++ .../Controllers/LocationController.cs | 55 ++++++------------- Source/Meadow.Clima/MainController.cs | 15 ++++- 3 files changed, 53 insertions(+), 39 deletions(-) diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index eed99bc..674aa73 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -29,6 +29,28 @@ public void LogAppStartup(string hardwareRevision) SendEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {hardwareRevision})"); } + public void LogDeviceInfo(string deviceName, double latitiude, double longitude) + { + // { + // "description": "Clima Boot Telemetry", + // "eventId": 109, + // "timestamp": "2024-05-20T22:25:15.862Z", + // "measurements": { + // "lat": 34.2277472, + // "long": -118.2273136 + // } + // } + var cloudEvent = new CloudEvent + { + Description = "Clima Position Telemetry", + Timestamp = DateTime.UtcNow, + EventId = 109, + }; + + cloudEvent.Measurements.Add("device_name", deviceName); + cloudEvent.Measurements.Add("lat", latitiude); + cloudEvent.Measurements.Add("long", longitude); + } public void LogWarning(string message) { SendLog(message, "warning"); diff --git a/Source/Meadow.Clima/Controllers/LocationController.cs b/Source/Meadow.Clima/Controllers/LocationController.cs index 63c7e35..0b71f94 100644 --- a/Source/Meadow.Clima/Controllers/LocationController.cs +++ b/Source/Meadow.Clima/Controllers/LocationController.cs @@ -1,60 +1,39 @@ using Meadow; using Meadow.Devices; using Meadow.Peripherals.Sensors.Location.Gnss; +using System; namespace Clima_Demo; public class LocationController { + private IGnssSensor gnss; + public bool LogData { get; set; } = false; + public event EventHandler PositionReceived; + public LocationController(IClimaHardware clima) { if (clima.Gnss is { } gnss) { - //gnss.GsaReceived += GnssGsaReceived; - //gnss.GsvReceived += GnssGsvReceived; - //gnss.VtgReceived += GnssVtgReceived; - gnss.RmcReceived += GnssRmcReceived; - gnss.GllReceived += GnssGllReceived; - gnss.StartUpdating(); - } - - } - private void GnssGsaReceived(object _, ActiveSatellites e) - { - if (e.SatellitesUsedForFix is { } sats) - { - Resolver.Log.Info($"Number of active satellites: {sats.Length}"); - } - } - - private void GnssGsvReceived(object _, SatellitesInView e) - { - Resolver.Log.Info($"Satellites in view: {e.Satellites.Length}"); - } - - private void GnssVtgReceived(object _, CourseOverGround e) - { - if (e is { } cv) - { - Resolver.Log.Info($"{cv}"); - }; - } - - private void GnssRmcReceived(object _, GnssPositionInfo e) - { - if (e.Valid) - { - Resolver.Log.InfoIf(LogData, $"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + this.gnss = gnss; + this.gnss.GnssDataReceived += OnGnssDataReceived; + this.gnss.StartUpdating(); } } - private void GnssGllReceived(object _, GnssPositionInfo e) + private void OnGnssDataReceived(object sender, IGnssResult e) { - if (e.Valid) + if (e is GnssPositionInfo pi) { - Resolver.Log.InfoIf(LogData, $"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + if (pi.IsValid && pi.Position != null) + { + // we only need one position fix - weather stations don't move + Resolver.Log.InfoIf(LogData, $"GNSS Position: lat: [{pi.Position.Latitude}], long: [{pi.Position.Longitude}]"); + PositionReceived?.Invoke(this, pi); + gnss.StopUpdating(); + } } } } diff --git a/Source/Meadow.Clima/MainController.cs b/Source/Meadow.Clima/MainController.cs index 0a0a08d..11170fb 100644 --- a/Source/Meadow.Clima/MainController.cs +++ b/Source/Meadow.Clima/MainController.cs @@ -38,7 +38,6 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) cloudController = new CloudController(); - Resolver.Services.Get()?.LogAppStartup(hardware.RevisionString); Resolver.Log.Info($"Running on Clima Hardware {hardware.RevisionString}"); sensorController = new SensorController(hardware); @@ -49,6 +48,8 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) locationController = new LocationController(hardware); + locationController.PositionReceived += OnPositionReceived; + if (networkAdapter == null) { Resolver.Log.Error("No network adapter found!"); @@ -89,6 +90,18 @@ public Task Initialize(IClimaHardware hardware, INetworkAdapter? networkAdapter) return Task.CompletedTask; } + private void OnPositionReceived(object sender, Peripherals.Sensors.Location.Gnss.GnssPositionInfo e) + { + if (e.Position != null) + { + // crop to 2 decimal places (~1km accuracy) for privacy + var lat = Math.Round(e.Position.Latitude, 2); + var lon = Math.Round(e.Position.Longitude, 2); + + cloudController.LogDeviceInfo(Resolver.Device.Information.DeviceName, lat, lon); + } + } + private void PlatformOS_AfterWake(object sender, WakeSource e) { Resolver.Log.Info("PlatformOS_AfterWake"); From 80c0d6f99df00d9f35263dce8f84eb5a9fb9558f Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Mon, 17 Jun 2024 17:03:11 -0500 Subject: [PATCH 2/2] updated to use new IApp pattern --- .../Controller/ClimateMonitorAgent.cs | 2 +- Source/Clima_Demo/Clima_Demo.csproj | 3 + Source/Clima_Demo/MeadowApp.cs | 82 +++++++++++++++++-- Source/Clima_Demo/app.build.yaml | 2 + Source/Clima_Demo/app.config.yaml | 2 +- Source/Meadow.Clima/ClimaAppBase.cs | 5 ++ .../{Clima.cs => ClimaHardwareProvider.cs} | 18 ++-- .../Hardware/ClimaHardwareBase.cs | 8 ++ .../Meadow.Clima/Hardware/ClimaHardwareV2.cs | 1 + .../Meadow.Clima/Hardware/ClimaHardwareV3.cs | 1 + .../Meadow.Clima/Hardware/ClimaHardwareV4.cs | 13 ++- .../Meadow.Clima/Hardware/IClimaHardware.cs | 4 +- 12 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 Source/Clima_Demo/app.build.yaml create mode 100644 Source/Meadow.Clima/ClimaAppBase.cs rename Source/Meadow.Clima/{Clima.cs => ClimaHardwareProvider.cs} (86%) diff --git a/Source/Additional Samples/Clima_SQLite_Demo/Controller/ClimateMonitorAgent.cs b/Source/Additional Samples/Clima_SQLite_Demo/Controller/ClimateMonitorAgent.cs index 48ce848..ebc77bc 100644 --- a/Source/Additional Samples/Clima_SQLite_Demo/Controller/ClimateMonitorAgent.cs +++ b/Source/Additional Samples/Clima_SQLite_Demo/Controller/ClimateMonitorAgent.cs @@ -24,7 +24,7 @@ private ClimateMonitorAgent() { } public async Task Initialize() { - clima = Meadow.Devices.Clima.Create(); + clima = Meadow.Devices.ClimaHardwareProvider.Create(); await StartUpdating(TimeSpan.FromSeconds(30)); } diff --git a/Source/Clima_Demo/Clima_Demo.csproj b/Source/Clima_Demo/Clima_Demo.csproj index fe32342..67fc3f5 100644 --- a/Source/Clima_Demo/Clima_Demo.csproj +++ b/Source/Clima_Demo/Clima_Demo.csproj @@ -11,6 +11,9 @@ + + Always + Always diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index d3a6b35..2ffe43f 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -6,17 +6,19 @@ namespace Clima_Demo; -public class MeadowApp : App +public class ClimaApp : ClimaAppBase { private MainController mainController; - public MeadowApp() + public ClimaApp() { mainController = new MainController(); } public override Task Initialize() { + Resolver.Log.Trace($"!! Initializing"); + var reliabilityService = Resolver.Services.Get(); reliabilityService!.MeadowSystemError += OnMeadowSystemError; if (reliabilityService.LastBootWasFromCrash) @@ -25,8 +27,8 @@ public override Task Initialize() reliabilityService.ClearCrashData(); } - var wifi = Device.NetworkAdapters.Primary(); - mainController.Initialize(Clima.Create(), wifi); + var wifi = Hardware.ComputeModule.NetworkAdapters.Primary(); + mainController.Initialize(Hardware, wifi); return Task.CompletedTask; } @@ -74,4 +76,74 @@ private void OnMeadowSystemError(object sender, MeadowSystemErrorInfo e) } } } -} \ No newline at end of file +} + +//public class MeadowApp : App +//{ +// private MainController mainController; + +// public MeadowApp() +// { +// mainController = new MainController(); +// } + +// 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(new Clima().Create() as IClimaHardware, wifi); + +// 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}"); +// 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 diff --git a/Source/Clima_Demo/app.build.yaml b/Source/Clima_Demo/app.build.yaml new file mode 100644 index 0000000..f9f0c47 --- /dev/null +++ b/Source/Clima_Demo/app.build.yaml @@ -0,0 +1,2 @@ +Deploy: + NoLink: [ Clima ] \ No newline at end of file diff --git a/Source/Clima_Demo/app.config.yaml b/Source/Clima_Demo/app.config.yaml index 08ab07f..90e11d7 100644 --- a/Source/Clima_Demo/app.config.yaml +++ b/Source/Clima_Demo/app.config.yaml @@ -4,7 +4,7 @@ Lifecycle: # Control whether Meadow will restart when an unhandled app exception occurs. Combine with Lifecycle > AppFailureRestartDelaySeconds to control restart timing. - RestartOnAppFailure: true + RestartOnAppFailure: false # # When app set to restart automatically on app failure, # AppFailureRestartDelaySeconds: 15 diff --git a/Source/Meadow.Clima/ClimaAppBase.cs b/Source/Meadow.Clima/ClimaAppBase.cs new file mode 100644 index 0000000..da541cb --- /dev/null +++ b/Source/Meadow.Clima/ClimaAppBase.cs @@ -0,0 +1,5 @@ +namespace Meadow.Devices; + +public abstract class ClimaAppBase : App +{ +} diff --git a/Source/Meadow.Clima/Clima.cs b/Source/Meadow.Clima/ClimaHardwareProvider.cs similarity index 86% rename from Source/Meadow.Clima/Clima.cs rename to Source/Meadow.Clima/ClimaHardwareProvider.cs index f7b7a26..e8915bf 100644 --- a/Source/Meadow.Clima/Clima.cs +++ b/Source/Meadow.Clima/ClimaHardwareProvider.cs @@ -8,16 +8,24 @@ namespace Meadow.Devices; /// /// Represents the Clima hardware /// -public class Clima +public class ClimaHardwareProvider : IMeadowAppEmbeddedHardwareProvider { - private Clima() { } + public ClimaHardwareProvider() + { + } + + public static IClimaHardware Create() + { + return new ClimaHardwareProvider() + .Create(Resolver.Services.Get()!); + } /// /// Create an instance of the Clima class /// /// /// - public static IClimaHardware Create() + public IClimaHardware Create(IMeadowDevice device) { IClimaHardware hardware; Logger? logger = Resolver.Log; @@ -25,9 +33,7 @@ public static IClimaHardware Create() logger?.Debug("Initializing Clima..."); - var device = Resolver.Device; - - if (Resolver.Device == null) + if (device == null) { var msg = "Clima instance must be created no earlier than App.Initialize()"; logger?.Error(msg); diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs index 24d5fcf..14d5335 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs @@ -87,6 +87,14 @@ public abstract class ClimaHardwareBase : IClimaHardware /// public I2cConnector? Qwiic => (I2cConnector?)Connectors[0]; + /// + public IMeadowDevice ComputeModule { get; } + + internal ClimaHardwareBase(IMeadowDevice device) + { + ComputeModule = device; + } + internal virtual I2cConnector? CreateQwiicConnector() { return null; diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs index 15f90af..3616245 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs @@ -27,6 +27,7 @@ public class ClimaHardwareV2 : ClimaHardwareBase /// The meadow device /// The I2C bus public ClimaHardwareV2(IF7FeatherMeadowDevice device, II2cBus i2cBus) + : base(device) { _device = device; diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs index 7745ce2..df3fc56 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs @@ -64,6 +64,7 @@ public class ClimaHardwareV3 : ClimaHardwareBase /// The I2C bus /// The Mcp23008 used to read version information public ClimaHardwareV3(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) + : base(device) { McpVersion = mcpVersion; diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs index 685aeff..55e482f 100644 --- a/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs @@ -1,7 +1,9 @@ -using Meadow.Foundation.ICs.IOExpanders; +using Meadow.Foundation; +using Meadow.Foundation.ICs.IOExpanders; using Meadow.Foundation.Sensors.Light; using Meadow.Hardware; using Meadow.Peripherals.Sensors.Light; +using Meadow.Units; namespace Meadow.Devices; @@ -49,14 +51,19 @@ public ClimaHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp230 { Logger?.Trace("Creating Light sensor"); _lightSensor = new Veml7700(_device.CreateI2cBus()); + if (_lightSensor is PollingSensorBase pollingSensor) + { + pollingSensor.CommunicationError += (s, e) => { _lightSensor.StopUpdating(); }; + } } catch { Logger?.Warn("Light sensor not found on I2C bus"); + _lightSensor = null; } - } - _firstLightQuery = false; + _firstLightQuery = false; + } return _lightSensor; } diff --git a/Source/Meadow.Clima/Hardware/IClimaHardware.cs b/Source/Meadow.Clima/Hardware/IClimaHardware.cs index c0c6a71..7d28ab5 100644 --- a/Source/Meadow.Clima/Hardware/IClimaHardware.cs +++ b/Source/Meadow.Clima/Hardware/IClimaHardware.cs @@ -12,7 +12,7 @@ namespace Meadow.Devices; /// /// Contract for the Clima hardware definitions /// -public interface IClimaHardware +public interface IClimaHardware : IMeadowAppEmbeddedHardware { /// /// The I2C Bus @@ -21,6 +21,8 @@ public interface IClimaHardware public ILightSensor? LightSensor { get; } + public IMeadowDevice ComputeModule { get; } + /// /// Gets the ITemperatureSensor on the Clima board ///