Skip to content

Commit 6b4594b

Browse files
Merge pull request #102 from WildernessLabs/v2.1.0.1
Release 2.1.0.1
2 parents 2eb4bf1 + ce2b028 commit 6b4594b

File tree

13 files changed

+385
-47
lines changed

13 files changed

+385
-47
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Meadow;
2+
using Meadow.Cloud;
3+
using System.Threading;
4+
5+
namespace Clima_Demo.Commands;
6+
7+
/// <summary>
8+
/// Restart Clima
9+
/// </summary>
10+
public class RestartCommand : IMeadowCommand
11+
{
12+
/// <summary>
13+
/// Delay to restart
14+
/// </summary>
15+
public int Delay { get; set; }
16+
17+
/// <summary>
18+
/// Initialise the command and register handler.
19+
/// </summary>
20+
public static void Initialise()
21+
{
22+
Resolver.CommandService.Subscribe<RestartCommand>(command =>
23+
{
24+
Resolver.Log.Info($"RestartCommand: Meadow will restart in {command.Delay} seconds.");
25+
Thread.Sleep(command.Delay * 1000);
26+
Resolver.Device.PlatformOS.Reset();
27+
});
28+
29+
}
30+
}

Source/Clima_Demo/MeadowApp.cs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Meadow;
1+
using Clima_Demo.Commands;
2+
using Meadow;
23
using Meadow.Devices;
34
using Meadow.Devices.Esp32.MessagePayloads;
45
using Meadow.Hardware;
@@ -31,6 +32,54 @@ public override Task Initialize()
3132
return Task.CompletedTask;
3233
}
3334

35+
public override Task Run()
36+
{
37+
var svc = Resolver.UpdateService;
38+
39+
// Uncomment to clear any persisted update info. This allows installing
40+
// the same update multiple times, such as you might do during development.
41+
// svc.ClearUpdates();
42+
43+
svc.StateChanged += (sender, updateState) =>
44+
{
45+
Resolver.Log.Info($"UpdateState {updateState}");
46+
if (updateState == UpdateState.DownloadingFile)
47+
{
48+
mainController?.StopUpdating();
49+
}
50+
};
51+
52+
svc.RetrieveProgress += (updateService, info) =>
53+
{
54+
short percentage = (short)((double)info.DownloadProgress / info.FileSize * 100);
55+
56+
Resolver.Log.Info($"Downloading... {percentage}%");
57+
};
58+
59+
svc.UpdateAvailable += async (updateService, info) =>
60+
{
61+
Resolver.Log.Info($"Update available!");
62+
63+
// Queue update for retrieval "later"
64+
await Task.Delay(5000);
65+
66+
updateService.RetrieveUpdate(info);
67+
};
68+
69+
svc.UpdateRetrieved += async (updateService, info) =>
70+
{
71+
Resolver.Log.Info($"Update retrieved!");
72+
73+
await Task.Delay(5000);
74+
75+
updateService.ApplyUpdate(info);
76+
};
77+
78+
RestartCommand.Initialise();
79+
80+
return Task.CompletedTask;
81+
}
82+
3483
private void OnMeadowSystemError(MeadowSystemErrorInfo error, bool recommendReset, out bool forceReset)
3584
{
3685
if (error is Esp32SystemErrorInfo espError)

Source/Clima_Demo/app.config.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ MeadowCloud:
2323
Enabled: true
2424

2525
# Enable Over-the-air Updates
26-
# EnableUpdates: false
26+
EnableUpdates: true
2727

2828
# Enable Health Metrics
2929
EnableHealthMetrics: true
3030

3131
# How often to send metrics to Meadow.Cloud
32-
HealthMetricsIntervalMinutes: 60
32+
HealthMetricsIntervalMinutes: 5
33+
34+
35+
# Settings for Clima
36+
Clima:
37+
OffsetToNorth: 0.0

Source/Clima_Demo/meadow.config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Coprocessor:
1616

1717
# Should the ESP32 automatically attempt to connect to an access point at startup?
1818
# If set to true, wifi.yaml credentials must be stored in the device.
19-
AutomaticallyStartNetwork: true
19+
AutomaticallyStartNetwork: false
2020

2121
# Should the ESP32 automatically reconnect to the configured access point?
2222
AutomaticallyReconnect: true

Source/Meadow.Clima/Controllers/CloudController.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Meadow.Devices.Clima.Constants;
33
using Meadow.Devices.Clima.Models;
44
using System;
5+
using System.Collections.Generic;
56
using System.Linq;
67
using System.Threading.Tasks;
78

@@ -47,21 +48,19 @@ public void LogAppStartup(string hardwareRevision)
4748
/// Logs the device information including name and location.
4849
/// </summary>
4950
/// <param name="deviceName">The name of the device.</param>
50-
/// <param name="latitiude">The latitude of the device location.</param>
51+
/// <param name="latitude">The latitude of the device location.</param>
5152
/// <param name="longitude">The longitude of the device location.</param>
52-
public void LogDeviceInfo(string deviceName, double latitiude, double longitude)
53+
public void LogDeviceInfo(string deviceName, double latitude, double longitude)
5354
{
54-
var cloudEvent = new CloudEvent
55+
Resolver.Log.Info("LogDeviceInfo: Create CloudEvent");
56+
CloudEvent cloudEvent = new CloudEvent
5557
{
5658
Description = "Clima Position Telemetry",
5759
Timestamp = DateTime.UtcNow,
5860
EventId = 109,
61+
Measurements = new Dictionary<string, object> { { "device_name", deviceName }, { "lat", latitude }, { "long", longitude } }
5962
};
60-
61-
cloudEvent.Measurements.Add("device_name", deviceName);
62-
cloudEvent.Measurements.Add("lat", latitiude);
63-
cloudEvent.Measurements.Add("long", longitude);
64-
63+
6564
SendEvent(cloudEvent);
6665
}
6766

Source/Meadow.Clima/Controllers/LocationController.cs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Meadow.Devices.Clima.Hardware;
22
using Meadow.Peripherals.Sensors.Location.Gnss;
3+
using Meadow.Units;
34
using System;
5+
using System.Threading;
46

57
namespace Meadow.Devices.Clima.Controllers;
68

@@ -21,6 +23,8 @@ public class LocationController
2123
/// </summary>
2224
public event EventHandler<GnssPositionInfo>? PositionReceived = null;
2325

26+
private ManualResetEvent positionReceived = new ManualResetEvent(false);
27+
2428
/// <summary>
2529
/// Initializes a new instance of the <see cref="LocationController"/> class.
2630
/// </summary>
@@ -31,21 +35,66 @@ public LocationController(IClimaHardware clima)
3135
{
3236
this.gnss = gnss;
3337
this.gnss.GnssDataReceived += OnGnssDataReceived;
34-
this.gnss.StartUpdating();
3538
}
3639
}
3740

41+
/// <summary>
42+
/// Gets the current geographic position as a <see cref="GeographicCoordinate"/>.
43+
/// </summary>
44+
/// <value>
45+
/// The geographic position, including latitude, longitude, and altitude, if available.
46+
/// </value>
47+
/// <remarks>
48+
/// This property is updated when valid GNSS data is received. It represents the last known position
49+
/// and remains unchanged until new valid data is processed.
50+
/// </remarks>
51+
public GeographicCoordinate? Position { get; private set; } = default;
52+
3853
private void OnGnssDataReceived(object sender, IGnssResult e)
3954
{
4055
if (e is GnssPositionInfo pi)
4156
{
4257
if (pi.IsValid && pi.Position != null)
4358
{
59+
// remember our position
60+
Position = pi.Position;
4461
// we only need one position fix - weather stations don't move
4562
Resolver.Log.InfoIf(LogData, $"GNSS Position: lat: [{pi.Position.Latitude}], long: [{pi.Position.Longitude}]");
63+
positionReceived.Set();
4664
PositionReceived?.Invoke(this, pi);
47-
gnss?.StopUpdating();
65+
StopUpdating();
4866
}
4967
}
5068
}
51-
}
69+
70+
/// <summary>
71+
/// Starts the GNSS sensor to begin updating location data.
72+
/// </summary>
73+
/// <remarks>
74+
/// This method invokes the <see cref="IGnssSensor.StartUpdating"/> method on the associated GNSS sensor,
75+
/// if it is available, to start receiving GNSS data updates.
76+
/// </remarks>
77+
public void StartUpdating(bool forced = false)
78+
{
79+
// start updating if forced to find new data or we don;t have current location
80+
if (forced || !positionReceived.WaitOne(0))
81+
{
82+
gnss?.StartUpdating();
83+
};
84+
}
85+
86+
/// <summary>
87+
/// Stops the GNSS sensor from updating location data.
88+
/// </summary>
89+
/// <remarks>
90+
/// This method halts the GNSS data updates by invoking the <see cref="IGnssSensor.StopUpdating"/>
91+
/// method on the associated GNSS sensor, if it is available.
92+
/// </remarks>
93+
public void StopUpdating()
94+
{
95+
// stop listening to data arriving from GNSS
96+
gnss?.StopUpdating();
97+
98+
// TODO: can we tell GNSS sensor to stop calculating GPS location and stop sending data to reduce power consumption?
99+
}
100+
}

Source/Meadow.Clima/Controllers/NetworkController.cs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Meadow.Hardware;
2+
using Meadow.Logging;
23
using System;
34
using System.Threading;
45
using System.Threading.Tasks;
@@ -27,17 +28,35 @@ public class NetworkController
2728
/// <summary>
2829
/// Gets a value indicating whether the network is connected.
2930
/// </summary>
30-
public bool IsConnected { get; private set; }
31+
public bool IsConnected => networkAdapter.IsConnected;
3132

3233
/// <summary>
3334
/// Gets the total time the network has been down.
3435
/// </summary>
35-
public TimeSpan DownTime { get; private set; }
36+
public TimeSpan DownTime => lastDown == null ? TimeSpan.Zero : DateTime.UtcNow - lastDown.Value;
3637

3738
/// <summary>
3839
/// Gets the period for triggering network down events.
3940
/// </summary>
40-
public TimeSpan DownEventPeriod { get; private set; }
41+
public TimeSpan DownEventPeriod { get; } = TimeSpan.FromSeconds(30);
42+
43+
44+
/// <summary>
45+
/// Port used for UdpLogging.
46+
/// </summary>
47+
/// <remarks>
48+
/// Default set in constructor is port 5100
49+
/// </remarks>
50+
private int UdpLoggingPort { get; set; }
51+
52+
/// <summary>
53+
/// Instance of UdpLogger. Use to remove UdpLogger if the network disconnects
54+
/// </summary>
55+
private UdpLogger? UdpLogger { get; set; }
56+
57+
private string WifiSsid { get; set; } = "SSID";
58+
private string WifiPassword { get; set; } = "PASSWORD";
59+
4160

4261
/// <summary>
4362
/// Initializes a new instance of the <see cref="NetworkController"/> class.
@@ -73,8 +92,8 @@ public async Task<bool> ConnectToCloud()
7392
{
7493
if (!wifi.IsConnected)
7594
{
76-
Resolver.Log.Info("Connecting to network...");
77-
await wifi.Connect("interwebs", "1234567890");
95+
Resolver.Log.Info($"Connecting to network: {WifiSsid}");
96+
await wifi.Connect(WifiSsid, WifiPassword);
7897
}
7998
}
8099

@@ -118,6 +137,14 @@ private void DownEventTimerProc(object _)
118137

119138
private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionEventArgs args)
120139
{
140+
// Remove the UdpLogger if it's in the LogProviderCollection.
141+
if (UdpLogger != null)
142+
{
143+
Resolver.Log.RemoveProvider(UdpLogger);
144+
UdpLogger.Dispose();
145+
UdpLogger = null;
146+
}
147+
121148
lastDown = DateTimeOffset.UtcNow;
122149
downEventTimer.Change(DownEventPeriod, TimeSpan.FromMilliseconds(-1));
123150
ConnectionStateChanged?.Invoke(this, false);
@@ -150,6 +177,9 @@ private async Task ReportWiFiScan(IWiFiNetworkAdapter wifi)
150177

151178
private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args)
152179
{
180+
Resolver.Log.Info("Add UdpLogger");
181+
Resolver.Log.AddProvider(UdpLogger = new UdpLogger(UdpLoggingPort));
182+
153183
if (sender is IWiFiNetworkAdapter wifi)
154184
{
155185
_ = ReportWiFiScan(wifi);

Source/Meadow.Clima/Controllers/NotificationController.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public NotificationController(IRgbPwmLed? rgbLed)
8181
/// <param name="status">The system status to set.</param>
8282
public void SetSystemStatus(SystemStatus status)
8383
{
84+
Resolver.Log.Info($"SetSystemStatus = {status}");
8485
switch (status)
8586
{
8687
case SystemStatus.LowPower:

Source/Meadow.Clima/Controllers/PowerController.cs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class PowerController
3535
/// <summary>
3636
/// Gets the interval at which power data is updated.
3737
/// </summary>
38-
public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5);
38+
public TimeSpan UpdateInterval { get; private set; } = TimeSpan.FromSeconds(10);
3939

4040
/// <summary>
4141
/// Gets the voltage level below which a battery warning is issued.
@@ -59,12 +59,35 @@ public class PowerController
5959
public PowerController(IClimaHardware clima)
6060
{
6161
this.clima = clima;
62+
}
63+
64+
/// <summary>
65+
/// Remove event handler and stop updating
66+
/// </summary>
67+
public void StopUpdating()
68+
{
69+
Resolver.Log.Info($"PowerController: Stop Updating");
70+
if (clima.SolarVoltageInput is { } solarVoltage)
71+
{
72+
solarVoltage.Updated -= SolarVoltageUpdated;
73+
solarVoltage.StopUpdating();
74+
}
6275

63-
Initialize();
76+
if (clima.BatteryVoltageInput is { } batteryVoltage)
77+
{
78+
batteryVoltage.Updated -= BatteryVoltageUpdated;
79+
batteryVoltage.StopUpdating();
80+
}
6481
}
6582

66-
private void Initialize()
83+
/// <summary>
84+
/// Add event handler and start updating
85+
/// </summary>
86+
/// <param name="powerControllerUpdateInterval"></param>
87+
public void StartUpdating(TimeSpan powerControllerUpdateInterval)
6788
{
89+
UpdateInterval = powerControllerUpdateInterval;
90+
6891
if (clima.SolarVoltageInput is { } solarVoltage)
6992
{
7093
solarVoltage.Updated += SolarVoltageUpdated;
@@ -84,13 +107,11 @@ private void Initialize()
84107
/// <returns>A task that represents the asynchronous operation. The task result contains the power data.</returns>
85108
public Task<PowerData> GetPowerData()
86109
{
87-
var data = new PowerData
110+
return Task.FromResult(new PowerData
88111
{
89112
BatteryVoltage = clima.BatteryVoltageInput?.Voltage ?? null,
90113
SolarVoltage = clima.SolarVoltageInput?.Voltage ?? null,
91-
};
92-
93-
return new Task<PowerData>(() => data);
114+
});
94115
}
95116

96117
/// <summary>

0 commit comments

Comments
 (0)