Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Source/Clima_Demo/Commands/RestartCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Meadow;
using Meadow.Cloud;
using System.Threading;

namespace Clima_Demo.Commands;

/// <summary>
/// Restart Clima
/// </summary>
public class RestartCommand : IMeadowCommand
{
/// <summary>
/// Delay to restart
/// </summary>
public int Delay { get; set; }

/// <summary>
/// Initialise the command and register handler.
/// </summary>
public static void Initialise()
{
Resolver.CommandService.Subscribe<RestartCommand>(command =>
{
Resolver.Log.Info($"RestartCommand: Meadow will restart in {command.Delay} seconds.");
Thread.Sleep(command.Delay * 1000);
Resolver.Device.PlatformOS.Reset();
});

}
}
51 changes: 50 additions & 1 deletion Source/Clima_Demo/MeadowApp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Meadow;
using Clima_Demo.Commands;
using Meadow;
using Meadow.Devices;
using Meadow.Devices.Esp32.MessagePayloads;
using Meadow.Hardware;
Expand Down Expand Up @@ -31,6 +32,54 @@ public override Task Initialize()
return Task.CompletedTask;
}

public override Task Run()
{
var svc = Resolver.UpdateService;

// Uncomment to clear any persisted update info. This allows installing
// the same update multiple times, such as you might do during development.
// svc.ClearUpdates();

svc.StateChanged += (sender, updateState) =>
{
Resolver.Log.Info($"UpdateState {updateState}");
if (updateState == UpdateState.DownloadingFile)
{
mainController?.StopUpdating();
}
};

svc.RetrieveProgress += (updateService, info) =>
{
short percentage = (short)((double)info.DownloadProgress / info.FileSize * 100);

Resolver.Log.Info($"Downloading... {percentage}%");
};

svc.UpdateAvailable += async (updateService, info) =>
{
Resolver.Log.Info($"Update available!");

// Queue update for retrieval "later"
await Task.Delay(5000);

updateService.RetrieveUpdate(info);
};

svc.UpdateRetrieved += async (updateService, info) =>
{
Resolver.Log.Info($"Update retrieved!");

await Task.Delay(5000);

updateService.ApplyUpdate(info);
};

RestartCommand.Initialise();

return Task.CompletedTask;
}

private void OnMeadowSystemError(MeadowSystemErrorInfo error, bool recommendReset, out bool forceReset)
{
if (error is Esp32SystemErrorInfo espError)
Expand Down
9 changes: 7 additions & 2 deletions Source/Clima_Demo/app.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ MeadowCloud:
Enabled: true

# Enable Over-the-air Updates
# EnableUpdates: false
EnableUpdates: true

# Enable Health Metrics
EnableHealthMetrics: true

# How often to send metrics to Meadow.Cloud
HealthMetricsIntervalMinutes: 60
HealthMetricsIntervalMinutes: 5


# Settings for Clima
Clima:
OffsetToNorth: 0.0
2 changes: 1 addition & 1 deletion Source/Clima_Demo/meadow.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Coprocessor:

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

# Should the ESP32 automatically reconnect to the configured access point?
AutomaticallyReconnect: true
Expand Down
15 changes: 7 additions & 8 deletions Source/Meadow.Clima/Controllers/CloudController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Meadow.Devices.Clima.Constants;
using Meadow.Devices.Clima.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

Expand Down Expand Up @@ -47,21 +48,19 @@ public void LogAppStartup(string hardwareRevision)
/// Logs the device information including name and location.
/// </summary>
/// <param name="deviceName">The name of the device.</param>
/// <param name="latitiude">The latitude of the device location.</param>
/// <param name="latitude">The latitude of the device location.</param>
/// <param name="longitude">The longitude of the device location.</param>
public void LogDeviceInfo(string deviceName, double latitiude, double longitude)
public void LogDeviceInfo(string deviceName, double latitude, double longitude)
{
var cloudEvent = new CloudEvent
Resolver.Log.Info("LogDeviceInfo: Create CloudEvent");
CloudEvent cloudEvent = new CloudEvent
{
Description = "Clima Position Telemetry",
Timestamp = DateTime.UtcNow,
EventId = 109,
Measurements = new Dictionary<string, object> { { "device_name", deviceName }, { "lat", latitude }, { "long", longitude } }
};

cloudEvent.Measurements.Add("device_name", deviceName);
cloudEvent.Measurements.Add("lat", latitiude);
cloudEvent.Measurements.Add("long", longitude);


SendEvent(cloudEvent);
}

Expand Down
55 changes: 52 additions & 3 deletions Source/Meadow.Clima/Controllers/LocationController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Meadow.Devices.Clima.Hardware;
using Meadow.Peripherals.Sensors.Location.Gnss;
using Meadow.Units;
using System;
using System.Threading;

namespace Meadow.Devices.Clima.Controllers;

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

private ManualResetEvent positionReceived = new ManualResetEvent(false);

/// <summary>
/// Initializes a new instance of the <see cref="LocationController"/> class.
/// </summary>
Expand All @@ -31,21 +35,66 @@ public LocationController(IClimaHardware clima)
{
this.gnss = gnss;
this.gnss.GnssDataReceived += OnGnssDataReceived;
this.gnss.StartUpdating();
}
}

/// <summary>
/// Gets the current geographic position as a <see cref="GeographicCoordinate"/>.
/// </summary>
/// <value>
/// The geographic position, including latitude, longitude, and altitude, if available.
/// </value>
/// <remarks>
/// This property is updated when valid GNSS data is received. It represents the last known position
/// and remains unchanged until new valid data is processed.
/// </remarks>
public GeographicCoordinate? Position { get; private set; } = default;

private void OnGnssDataReceived(object sender, IGnssResult e)
{
if (e is GnssPositionInfo pi)
{
if (pi.IsValid && pi.Position != null)
{
// remember our position
Position = pi.Position;
// 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.Set();
PositionReceived?.Invoke(this, pi);
gnss?.StopUpdating();
StopUpdating();
}
}
}
}

/// <summary>
/// Starts the GNSS sensor to begin updating location data.
/// </summary>
/// <remarks>
/// This method invokes the <see cref="IGnssSensor.StartUpdating"/> method on the associated GNSS sensor,
/// if it is available, to start receiving GNSS data updates.
/// </remarks>
public void StartUpdating(bool forced = false)
{
// start updating if forced to find new data or we don;t have current location
if (forced || !positionReceived.WaitOne(0))
{
gnss?.StartUpdating();
};
}

/// <summary>
/// Stops the GNSS sensor from updating location data.
/// </summary>
/// <remarks>
/// This method halts the GNSS data updates by invoking the <see cref="IGnssSensor.StopUpdating"/>
/// method on the associated GNSS sensor, if it is available.
/// </remarks>
public void StopUpdating()
{
// stop listening to data arriving from GNSS
gnss?.StopUpdating();

// TODO: can we tell GNSS sensor to stop calculating GPS location and stop sending data to reduce power consumption?
}
}
40 changes: 35 additions & 5 deletions Source/Meadow.Clima/Controllers/NetworkController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Meadow.Hardware;
using Meadow.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -27,17 +28,35 @@
/// <summary>
/// Gets a value indicating whether the network is connected.
/// </summary>
public bool IsConnected { get; private set; }
public bool IsConnected => networkAdapter.IsConnected;

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

/// <summary>
/// Gets the period for triggering network down events.
/// </summary>
public TimeSpan DownEventPeriod { get; private set; }
public TimeSpan DownEventPeriod { get; } = TimeSpan.FromSeconds(30);


/// <summary>
/// Port used for UdpLogging.
/// </summary>
/// <remarks>
/// Default set in constructor is port 5100
/// </remarks>
private int UdpLoggingPort { get; set; }

/// <summary>
/// Instance of UdpLogger. Use to remove UdpLogger if the network disconnects
/// </summary>
private UdpLogger? UdpLogger { get; set; }

Check failure on line 55 in Source/Meadow.Clima/Controllers/NetworkController.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'UdpLogger' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 55 in Source/Meadow.Clima/Controllers/NetworkController.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'UdpLogger' could not be found (are you missing a using directive or an assembly reference?)

private string WifiSsid { get; set; } = "SSID";
private string WifiPassword { get; set; } = "PASSWORD";


/// <summary>
/// Initializes a new instance of the <see cref="NetworkController"/> class.
Expand Down Expand Up @@ -73,8 +92,8 @@
{
if (!wifi.IsConnected)
{
Resolver.Log.Info("Connecting to network...");
await wifi.Connect("interwebs", "1234567890");
Resolver.Log.Info($"Connecting to network: {WifiSsid}");
await wifi.Connect(WifiSsid, WifiPassword);
}
}

Expand Down Expand Up @@ -118,6 +137,14 @@

private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionEventArgs args)
{
// Remove the UdpLogger if it's in the LogProviderCollection.
if (UdpLogger != null)
{
Resolver.Log.RemoveProvider(UdpLogger);
UdpLogger.Dispose();
UdpLogger = null;
}

lastDown = DateTimeOffset.UtcNow;
downEventTimer.Change(DownEventPeriod, TimeSpan.FromMilliseconds(-1));
ConnectionStateChanged?.Invoke(this, false);
Expand Down Expand Up @@ -150,6 +177,9 @@

private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args)
{
Resolver.Log.Info("Add UdpLogger");
Resolver.Log.AddProvider(UdpLogger = new UdpLogger(UdpLoggingPort));

if (sender is IWiFiNetworkAdapter wifi)
{
_ = ReportWiFiScan(wifi);
Expand Down
1 change: 1 addition & 0 deletions Source/Meadow.Clima/Controllers/NotificationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public NotificationController(IRgbPwmLed? rgbLed)
/// <param name="status">The system status to set.</param>
public void SetSystemStatus(SystemStatus status)
{
Resolver.Log.Info($"SetSystemStatus = {status}");
switch (status)
{
case SystemStatus.LowPower:
Expand Down
35 changes: 28 additions & 7 deletions Source/Meadow.Clima/Controllers/PowerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class PowerController
/// <summary>
/// Gets the interval at which power data is updated.
/// </summary>
public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5);
public TimeSpan UpdateInterval { get; private set; } = TimeSpan.FromSeconds(10);

/// <summary>
/// Gets the voltage level below which a battery warning is issued.
Expand All @@ -59,12 +59,35 @@ public class PowerController
public PowerController(IClimaHardware clima)
{
this.clima = clima;
}

/// <summary>
/// Remove event handler and stop updating
/// </summary>
public void StopUpdating()
{
Resolver.Log.Info($"PowerController: Stop Updating");
if (clima.SolarVoltageInput is { } solarVoltage)
{
solarVoltage.Updated -= SolarVoltageUpdated;
solarVoltage.StopUpdating();
}

Initialize();
if (clima.BatteryVoltageInput is { } batteryVoltage)
{
batteryVoltage.Updated -= BatteryVoltageUpdated;
batteryVoltage.StopUpdating();
}
}

private void Initialize()
/// <summary>
/// Add event handler and start updating
/// </summary>
/// <param name="powerControllerUpdateInterval"></param>
public void StartUpdating(TimeSpan powerControllerUpdateInterval)
{
UpdateInterval = powerControllerUpdateInterval;

if (clima.SolarVoltageInput is { } solarVoltage)
{
solarVoltage.Updated += SolarVoltageUpdated;
Expand All @@ -84,13 +107,11 @@ private void Initialize()
/// <returns>A task that represents the asynchronous operation. The task result contains the power data.</returns>
public Task<PowerData> GetPowerData()
{
var data = new PowerData
return Task.FromResult(new PowerData
{
BatteryVoltage = clima.BatteryVoltageInput?.Voltage ?? null,
SolarVoltage = clima.SolarVoltageInput?.Voltage ?? null,
};

return new Task<PowerData>(() => data);
});
}

/// <summary>
Expand Down
Loading
Loading