From 17a415e9ac2ec47e55185ec68bc180f97df0a826 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Sat, 20 Jan 2024 10:55:53 -0800 Subject: [PATCH] Release 1.8.0.1 --- .../GnssTracker_SQLite_Demo.csproj | 2 +- Source/GnssTracker/DisplayConnector.cs | 3 +- Source/GnssTracker/GnssTracker.cs | 6 +-- Source/GnssTracker/GnssTracker.csproj | 14 +++--- Source/GnssTracker/GnssTrackerHardareBase.cs | 22 +++++---- Source/GnssTracker/GnssTrackerHardwareV1.cs | 3 +- .../Controllers/DisplayController.cs | 21 +------- .../GnssTracker_Demo/GnssTracker_Demo.csproj | 10 ++-- Source/GnssTracker_Demo/MeadowApp.cs | 46 +++++++++++++----- Source/GnssTracker_Demo/gnss_tracker.bmp | Bin 91882 -> 0 bytes 10 files changed, 62 insertions(+), 65 deletions(-) delete mode 100644 Source/GnssTracker_Demo/gnss_tracker.bmp diff --git a/Source/Additional Samples/GnssTracker_SQLite_Demo/GnssTracker_SQLite_Demo.csproj b/Source/Additional Samples/GnssTracker_SQLite_Demo/GnssTracker_SQLite_Demo.csproj index e9f59a6..3d11783 100644 --- a/Source/Additional Samples/GnssTracker_SQLite_Demo/GnssTracker_SQLite_Demo.csproj +++ b/Source/Additional Samples/GnssTracker_SQLite_Demo/GnssTracker_SQLite_Demo.csproj @@ -21,7 +21,7 @@ - + diff --git a/Source/GnssTracker/DisplayConnector.cs b/Source/GnssTracker/DisplayConnector.cs index f7e307d..36a7a55 100644 --- a/Source/GnssTracker/DisplayConnector.cs +++ b/Source/GnssTracker/DisplayConnector.cs @@ -121,6 +121,5 @@ internal DisplayConnectorPinDefinitions(PinMapping mapping) /// The mappings to the host controller public DisplayConnector(string name, PinMapping mapping) : base(name, new DisplayConnectorPinDefinitions(mapping)) - { - } + { } } \ No newline at end of file diff --git a/Source/GnssTracker/GnssTracker.cs b/Source/GnssTracker/GnssTracker.cs index f3c08f6..6503f18 100644 --- a/Source/GnssTracker/GnssTracker.cs +++ b/Source/GnssTracker/GnssTracker.cs @@ -5,6 +5,9 @@ namespace WildernessLabs.Hardware.GnssTracker { + /// + /// Represents a Gnss Tracker IoT acclerator + /// public class GnssTracker { private GnssTracker() { } @@ -60,9 +63,6 @@ public static IGnssTrackerHardware Create() logger?.Info("Instantiating GnssTracker v1 hardware"); hardware = new GnssTrackerHardwareV1(ccm, i2cBus); } - - - } else { diff --git a/Source/GnssTracker/GnssTracker.csproj b/Source/GnssTracker/GnssTracker.csproj index 8db68d2..9e773f8 100644 --- a/Source/GnssTracker/GnssTracker.csproj +++ b/Source/GnssTracker/GnssTracker.csproj @@ -22,12 +22,12 @@ - - - - - - - + + + + + + + diff --git a/Source/GnssTracker/GnssTrackerHardareBase.cs b/Source/GnssTracker/GnssTrackerHardareBase.cs index 0bb0341..610d7ef 100644 --- a/Source/GnssTracker/GnssTrackerHardareBase.cs +++ b/Source/GnssTracker/GnssTrackerHardareBase.cs @@ -50,6 +50,7 @@ public abstract class GnssTrackerHardwareBase : IGnssTrackerHardware /// public I2cConnector I2cHeader => (I2cConnector)Connectors[1]!; + /// public UartConnector UartHeader => (UartConnector)Connectors[0]!; @@ -90,7 +91,7 @@ internal UartConnector CreateUartConnector() new PinMapping.PinAlias(UartConnector.PinNames.RX, _device.Pins.PI9), new PinMapping.PinAlias(UartConnector.PinNames.TX, _device.Pins.PH13), }, - _device.PlatformOS.GetSerialPortName("com4")); + _device.PlatformOS.GetSerialPortName("com4")!); } internal I2cConnector CreateI2cConnector() @@ -128,7 +129,8 @@ internal DisplayConnector CreateDisplayConnector() /// /// Create a new GnssTrackerHardware base object /// - /// + /// The Meadow device + /// The I2C bus public GnssTrackerHardwareBase(IF7CoreComputeMeadowDevice device, II2cBus i2cBus) { Log.Debug("Initialize hardware..."); @@ -150,28 +152,28 @@ public GnssTrackerHardwareBase(IF7CoreComputeMeadowDevice device, II2cBus i2cBus try { - Log.Debug("Initializing BME688"); + Resolver.Log.Debug("Initializing GNSS"); - AtmosphericSensor = new Bme688(I2cBus, (byte)Bme688.Addresses.Address_0x76); + Gnss = new NeoM8(device, device.PlatformOS.GetSerialPortName("COM4")!, device.Pins.D09, device.Pins.D11); - Log.Debug("BME688 initialized"); + Resolver.Log.Debug("GNSS initialized"); } catch (Exception e) { - Log.Error($"Err initializing BME688: {e.Message}"); + Resolver.Log.Error($"Err initializing GNSS: {e.Message}"); } try { - Resolver.Log.Debug("Initializing GNSS"); + Log.Debug("Initializing BME688"); - Gnss = new NeoM8(device, device.PlatformOS.GetSerialPortName("COM4"), device.Pins.D09, device.Pins.D11); + AtmosphericSensor = new Bme688(I2cBus, (byte)Bme688.Addresses.Address_0x76); - Resolver.Log.Debug("GNSS initialized"); + Log.Debug("BME688 initialized"); } catch (Exception e) { - Resolver.Log.Error($"Err initializing GNSS: {e.Message}"); + Log.Error($"Err initializing BME688: {e.Message}"); } try diff --git a/Source/GnssTracker/GnssTrackerHardwareV1.cs b/Source/GnssTracker/GnssTrackerHardwareV1.cs index 23e9228..4f1bff8 100644 --- a/Source/GnssTracker/GnssTrackerHardwareV1.cs +++ b/Source/GnssTracker/GnssTrackerHardwareV1.cs @@ -22,7 +22,6 @@ public class GnssTrackerHardwareV1 : GnssTrackerHardwareBase /// The Meadow device /// The I2C bus public GnssTrackerHardwareV1(IF7CoreComputeMeadowDevice device, II2cBus i2cBus) : base(device, i2cBus) - { - } + { } } } \ No newline at end of file diff --git a/Source/GnssTracker_Demo/Controllers/DisplayController.cs b/Source/GnssTracker_Demo/Controllers/DisplayController.cs index 24a8026..ff9c1a7 100644 --- a/Source/GnssTracker_Demo/Controllers/DisplayController.cs +++ b/Source/GnssTracker_Demo/Controllers/DisplayController.cs @@ -1,5 +1,4 @@ using Meadow; -using Meadow.Foundation; using Meadow.Foundation.Graphics; using Meadow.Foundation.Graphics.MicroLayout; using Meadow.Logging; @@ -14,7 +13,6 @@ public class DisplayController protected Logger Log { get => Resolver.Log; } protected DisplayScreen DisplayScreen { get; set; } - protected AbsoluteLayout SplashLayout { get; set; } protected AbsoluteLayout DataLayout { get; set; } protected Font12x20 LargeFont { get; set; } @@ -34,18 +32,6 @@ public DisplayController(IGraphicsDisplay display) DisplayScreen = new DisplayScreen(display, RotationType._270Degrees); - SplashLayout = new AbsoluteLayout(DisplayScreen, 0, 0, DisplayScreen.Width, DisplayScreen.Height); - - var image = Image.LoadFromResource("GnssTracker_Demo.gnss_tracker.bmp"); - var displayImage = new Picture(0, 0, 250, 122, image) - { - BackColor = Color.FromHex("#23ABE3"), - HorizontalAlignment = HorizontalAlignment.Center, - VerticalAlignment = VerticalAlignment.Center, - }; - - SplashLayout.Controls.Add(displayImage); - DataLayout = new AbsoluteLayout(DisplayScreen, 0, 0, DisplayScreen.Width, DisplayScreen.Height); var box = new Box(0, 0, DisplayScreen.Width, DisplayScreen.Height) @@ -116,16 +102,11 @@ public DisplayController(IGraphicsDisplay display) DataLayout.Controls.Add(box, frame, TemperatureLabel, HumidityLabel, PressureLabel, LatitudeLabel, LongitudeLabel, CounterLabel); - DisplayScreen.Controls.Add(SplashLayout, DataLayout); - - DataLayout.IsVisible = false; + DisplayScreen.Controls.Add(DataLayout); } public void UpdateDisplay((Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure, Resistance? GasResistance) conditions, GnssPositionInfo locationInfo) { - SplashLayout.IsVisible = false; - DataLayout.IsVisible = true; - TemperatureLabel.Text = $"Temp: {conditions.Temperature?.Celsius:n1}°C"; HumidityLabel.Text = $"Humidity: {conditions.Humidity?.Percent:n1}%"; PressureLabel.Text = $"Pressure: {conditions.Pressure?.StandardAtmosphere:n2}atm"; diff --git a/Source/GnssTracker_Demo/GnssTracker_Demo.csproj b/Source/GnssTracker_Demo/GnssTracker_Demo.csproj index 98a1ba7..d0bf864 100644 --- a/Source/GnssTracker_Demo/GnssTracker_Demo.csproj +++ b/Source/GnssTracker_Demo/GnssTracker_Demo.csproj @@ -4,20 +4,16 @@ true Library App + GnssTracker_Demo + 10.0 - - - - - - Always - + diff --git a/Source/GnssTracker_Demo/MeadowApp.cs b/Source/GnssTracker_Demo/MeadowApp.cs index bc02429..0ae5898 100644 --- a/Source/GnssTracker_Demo/MeadowApp.cs +++ b/Source/GnssTracker_Demo/MeadowApp.cs @@ -11,12 +11,17 @@ namespace GnssTracker_Demo { public class MeadowApp : App { - GnssPositionInfo? _positionInfo; + protected DisplayController DisplayController { get; set; } + protected IGnssTrackerHardware gnssTracker { get; set; } - protected DisplayController DisplayController { get; set; } + GnssPositionInfo lastGNSSPosition; + DateTime lastGNSSPositionReportTime = DateTime.MinValue; + readonly TimeSpan GNSSPositionReportInterval = TimeSpan.FromSeconds(15); - public override async Task Initialize() + readonly TimeSpan sensorUpdateInterval = TimeSpan.FromSeconds(90); + + public override Task Initialize() { Resolver.Log.Info("Initialize hardware..."); @@ -51,7 +56,6 @@ public override async Task Initialize() if (gnssTracker.Display is { } display) { DisplayController = new DisplayController(display); - await Task.Delay(TimeSpan.FromSeconds(20)); } if (gnssTracker.OnboardLed is { } onboardLed) @@ -60,6 +64,8 @@ public override async Task Initialize() } Resolver.Log.Info("Initialization complete"); + + return Task.CompletedTask; } private void Bmi270_Updated(object sender, IChangeResult<(Acceleration3D? Acceleration3D, AngularVelocity3D? AngularVelocity3D, Temperature? Temperature)> e) @@ -76,15 +82,15 @@ private void Bme688Updated(object sender, IChangeResult<(Temperature? Temperatur { Resolver.Log.Info($"BME688: {(int)e.New.Temperature?.Celsius:0.0}C, {(int)e.New.Humidity?.Percent:0.#}%, {(int)e.New.Pressure?.Millibar:0.#}mbar"); - DisplayController.UpdateDisplay(e.New, _positionInfo); + DisplayController.UpdateDisplay(e.New, lastGNSSPosition); } private void GnssRmcReceived(object sender, GnssPositionInfo e) { if (e.Valid) { - Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); - _positionInfo = e; + ReportGNSSPosition(e); + lastGNSSPosition = e; } } @@ -92,7 +98,8 @@ private void GnssGllReceived(object sender, GnssPositionInfo e) { if (e.Valid) { - Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + ReportGNSSPosition(e); + lastGNSSPosition = e; } } @@ -107,22 +114,22 @@ public override Task Run() if (gnssTracker.AtmosphericSensor is { } bme688) { - bme688.StartUpdating(TimeSpan.FromSeconds(30)); + bme688.StartUpdating(sensorUpdateInterval); } if (gnssTracker.EnvironmentalSensor is { } scd40) { - scd40.StartUpdating(TimeSpan.FromSeconds(30)); + scd40.StartUpdating(sensorUpdateInterval); } if (gnssTracker.MotionSensor is { } bmi270) { - bmi270.StartUpdating(TimeSpan.FromSeconds(30)); + bmi270.StartUpdating(sensorUpdateInterval); } if (gnssTracker.SolarVoltageInput is { } solarVoltage) { - solarVoltage.StartUpdating(TimeSpan.FromSeconds(30)); + solarVoltage.StartUpdating(sensorUpdateInterval); } if (gnssTracker.Gnss is { } gnss) @@ -130,7 +137,20 @@ public override Task Run() gnss.StartUpdating(); } - return base.Run(); + return Task.CompletedTask; + } + + private void ReportGNSSPosition(GnssPositionInfo e) + { + if (e.Valid) + { + if (DateTime.UtcNow - lastGNSSPositionReportTime >= GNSSPositionReportInterval) + { + Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + + lastGNSSPositionReportTime = DateTime.UtcNow; + } + } } } } \ No newline at end of file diff --git a/Source/GnssTracker_Demo/gnss_tracker.bmp b/Source/GnssTracker_Demo/gnss_tracker.bmp deleted file mode 100644 index 073e8ae7180a4ca49200f0013f1f8c46f41382b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91882 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 L0dL5_y?g%;9-upY