From 0caae2ad70e8ec936c5877debb9334109487313e Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 18 Apr 2024 18:57:38 -0500 Subject: [PATCH 1/3] starting code clean up and refactor --- Source/.editorconfig | 231 ++++++++++++ Source/Clima_Demo/Clima_Demo.csproj | 1 + Source/Clima_Demo/MeadowApp.cs | 332 ++++++++++-------- Source/Clima_Demo/wifi.config.yaml | 6 +- Source/Meadow.Clima/Clima.cs | 113 +++--- Source/Meadow.Clima/ClimaHardwareBase.cs | 203 ----------- Source/Meadow.Clima/ClimaHardwareV2.cs | 154 -------- Source/Meadow.Clima/ClimaHardwareV3.cs | 246 ------------- Source/Meadow.Clima/ClimaHardwareV4.cs | 39 -- .../Meadow.Clima/Constants/CloudEventIds.cs | 7 + .../Controllers/CloudController.cs | 48 +++ .../Hardware/ClimaHardwareBase.cs | 202 +++++++++++ .../Meadow.Clima/Hardware/ClimaHardwareV2.cs | 153 ++++++++ .../Meadow.Clima/Hardware/ClimaHardwareV3.cs | 245 +++++++++++++ .../Meadow.Clima/Hardware/ClimaHardwareV4.cs | 38 ++ .../Meadow.Clima/Hardware/IClimaHardware.cs | 90 +++++ Source/Meadow.Clima/IClimaHardware.cs | 91 ----- 17 files changed, 1262 insertions(+), 937 deletions(-) create mode 100644 Source/.editorconfig delete mode 100644 Source/Meadow.Clima/ClimaHardwareBase.cs delete mode 100644 Source/Meadow.Clima/ClimaHardwareV2.cs delete mode 100644 Source/Meadow.Clima/ClimaHardwareV3.cs delete mode 100644 Source/Meadow.Clima/ClimaHardwareV4.cs create mode 100644 Source/Meadow.Clima/Constants/CloudEventIds.cs create mode 100644 Source/Meadow.Clima/Controllers/CloudController.cs create mode 100644 Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs create mode 100644 Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs create mode 100644 Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs create mode 100644 Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs create mode 100644 Source/Meadow.Clima/Hardware/IClimaHardware.cs delete mode 100644 Source/Meadow.Clima/IClimaHardware.cs diff --git a/Source/.editorconfig b/Source/.editorconfig new file mode 100644 index 0000000..460cb30 --- /dev/null +++ b/Source/.editorconfig @@ -0,0 +1,231 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = true +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = false + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true +csharp_style_var_for_built_in_types = true +csharp_style_var_when_type_is_apparent = true + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = false +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = file_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = false + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = false +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/Source/Clima_Demo/Clima_Demo.csproj b/Source/Clima_Demo/Clima_Demo.csproj index 662734d..0907a6f 100644 --- a/Source/Clima_Demo/Clima_Demo.csproj +++ b/Source/Clima_Demo/Clima_Demo.csproj @@ -4,6 +4,7 @@ true Library App + 10 diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index df7b5d6..d35c65e 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -1,228 +1,272 @@ using Meadow; using Meadow.Devices; +using Meadow.Hardware; using Meadow.Peripherals.Sensors.Location.Gnss; using Meadow.Units; using System; +using System.Collections.Generic; using System.Threading.Tasks; -namespace Clima_Demo +namespace Clima_Demo; + +public class MeadowApp : App { - // Change F7FeatherV2 to F7FeatherV1 for V1.x boards - public class MeadowApp : App - { - IClimaHardware clima; + private IClimaHardware clima; + private NotificationController notificationController; - public override Task Initialize() - { - Resolver.Log.LogLevel = Meadow.Logging.LogLevel.Information; + public MeadowApp() + { + Resolver.Services.Add(new CloudController()); + } - Resolver.Log.Info("Initialize hardware..."); + public override void OnBootFromCrash(IEnumerable crashReports) + { + Resolver.Services.Get()?.LogEvent(CloudEventIds.BootFromCrash, "Device restarted after a crash"); + } - clima = Clima.Create(); - clima.RgbLed.SetColor(Color.Red); + public override Task Initialize() + { + Resolver.Log.LogLevel = Meadow.Logging.LogLevel.Information; - Resolver.Log.Info($"Running on Clima Hardware {clima.RevisionString}"); + Resolver.Log.Info("Initialize hardware..."); - if (clima.TemperatureSensor is { } temperatureSensor) - { - temperatureSensor.Updated += TemperatureUpdated; - } + clima = Clima.Create(); - if (clima.BarometricPressureSensor is { } pressureSensor) - { - pressureSensor.Updated += PressureUpdated; - } + notificationController = new NotificationController(clima.RgbLed); + Resolver.Services.Add(notificationController); - if (clima.HumiditySensor is { } humiditySensor) - { - humiditySensor.Updated += HumidityUpdated; - } + notificationController.Starting(); - if (clima.CO2ConcentrationSensor is { } co2Sensor) - { - co2Sensor.Updated += Co2Updated; - } + Resolver.Services.Get()?.LogEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {clima.RevisionString})"); + Resolver.Log.Info($"Running on Clima Hardware {clima.RevisionString}"); - if (clima.WindVane is { } windVane) - { - windVane.Updated += WindvaneUpdated; - } + InitializeSensors(); - if (clima.RainGauge is { } rainGuage) - { - rainGuage.Updated += RainGuageUpdated; - } + Resolver.Log.Info("Initialization complete"); - if (clima.Anemometer is { } anemometer) - { - anemometer.Updated += AnemometerUpdated; - } + clima.RgbLed.SetColor(Color.Yellow); - if (clima.SolarVoltageInput is { } solarVoltage) - { - solarVoltage.Updated += SolarVoltageUpdated; - } + var wifi = Device.NetworkAdapters.Primary(); + wifi.NetworkConnected += OnNetworkConnected; + wifi.NetworkDisconnected += OnNetworkDisconnected; - if (clima.BatteryVoltageInput is { } batteryVoltage) - { - batteryVoltage.Updated += BatteryVoltageUpdated; - } + if (wifi.IsConnected) + { + notificationController.NetworkConnected(); + } + else + { + notificationController.NetworkDisconnected(); + } - if (clima.Gnss is { } gnss) - { - //gnss.GsaReceived += GnssGsaReceived; - //gnss.GsvReceived += GnssGsvReceived; - //gnss.VtgReceived += GnssVtgReceived; - gnss.RmcReceived += GnssRmcReceived; - gnss.GllReceived += GnssGllReceived; - } + return Task.CompletedTask; + } - Resolver.Log.Info("Initialization complete"); + private void OnNetworkDisconnected(INetworkAdapter sender, NetworkDisconnectionEventArgs args) + { + notificationController.NetworkDisconnected(); + } - clima.RgbLed.SetColor(Color.Green); + private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventArgs args) + { + notificationController.NetworkConnected(); + } - return base.Initialize(); + private void InitializeSensors() + { + if (clima.TemperatureSensor is { } temperatureSensor) + { + temperatureSensor.Updated += TemperatureUpdated; } - private void GnssGsaReceived(object _, ActiveSatellites e) + if (clima.BarometricPressureSensor is { } pressureSensor) { - if (e.SatellitesUsedForFix is { } sats) - { - Resolver.Log.Info($"Number of active satellites: {sats.Length}"); - } + pressureSensor.Updated += PressureUpdated; } - private void GnssGsvReceived(object _, SatellitesInView e) + if (clima.HumiditySensor is { } humiditySensor) { - Resolver.Log.Info($"Satellites in view: {e.Satellites.Length}"); + humiditySensor.Updated += HumidityUpdated; } - private void GnssVtgReceived(object _, CourseOverGround e) + if (clima.CO2ConcentrationSensor is { } co2Sensor) { - if (e is { } cv) - { - Resolver.Log.Info($"{cv}"); - }; + co2Sensor.Updated += Co2Updated; } - private void GnssRmcReceived(object _, GnssPositionInfo e) + if (clima.WindVane is { } windVane) { - if (e.Valid) - { - Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); - } + windVane.Updated += WindvaneUpdated; } - private void GnssGllReceived(object _, GnssPositionInfo e) + if (clima.RainGauge is { } rainGuage) { - if (e.Valid) - { - Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); - } + rainGuage.Updated += RainGuageUpdated; } - public override Task Run() + if (clima.Anemometer is { } anemometer) { - Resolver.Log.Info("Run..."); - - var updateInterval = TimeSpan.FromSeconds(5); + anemometer.Updated += AnemometerUpdated; + } - if (clima.TemperatureSensor is { } temp) - { - temp.StartUpdating(updateInterval); - } + if (clima.SolarVoltageInput is { } solarVoltage) + { + solarVoltage.Updated += SolarVoltageUpdated; + } - if (clima.HumiditySensor is { } humidity) - { - humidity.StartUpdating(updateInterval); - } + if (clima.BatteryVoltageInput is { } batteryVoltage) + { + batteryVoltage.Updated += BatteryVoltageUpdated; + } - if (clima.BarometricPressureSensor is { } pressure) - { - pressure.StartUpdating(updateInterval); - } + if (clima.Gnss is { } gnss) + { + //gnss.GsaReceived += GnssGsaReceived; + //gnss.GsvReceived += GnssGsvReceived; + //gnss.VtgReceived += GnssVtgReceived; + gnss.RmcReceived += GnssRmcReceived; + gnss.GllReceived += GnssGllReceived; + } + } - if (clima.CO2ConcentrationSensor is { } co2) - { - co2.StartUpdating(updateInterval); - } + private void GnssGsaReceived(object _, ActiveSatellites e) + { + if (e.SatellitesUsedForFix is { } sats) + { + Resolver.Log.Info($"Number of active satellites: {sats.Length}"); + } + } - if (clima.WindVane is { } windVane) - { - windVane.StartUpdating(updateInterval); - } + private void GnssGsvReceived(object _, SatellitesInView e) + { + Resolver.Log.Info($"Satellites in view: {e.Satellites.Length}"); + } - if (clima.RainGauge is { } rainGuage) - { - rainGuage.StartUpdating(updateInterval); - } + private void GnssVtgReceived(object _, CourseOverGround e) + { + if (e is { } cv) + { + Resolver.Log.Info($"{cv}"); + }; + } - if (clima.Anemometer is { } anemometer) - { - anemometer.StartUpdating(updateInterval); - } + private void GnssRmcReceived(object _, GnssPositionInfo e) + { + if (e.Valid) + { + Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + } + } - if (clima.SolarVoltageInput is { } solarVoltage) - { - solarVoltage.StartUpdating(updateInterval); - } + private void GnssGllReceived(object _, GnssPositionInfo e) + { + if (e.Valid) + { + Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + } + } - if (clima.BatteryVoltageInput is { } batteryVoltage) - { - batteryVoltage.StartUpdating(updateInterval); - } + public override Task Run() + { + Resolver.Log.Info("Run..."); - if (clima.Gnss is { } gnss) - { - gnss.StartUpdating(); - } + var updateInterval = TimeSpan.FromSeconds(5); - return base.Run(); + if (clima.TemperatureSensor is { } temp) + { + temp.StartUpdating(updateInterval); } - private void TemperatureUpdated(object sender, IChangeResult e) + if (clima.HumiditySensor is { } humidity) { - Resolver.Log.Info($"Temperature: {e.New.Celsius:0.#}C"); + humidity.StartUpdating(updateInterval); } - private void PressureUpdated(object sender, IChangeResult e) + if (clima.BarometricPressureSensor is { } pressure) { - Resolver.Log.Info($"Pressure: {e.New.Millibar:0.#}mbar"); + pressure.StartUpdating(updateInterval); } - private void HumidityUpdated(object sender, IChangeResult e) + if (clima.CO2ConcentrationSensor is { } co2) { - Resolver.Log.Info($"Humidity: {e.New.Percent:0.#}%"); + co2.StartUpdating(updateInterval); } - private void Co2Updated(object sender, IChangeResult e) + if (clima.WindVane is { } windVane) { - Resolver.Log.Info($"CO2: {e.New.PartsPerMillion:0.#}ppm"); + windVane.StartUpdating(updateInterval); } - private void SolarVoltageUpdated(object sender, IChangeResult e) + if (clima.RainGauge is { } rainGuage) { - Resolver.Log.Info($"Solar Voltage: {e.New.Volts:0.#} volts"); + rainGuage.StartUpdating(updateInterval); } - private void BatteryVoltageUpdated(object sender, IChangeResult e) + if (clima.Anemometer is { } anemometer) { - Resolver.Log.Info($"Battery Voltage: {e.New.Volts:0.#} volts"); + anemometer.StartUpdating(updateInterval); } - private void AnemometerUpdated(object sender, IChangeResult e) + if (clima.SolarVoltageInput is { } solarVoltage) { - Resolver.Log.Info($"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); + solarVoltage.StartUpdating(updateInterval); } - private void RainGuageUpdated(object sender, IChangeResult e) + if (clima.BatteryVoltageInput is { } batteryVoltage) { - Resolver.Log.Info($"Rain Gauge: {e.New.Millimeters:0.#} mm"); + batteryVoltage.StartUpdating(updateInterval); } - private void WindvaneUpdated(object sender, IChangeResult e) + if (clima.Gnss is { } gnss) { - Resolver.Log.Info($"Wind Vane: {e.New.Compass16PointCardinalName} ({e.New.Radians:0.#} radians)"); + gnss.StartUpdating(); } + + return base.Run(); + } + + private void TemperatureUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Temperature: {e.New.Celsius:0.#}C"); + } + + private void PressureUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Pressure: {e.New.Millibar:0.#}mbar"); + } + + private void HumidityUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Humidity: {e.New.Percent:0.#}%"); + } + + private void Co2Updated(object sender, IChangeResult e) + { + Resolver.Log.Info($"CO2: {e.New.PartsPerMillion:0.#}ppm"); + } + + private void SolarVoltageUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Solar Voltage: {e.New.Volts:0.#} volts"); + } + + private void BatteryVoltageUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Battery Voltage: {e.New.Volts:0.#} volts"); + } + + private void AnemometerUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); + } + + private void RainGuageUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Rain Gauge: {e.New.Millimeters:0.#} mm"); + } + + private void WindvaneUpdated(object sender, IChangeResult e) + { + Resolver.Log.Info($"Wind Vane: {e.New.Compass16PointCardinalName} ({e.New.Radians:0.#} radians)"); } } \ No newline at end of file diff --git a/Source/Clima_Demo/wifi.config.yaml b/Source/Clima_Demo/wifi.config.yaml index f07bfaa..fc7a427 100644 --- a/Source/Clima_Demo/wifi.config.yaml +++ b/Source/Clima_Demo/wifi.config.yaml @@ -3,6 +3,6 @@ # http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/WiFi_Configuration/ # # To enable automatically connecting to a default network, make sure to enable the Coprocessor > AutomaticallyStartNetwork value in meadow.config.yaml. -# Credentials: -# Ssid: YourSSID -# Password: SSIDPassword + Credentials: + Ssid: interwebs + Password: 1234567890 diff --git a/Source/Meadow.Clima/Clima.cs b/Source/Meadow.Clima/Clima.cs index 9c61bb9..eb35032 100644 --- a/Source/Meadow.Clima/Clima.cs +++ b/Source/Meadow.Clima/Clima.cs @@ -3,83 +3,82 @@ using Meadow.Logging; using System; -namespace Meadow.Devices +namespace Meadow.Devices; + +/// +/// Represents the Clima hardware +/// +public class Clima { + private Clima() { } + /// - /// Represents the Clima hardware + /// Create an instance of the Clima class /// - public class Clima + /// + /// + public static IClimaHardware Create() { - private Clima() { } + IClimaHardware hardware; + Logger? logger = Resolver.Log; + II2cBus i2cBus; - /// - /// Create an instance of the Clima class - /// - /// - /// - public static IClimaHardware Create() - { - IClimaHardware hardware; - Logger? logger = Resolver.Log; - II2cBus i2cBus; + logger?.Debug("Initializing Clima..."); - logger?.Debug("Initializing Clima..."); + var device = Resolver.Device; - var device = Resolver.Device; + if (Resolver.Device == null) + { + var msg = "Clima instance must be created no earlier than App.Initialize()"; + logger?.Error(msg); + throw new Exception(msg); + } - if (Resolver.Device == null) - { - var msg = "Clima instance must be created no earlier than App.Initialize()"; - logger?.Error(msg); - throw new Exception(msg); - } + i2cBus = device.CreateI2cBus(); - i2cBus = device.CreateI2cBus(); + logger?.Debug("I2C Bus instantiated"); - logger?.Debug("I2C Bus instantiated"); + if (device is IF7FeatherMeadowDevice { } feather) + { + logger?.Info("Instantiating Clima v2 specific hardware"); + hardware = new ClimaHardwareV2(feather, i2cBus); + } + else if (device is IF7CoreComputeMeadowDevice { } ccm) + { + Mcp23008? mcpVersion = null; + byte version = 0; - if (device is IF7FeatherMeadowDevice { } feather) + try { - logger?.Info("Instantiating Clima v2 specific hardware"); - hardware = new ClimaHardwareV2(feather, i2cBus); - } - else if (device is IF7CoreComputeMeadowDevice { } ccm) - { - Mcp23008? mcpVersion = null; - byte version = 0; - - try - { - logger?.Info("Instantiating version MCP23008"); + logger?.Info("Instantiating version MCP23008"); - var resetPort = ccm.Pins.D02.CreateDigitalOutputPort(); + var resetPort = ccm.Pins.D02.CreateDigitalOutputPort(); - mcpVersion = new Mcp23008(i2cBus, address: 0x27, resetPort: resetPort); + mcpVersion = new Mcp23008(i2cBus, address: 0x27, resetPort: resetPort); - version = mcpVersion.ReadFromPorts(); - } - catch - { - logger?.Info("Failed to instantiate version MCP23008"); - } + version = mcpVersion.ReadFromPorts(); + } + catch + { + logger?.Info("Failed to instantiate version MCP23008"); + } - if (version > 4) - { - logger?.Info("Instantiating Clima v4 specific hardware"); - hardware = new ClimaHardwareV4(ccm, i2cBus, mcpVersion!); - } - else - { - logger?.Info("Instantiating Clima v3 specific hardware"); - hardware = new ClimaHardwareV3(ccm, i2cBus, mcpVersion!); - } + if (version > 4) + { + logger?.Info("Instantiating Clima v4 specific hardware"); + hardware = new ClimaHardwareV4(ccm, i2cBus, mcpVersion!); } else { - throw new NotSupportedException(); + logger?.Info("Instantiating Clima v3 specific hardware"); + hardware = new ClimaHardwareV3(ccm, i2cBus, mcpVersion!); } - - return hardware; } + else + { + throw new NotSupportedException(); + } + + return hardware; } } \ No newline at end of file diff --git a/Source/Meadow.Clima/ClimaHardwareBase.cs b/Source/Meadow.Clima/ClimaHardwareBase.cs deleted file mode 100644 index b7c9834..0000000 --- a/Source/Meadow.Clima/ClimaHardwareBase.cs +++ /dev/null @@ -1,203 +0,0 @@ -using Meadow.Foundation.Sensors.Atmospheric; -using Meadow.Foundation.Sensors.Gnss; -using Meadow.Hardware; -using Meadow.Logging; -using Meadow.Peripherals.Leds; -using Meadow.Peripherals.Sensors; -using Meadow.Peripherals.Sensors.Atmospheric; -using Meadow.Peripherals.Sensors.Environmental; -using Meadow.Peripherals.Sensors.Weather; -using System; - -namespace Meadow.Devices -{ - /// - /// Contains common elements of Clima hardware - /// - public abstract class ClimaHardwareBase : IClimaHardware - { - private IConnector?[]? _connectors; - private Bme688? _atmosphericSensor; - private ITemperatureSensor? _temperatureSensor; - private IHumiditySensor? _humiditySensor; - private IBarometricPressureSensor? _barometricPressureSensor; - private IGasResistanceSensor? _gasResistanceSensor; - internal IWindVane? _windVane; - internal IRainGauge? _rainGauge; - internal IAnemometer? _anemometer; - internal IRgbPwmLed? _rgbLed; - internal NeoM8? _gnss; - - /// - /// Get a reference to Meadow Logger - /// - protected Logger? Logger { get; } = Resolver.Log; - - /// - public abstract II2cBus I2cBus { get; } - - /// - public Bme688? AtmosphericSensor => GetAtmosphericSensor(); - - /// - public ITemperatureSensor? TemperatureSensor => GetTemperatureSensor(); - - /// - public IHumiditySensor? HumiditySensor => GetHumiditySensor(); - - /// - public IBarometricPressureSensor? BarometricPressureSensor => GetBarometricPressureSensor(); - - /// - public IGasResistanceSensor? GasResistanceSensor => GetGasResistanceSensor(); - - /// - public virtual ICO2ConcentrationSensor? CO2ConcentrationSensor => throw new NotImplementedException(); - - /// - public IWindVane? WindVane => GetWindVane(); - - /// - public IRainGauge? RainGauge => GetRainGauge(); - - /// - public IAnemometer? Anemometer => GetAnemometer(); - - /// - public IAnalogInputPort? SolarVoltageInput { get; protected set; } - - /// - public IAnalogInputPort? BatteryVoltageInput { get; protected set; } - - /// - public IRgbPwmLed? RgbLed => GetRgbPwmLed(); - - /// - public abstract string RevisionString { get; } - - /// - /// The Neo GNSS sensor - /// - public NeoM8? Gnss => GetNeoM8(); - - /// - public I2cConnector? Qwiic => (I2cConnector?)Connectors[0]; - - internal virtual I2cConnector? CreateQwiicConnector() - { - return null; - } - - /// - /// Collection of connectors on the Clima board - /// - public IConnector?[] Connectors - { - get - { - if (_connectors == null) - { - _connectors = new IConnector[1]; - _connectors[0] = CreateQwiicConnector(); - } - - return _connectors; - } - } - - private Bme688? GetAtmosphericSensor() - { - if (_atmosphericSensor == null) - { - InitializeBme688(); - } - - return _atmosphericSensor; - } - - private ITemperatureSensor? GetTemperatureSensor() - { - if (_temperatureSensor == null) - { - InitializeBme688(); - } - - return _temperatureSensor; - } - - private IHumiditySensor? GetHumiditySensor() - { - if (_humiditySensor == null) - { - InitializeBme688(); - } - - return _humiditySensor; - } - - private IBarometricPressureSensor? GetBarometricPressureSensor() - { - if (_barometricPressureSensor == null) - { - InitializeBme688(); - } - - return _barometricPressureSensor; - } - - private IGasResistanceSensor? GetGasResistanceSensor() - { - if (_gasResistanceSensor == null) - { - InitializeBme688(); - } - - return _gasResistanceSensor; - } - - /// - /// Get the Wind Vane on the Clima board - /// - protected abstract IWindVane? GetWindVane(); - - /// - /// Get the Rain Gauge on the Clima board - /// - protected abstract IRainGauge? GetRainGauge(); - - /// - /// Get the Anemometer on the Clima board - /// - protected abstract IAnemometer? GetAnemometer(); - - /// - /// Get the RGB LED on the Clima board - /// - protected abstract IRgbPwmLed? GetRgbPwmLed(); - - /// - /// Get the Neo GNSS sensor - /// - protected abstract NeoM8? GetNeoM8(); - - private void InitializeBme688() - { - try - { - Logger?.Trace("Instantiating atmospheric sensor"); - var bme = new Bme688(I2cBus, (byte)Bme68x.Addresses.Address_0x76); - _atmosphericSensor = bme; - _temperatureSensor = bme; - _humiditySensor = bme; - _barometricPressureSensor = bme; - _gasResistanceSensor = bme; - Resolver.SensorService.RegisterSensor(_atmosphericSensor); - Logger?.Trace("Atmospheric sensor up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the BME688 atmospheric sensor: {ex.Message}"); - } - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Clima/ClimaHardwareV2.cs b/Source/Meadow.Clima/ClimaHardwareV2.cs deleted file mode 100644 index 7263cc3..0000000 --- a/Source/Meadow.Clima/ClimaHardwareV2.cs +++ /dev/null @@ -1,154 +0,0 @@ -using Meadow.Foundation.Leds; -using Meadow.Foundation.Sensors.Gnss; -using Meadow.Foundation.Sensors.Weather; -using Meadow.Hardware; -using Meadow.Peripherals.Leds; -using Meadow.Peripherals.Sensors.Weather; -using System; - -namespace Meadow.Devices -{ - /// - /// Represents the Clima v2.x hardware - /// - public class ClimaHardwareV2 : ClimaHardwareBase - { - private readonly IF7FeatherMeadowDevice _device; - - /// - public sealed override II2cBus I2cBus { get; } - - /// - public override string RevisionString => "v2.x"; - - /// - /// Create a new ClimaHardwareV2 object - /// - /// The meadow device - /// The I2C bus - public ClimaHardwareV2(IF7FeatherMeadowDevice device, II2cBus i2cBus) - { - _device = device; - - I2cBus = i2cBus; - - // See hack in Meadow.Core\source\implementations\f7\Meadow.F7\Devices\DeviceChannelManager.cs - // Must initialise any PWM based I/O first - GetRgbPwmLed(); - - try - { - Logger?.Trace("Instantiating Solar Voltage Input"); - SolarVoltageInput = device.Pins.A02.CreateAnalogInputPort(5); - Logger?.Trace("Solar Voltage Input up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Solar Voltage Input: {ex.Message}"); - } - } - - /// - protected override IRgbPwmLed? GetRgbPwmLed() - { - if (_rgbLed == null) - { - try - { - Logger?.Trace("Instantiating RGB LED"); - _rgbLed = new RgbPwmLed( - redPwmPin: _device.Pins.OnboardLedRed, - greenPwmPin: _device.Pins.OnboardLedGreen, - bluePwmPin: _device.Pins.OnboardLedBlue, - CommonType.CommonAnode); - Logger?.Trace("RGB LED up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); - } - } - - return _rgbLed; - } - - /// - protected override NeoM8? GetNeoM8() - { - if (_gnss == null) - { - try - { - Logger?.Trace("Instantiating GNSS"); - _gnss = new NeoM8(_device, _device.PlatformOS.GetSerialPortName("COM4")!, null, null); - Logger?.Trace("GNSS initialized"); - } - catch (Exception e) - { - Logger?.Error($"Err initializing GNSS: {e.Message}"); - } - } - return _gnss; - } - - /// - protected override IWindVane? GetWindVane() - { - if (_windVane == null) - { - try - { - Logger?.Trace("Instantiating Wind Vane"); - _windVane = new WindVane(_device.Pins.A00); - Resolver.SensorService.RegisterSensor(_windVane); - Logger?.Trace("Wind Vane up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Wind Vane: {ex.Message}"); - } - } - return _windVane; - } - - /// - protected override IRainGauge? GetRainGauge() - { - if (_rainGauge == null) - { - try - { - Logger?.Trace("Instantiating Rain Gauge"); - _rainGauge = new SwitchingRainGauge(_device.Pins.D11); - Resolver.SensorService.RegisterSensor(_rainGauge); - Logger?.Trace("Rain Gauge up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Rain Gauge: {ex.Message}"); - } - } - return _rainGauge; - } - - /// - protected override IAnemometer? GetAnemometer() - { - if (_anemometer == null) - { - try - { - Logger?.Trace("Instantiating Anemometer"); - _anemometer = new SwitchingAnemometer(_device.Pins.A01); - Resolver.SensorService.RegisterSensor(_anemometer); - Logger?.Trace("Anemometer up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Anemometer: {ex.Message}"); - } - } - return _anemometer; - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Clima/ClimaHardwareV3.cs b/Source/Meadow.Clima/ClimaHardwareV3.cs deleted file mode 100644 index 487edfd..0000000 --- a/Source/Meadow.Clima/ClimaHardwareV3.cs +++ /dev/null @@ -1,246 +0,0 @@ -using Meadow.Foundation.ICs.IOExpanders; -using Meadow.Foundation.Leds; -using Meadow.Foundation.Sensors.Environmental; -using Meadow.Foundation.Sensors.Gnss; -using Meadow.Foundation.Sensors.Weather; -using Meadow.Hardware; -using Meadow.Peripherals.Leds; -using Meadow.Peripherals.Sensors.Environmental; -using Meadow.Peripherals.Sensors.Weather; -using System; - -namespace Meadow.Devices -{ - /// - /// Represents the Clima v3.x hardware - /// - public class ClimaHardwareV3 : ClimaHardwareBase - { - /// - /// The Meadow CCM device - /// - protected readonly IF7CoreComputeMeadowDevice _device; - - private Scd40? _environmentalSensor; - private ICO2ConcentrationSensor? _co2ConcentrationSensor; - - /// - public sealed override II2cBus I2cBus { get; } - - /// - /// The SCD40 environmental sensor on the Clima board - /// - public Scd40? EnvironmentalSensor => GetEnvironmentalSensor(); - - /// - public override ICO2ConcentrationSensor? CO2ConcentrationSensor => GetCO2ConcentrationSensor(); - - /// - /// The MCP23008 IO expander that contains the Clima hardware version - /// - public Mcp23008 McpVersion { get; protected set; } - - /// - public override string RevisionString => "v3.x"; - - /// - /// Analog inputs to measure Solar voltage has Resistor Divider with R1 = 1000 Ohm, R2 = 680 Ohm - /// Measured analogue voltage needs to be scaled to RETURN actual input voltage - /// Input Voltage = AIN / Resistor Divider - /// - protected const double SolarVoltageResistorDivider = 680.0 / (1000.0 + 680.0); - - /// - /// Analog inputs to measure Solar voltage has Resistor Divider with R1 = 1000 Ohm, R2 = 680 Ohm - /// Measured analogue voltage needs to be scaled to RETURN actual input voltage - /// Input Voltage = AIN / Resistor Divider - /// - protected const double BatteryVoltageResistorDivider = 2000.0 / (1000.0 + 2000.0); - - /// - /// Create a new ClimaHardwareV3 object - /// - /// The meadow device - /// The I2C bus - /// The Mcp23008 used to read version information - public ClimaHardwareV3(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) - { - McpVersion = mcpVersion; - - _device = device; - - I2cBus = i2cBus; - - // See hack in Meadow.Core\source\implementations\f7\Meadow.F7\Devices\DeviceChannelManager.cs - // Must initialise any PWM based I/O first - GetRgbPwmLed(); - - try - { - Logger?.Trace("Instantiating Solar Voltage Input"); - SolarVoltageInput = device.Pins.A02.CreateAnalogInputPort(5, - AnalogInputPort.DefaultSampleInterval, - AnalogInputPort.DefaultReferenceVoltage / SolarVoltageResistorDivider); - Logger?.Trace("Solar Voltage Input up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Solar Voltage Input: {ex.Message}"); - } - - try - { - Logger?.Trace("Instantiating Battery Voltage Input"); - BatteryVoltageInput = device.Pins.A04.CreateAnalogInputPort(5, - AnalogInputPort.DefaultSampleInterval, - AnalogInputPort.DefaultReferenceVoltage / BatteryVoltageResistorDivider); - - Logger?.Trace("Battery Voltage Input up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Battery Voltage Input: {ex.Message}"); - } - } - - private Scd40? GetEnvironmentalSensor() - { - if (_environmentalSensor == null) - { - InitializeScd40(); - } - - return _environmentalSensor; - } - - private ICO2ConcentrationSensor? GetCO2ConcentrationSensor() - { - if (_co2ConcentrationSensor == null) - { - InitializeScd40(); - } - - return _co2ConcentrationSensor; - } - - private void InitializeScd40() - { - try - { - Logger?.Trace("Instantiating environmental sensor"); - var scd = new Scd40(I2cBus, (byte)Scd40.Addresses.Default); - _co2ConcentrationSensor = scd; - _environmentalSensor = scd; - Resolver.SensorService.RegisterSensor(scd); - Logger?.Trace("Environmental sensor up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the SCD40 Environmental Sensor: {ex.Message}"); - } - } - - /// - protected override IRgbPwmLed? GetRgbPwmLed() - { - if (_rgbLed == null) - { - try - { - Logger?.Trace("Instantiating RGB LED"); - _rgbLed = new RgbPwmLed( - redPwmPin: _device.Pins.D09, - greenPwmPin: _device.Pins.D10, - bluePwmPin: _device.Pins.D11, - CommonType.CommonAnode); - Logger?.Trace("RGB LED up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); - } - } - - return _rgbLed; - } - - /// - protected override NeoM8? GetNeoM8() - { - if (_gnss == null) - { - try - { - Logger?.Trace("Instantiating GNSS"); - _gnss = new NeoM8(_device, _device.PlatformOS.GetSerialPortName("COM4")!, _device.Pins.D05, _device.Pins.A03); - Logger?.Trace("GNSS initialized"); - } - catch (Exception e) - { - Logger?.Error($"Err initializing GNSS: {e.Message}"); - } - } - return _gnss; - } - - /// - protected override IWindVane? GetWindVane() - { - if (_windVane == null) - { - try - { - Logger?.Trace("Instantiating Wind Vane"); - _windVane = new WindVane(_device.Pins.A00); - Resolver.SensorService.RegisterSensor(_windVane); - Logger?.Trace("Wind Vane up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Wind Vane: {ex.Message}"); - } - } - return _windVane; - } - - /// - protected override IRainGauge? GetRainGauge() - { - if (_rainGauge == null) - { - try - { - Logger?.Trace("Instantiating Rain Gauge"); - _rainGauge = new SwitchingRainGauge(_device.Pins.D16); - Resolver.SensorService.RegisterSensor(_rainGauge); - Logger?.Trace("Rain Gauge up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Rain Gauge: {ex.Message}"); - } - } - return _rainGauge; - } - - /// - protected override IAnemometer? GetAnemometer() - { - if (_anemometer == null) - { - try - { - Logger?.Trace("Instantiating Anemometer"); - _anemometer = new SwitchingAnemometer(_device.Pins.A01); - Resolver.SensorService.RegisterSensor(_anemometer); - Logger?.Trace("Anemometer up"); - } - catch (Exception ex) - { - Logger?.Error($"Unable to create the Anemometer: {ex.Message}"); - } - } - return _anemometer; - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Clima/ClimaHardwareV4.cs b/Source/Meadow.Clima/ClimaHardwareV4.cs deleted file mode 100644 index 25baeda..0000000 --- a/Source/Meadow.Clima/ClimaHardwareV4.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Meadow.Foundation.ICs.IOExpanders; -using Meadow.Hardware; - -namespace Meadow.Devices -{ - /// - /// Represents the Clima v4.x hardware - /// - public class ClimaHardwareV4 : ClimaHardwareV3 - { - /// - public override string RevisionString => "v4.x"; - - /// - /// Create a new ClimaHardwareV4 object - /// - /// The meadow device - /// The I2C bus - /// The Mcp23008 used to read version information - public ClimaHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) - : base(device, i2cBus, mcpVersion) - { - } - - internal override I2cConnector? CreateQwiicConnector() - { - Logger?.Trace("Creating Qwiic I2C connector"); - - return new I2cConnector( - nameof(Qwiic), - new PinMapping - { - new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.I2C1_SCL), - new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.I2C1_SDA), - }, - new I2cBusMapping(_device, 1)); - } - } -} \ No newline at end of file diff --git a/Source/Meadow.Clima/Constants/CloudEventIds.cs b/Source/Meadow.Clima/Constants/CloudEventIds.cs new file mode 100644 index 0000000..ff15aba --- /dev/null +++ b/Source/Meadow.Clima/Constants/CloudEventIds.cs @@ -0,0 +1,7 @@ +namespace Clima_Demo; + +public enum CloudEventIds +{ + DeviceStarted = 100, + BootFromCrash = 200 +} diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs new file mode 100644 index 0000000..4f086f8 --- /dev/null +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -0,0 +1,48 @@ +using Meadow; +using Meadow.Cloud; +using Meadow.Peripherals.Leds; +using System; + +namespace Clima_Demo; + +public class NotificationController +{ + private readonly IRgbPwmLed? rgbLed; + + public NotificationController(IRgbPwmLed? rgbLed) + { + this.rgbLed = rgbLed; + } + + public void Starting() + { + rgbLed?.SetColor(RgbLedColors.Red); + } + + public void NetworkConnected() + { + rgbLed?.SetColor(RgbLedColors.Green); + } + + public void NetworkDisconnected() + { + rgbLed?.SetColor(RgbLedColors.Yellow); + } +} + +public class CloudController +{ + public void LogEvent(CloudEventIds eventId, string message) + { + if (Resolver.MeadowCloudService == null + || !Resolver.MeadowCloudService.IsEnabled) return; + + Resolver.MeadowCloudService.SendEvent( + new CloudEvent + { + EventId = (int)eventId, + Description = message, + Timestamp = DateTime.UtcNow, + }); + } +} diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs new file mode 100644 index 0000000..6105568 --- /dev/null +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareBase.cs @@ -0,0 +1,202 @@ +using Meadow.Foundation.Sensors.Atmospheric; +using Meadow.Foundation.Sensors.Gnss; +using Meadow.Hardware; +using Meadow.Logging; +using Meadow.Peripherals.Leds; +using Meadow.Peripherals.Sensors; +using Meadow.Peripherals.Sensors.Atmospheric; +using Meadow.Peripherals.Sensors.Environmental; +using Meadow.Peripherals.Sensors.Weather; +using System; + +namespace Meadow.Devices; + +/// +/// Contains common elements of Clima hardware +/// +public abstract class ClimaHardwareBase : IClimaHardware +{ + private IConnector?[]? _connectors; + private Bme688? _atmosphericSensor; + private ITemperatureSensor? _temperatureSensor; + private IHumiditySensor? _humiditySensor; + private IBarometricPressureSensor? _barometricPressureSensor; + private IGasResistanceSensor? _gasResistanceSensor; + internal IWindVane? _windVane; + internal IRainGauge? _rainGauge; + internal IAnemometer? _anemometer; + internal IRgbPwmLed? _rgbLed; + internal NeoM8? _gnss; + + /// + /// Get a reference to Meadow Logger + /// + protected Logger? Logger { get; } = Resolver.Log; + + /// + public abstract II2cBus I2cBus { get; } + + /// + public Bme688? AtmosphericSensor => GetAtmosphericSensor(); + + /// + public ITemperatureSensor? TemperatureSensor => GetTemperatureSensor(); + + /// + public IHumiditySensor? HumiditySensor => GetHumiditySensor(); + + /// + public IBarometricPressureSensor? BarometricPressureSensor => GetBarometricPressureSensor(); + + /// + public IGasResistanceSensor? GasResistanceSensor => GetGasResistanceSensor(); + + /// + public virtual ICO2ConcentrationSensor? CO2ConcentrationSensor => throw new NotImplementedException(); + + /// + public IWindVane? WindVane => GetWindVane(); + + /// + public IRainGauge? RainGauge => GetRainGauge(); + + /// + public IAnemometer? Anemometer => GetAnemometer(); + + /// + public IAnalogInputPort? SolarVoltageInput { get; protected set; } + + /// + public IAnalogInputPort? BatteryVoltageInput { get; protected set; } + + /// + public IRgbPwmLed? RgbLed => GetRgbPwmLed(); + + /// + public abstract string RevisionString { get; } + + /// + /// The Neo GNSS sensor + /// + public NeoM8? Gnss => GetNeoM8(); + + /// + public I2cConnector? Qwiic => (I2cConnector?)Connectors[0]; + + internal virtual I2cConnector? CreateQwiicConnector() + { + return null; + } + + /// + /// Collection of connectors on the Clima board + /// + public IConnector?[] Connectors + { + get + { + if (_connectors == null) + { + _connectors = new IConnector[1]; + _connectors[0] = CreateQwiicConnector(); + } + + return _connectors; + } + } + + private Bme688? GetAtmosphericSensor() + { + if (_atmosphericSensor == null) + { + InitializeBme688(); + } + + return _atmosphericSensor; + } + + private ITemperatureSensor? GetTemperatureSensor() + { + if (_temperatureSensor == null) + { + InitializeBme688(); + } + + return _temperatureSensor; + } + + private IHumiditySensor? GetHumiditySensor() + { + if (_humiditySensor == null) + { + InitializeBme688(); + } + + return _humiditySensor; + } + + private IBarometricPressureSensor? GetBarometricPressureSensor() + { + if (_barometricPressureSensor == null) + { + InitializeBme688(); + } + + return _barometricPressureSensor; + } + + private IGasResistanceSensor? GetGasResistanceSensor() + { + if (_gasResistanceSensor == null) + { + InitializeBme688(); + } + + return _gasResistanceSensor; + } + + /// + /// Get the Wind Vane on the Clima board + /// + protected abstract IWindVane? GetWindVane(); + + /// + /// Get the Rain Gauge on the Clima board + /// + protected abstract IRainGauge? GetRainGauge(); + + /// + /// Get the Anemometer on the Clima board + /// + protected abstract IAnemometer? GetAnemometer(); + + /// + /// Get the RGB LED on the Clima board + /// + protected abstract IRgbPwmLed? GetRgbPwmLed(); + + /// + /// Get the Neo GNSS sensor + /// + protected abstract NeoM8? GetNeoM8(); + + private void InitializeBme688() + { + try + { + Logger?.Trace("Instantiating atmospheric sensor"); + var bme = new Bme688(I2cBus, (byte)Bme68x.Addresses.Address_0x76); + _atmosphericSensor = bme; + _temperatureSensor = bme; + _humiditySensor = bme; + _barometricPressureSensor = bme; + _gasResistanceSensor = bme; + Resolver.SensorService.RegisterSensor(_atmosphericSensor); + Logger?.Trace("Atmospheric sensor up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the BME688 atmospheric sensor: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs new file mode 100644 index 0000000..15f90af --- /dev/null +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV2.cs @@ -0,0 +1,153 @@ +using Meadow.Foundation.Leds; +using Meadow.Foundation.Sensors.Gnss; +using Meadow.Foundation.Sensors.Weather; +using Meadow.Hardware; +using Meadow.Peripherals.Leds; +using Meadow.Peripherals.Sensors.Weather; +using System; + +namespace Meadow.Devices; + +/// +/// Represents the Clima v2.x hardware +/// +public class ClimaHardwareV2 : ClimaHardwareBase +{ + private readonly IF7FeatherMeadowDevice _device; + + /// + public sealed override II2cBus I2cBus { get; } + + /// + public override string RevisionString => "v2.x"; + + /// + /// Create a new ClimaHardwareV2 object + /// + /// The meadow device + /// The I2C bus + public ClimaHardwareV2(IF7FeatherMeadowDevice device, II2cBus i2cBus) + { + _device = device; + + I2cBus = i2cBus; + + // See hack in Meadow.Core\source\implementations\f7\Meadow.F7\Devices\DeviceChannelManager.cs + // Must initialise any PWM based I/O first + GetRgbPwmLed(); + + try + { + Logger?.Trace("Instantiating Solar Voltage Input"); + SolarVoltageInput = device.Pins.A02.CreateAnalogInputPort(5); + Logger?.Trace("Solar Voltage Input up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Solar Voltage Input: {ex.Message}"); + } + } + + /// + protected override IRgbPwmLed? GetRgbPwmLed() + { + if (_rgbLed == null) + { + try + { + Logger?.Trace("Instantiating RGB LED"); + _rgbLed = new RgbPwmLed( + redPwmPin: _device.Pins.OnboardLedRed, + greenPwmPin: _device.Pins.OnboardLedGreen, + bluePwmPin: _device.Pins.OnboardLedBlue, + CommonType.CommonAnode); + Logger?.Trace("RGB LED up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); + } + } + + return _rgbLed; + } + + /// + protected override NeoM8? GetNeoM8() + { + if (_gnss == null) + { + try + { + Logger?.Trace("Instantiating GNSS"); + _gnss = new NeoM8(_device, _device.PlatformOS.GetSerialPortName("COM4")!, null, null); + Logger?.Trace("GNSS initialized"); + } + catch (Exception e) + { + Logger?.Error($"Err initializing GNSS: {e.Message}"); + } + } + return _gnss; + } + + /// + protected override IWindVane? GetWindVane() + { + if (_windVane == null) + { + try + { + Logger?.Trace("Instantiating Wind Vane"); + _windVane = new WindVane(_device.Pins.A00); + Resolver.SensorService.RegisterSensor(_windVane); + Logger?.Trace("Wind Vane up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Wind Vane: {ex.Message}"); + } + } + return _windVane; + } + + /// + protected override IRainGauge? GetRainGauge() + { + if (_rainGauge == null) + { + try + { + Logger?.Trace("Instantiating Rain Gauge"); + _rainGauge = new SwitchingRainGauge(_device.Pins.D11); + Resolver.SensorService.RegisterSensor(_rainGauge); + Logger?.Trace("Rain Gauge up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Rain Gauge: {ex.Message}"); + } + } + return _rainGauge; + } + + /// + protected override IAnemometer? GetAnemometer() + { + if (_anemometer == null) + { + try + { + Logger?.Trace("Instantiating Anemometer"); + _anemometer = new SwitchingAnemometer(_device.Pins.A01); + Resolver.SensorService.RegisterSensor(_anemometer); + Logger?.Trace("Anemometer up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Anemometer: {ex.Message}"); + } + } + return _anemometer; + } +} \ No newline at end of file diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs new file mode 100644 index 0000000..7745ce2 --- /dev/null +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV3.cs @@ -0,0 +1,245 @@ +using Meadow.Foundation.ICs.IOExpanders; +using Meadow.Foundation.Leds; +using Meadow.Foundation.Sensors.Environmental; +using Meadow.Foundation.Sensors.Gnss; +using Meadow.Foundation.Sensors.Weather; +using Meadow.Hardware; +using Meadow.Peripherals.Leds; +using Meadow.Peripherals.Sensors.Environmental; +using Meadow.Peripherals.Sensors.Weather; +using System; + +namespace Meadow.Devices; + +/// +/// Represents the Clima v3.x hardware +/// +public class ClimaHardwareV3 : ClimaHardwareBase +{ + /// + /// The Meadow CCM device + /// + protected readonly IF7CoreComputeMeadowDevice _device; + + private Scd40? _environmentalSensor; + private ICO2ConcentrationSensor? _co2ConcentrationSensor; + + /// + public sealed override II2cBus I2cBus { get; } + + /// + /// The SCD40 environmental sensor on the Clima board + /// + public Scd40? EnvironmentalSensor => GetEnvironmentalSensor(); + + /// + public override ICO2ConcentrationSensor? CO2ConcentrationSensor => GetCO2ConcentrationSensor(); + + /// + /// The MCP23008 IO expander that contains the Clima hardware version + /// + public Mcp23008 McpVersion { get; protected set; } + + /// + public override string RevisionString => "v3.x"; + + /// + /// Analog inputs to measure Solar voltage has Resistor Divider with R1 = 1000 Ohm, R2 = 680 Ohm + /// Measured analogue voltage needs to be scaled to RETURN actual input voltage + /// Input Voltage = AIN / Resistor Divider + /// + protected const double SolarVoltageResistorDivider = 680.0 / (1000.0 + 680.0); + + /// + /// Analog inputs to measure Solar voltage has Resistor Divider with R1 = 1000 Ohm, R2 = 680 Ohm + /// Measured analogue voltage needs to be scaled to RETURN actual input voltage + /// Input Voltage = AIN / Resistor Divider + /// + protected const double BatteryVoltageResistorDivider = 2000.0 / (1000.0 + 2000.0); + + /// + /// Create a new ClimaHardwareV3 object + /// + /// The meadow device + /// The I2C bus + /// The Mcp23008 used to read version information + public ClimaHardwareV3(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) + { + McpVersion = mcpVersion; + + _device = device; + + I2cBus = i2cBus; + + // See hack in Meadow.Core\source\implementations\f7\Meadow.F7\Devices\DeviceChannelManager.cs + // Must initialise any PWM based I/O first + GetRgbPwmLed(); + + try + { + Logger?.Trace("Instantiating Solar Voltage Input"); + SolarVoltageInput = device.Pins.A02.CreateAnalogInputPort(5, + AnalogInputPort.DefaultSampleInterval, + AnalogInputPort.DefaultReferenceVoltage / SolarVoltageResistorDivider); + Logger?.Trace("Solar Voltage Input up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Solar Voltage Input: {ex.Message}"); + } + + try + { + Logger?.Trace("Instantiating Battery Voltage Input"); + BatteryVoltageInput = device.Pins.A04.CreateAnalogInputPort(5, + AnalogInputPort.DefaultSampleInterval, + AnalogInputPort.DefaultReferenceVoltage / BatteryVoltageResistorDivider); + + Logger?.Trace("Battery Voltage Input up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Battery Voltage Input: {ex.Message}"); + } + } + + private Scd40? GetEnvironmentalSensor() + { + if (_environmentalSensor == null) + { + InitializeScd40(); + } + + return _environmentalSensor; + } + + private ICO2ConcentrationSensor? GetCO2ConcentrationSensor() + { + if (_co2ConcentrationSensor == null) + { + InitializeScd40(); + } + + return _co2ConcentrationSensor; + } + + private void InitializeScd40() + { + try + { + Logger?.Trace("Instantiating environmental sensor"); + var scd = new Scd40(I2cBus, (byte)Scd40.Addresses.Default); + _co2ConcentrationSensor = scd; + _environmentalSensor = scd; + Resolver.SensorService.RegisterSensor(scd); + Logger?.Trace("Environmental sensor up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the SCD40 Environmental Sensor: {ex.Message}"); + } + } + + /// + protected override IRgbPwmLed? GetRgbPwmLed() + { + if (_rgbLed == null) + { + try + { + Logger?.Trace("Instantiating RGB LED"); + _rgbLed = new RgbPwmLed( + redPwmPin: _device.Pins.D09, + greenPwmPin: _device.Pins.D10, + bluePwmPin: _device.Pins.D11, + CommonType.CommonAnode); + Logger?.Trace("RGB LED up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the RGB LED: {ex.Message}"); + } + } + + return _rgbLed; + } + + /// + protected override NeoM8? GetNeoM8() + { + if (_gnss == null) + { + try + { + Logger?.Trace("Instantiating GNSS"); + _gnss = new NeoM8(_device, _device.PlatformOS.GetSerialPortName("COM4")!, _device.Pins.D05, _device.Pins.A03); + Logger?.Trace("GNSS initialized"); + } + catch (Exception e) + { + Logger?.Error($"Err initializing GNSS: {e.Message}"); + } + } + return _gnss; + } + + /// + protected override IWindVane? GetWindVane() + { + if (_windVane == null) + { + try + { + Logger?.Trace("Instantiating Wind Vane"); + _windVane = new WindVane(_device.Pins.A00); + Resolver.SensorService.RegisterSensor(_windVane); + Logger?.Trace("Wind Vane up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Wind Vane: {ex.Message}"); + } + } + return _windVane; + } + + /// + protected override IRainGauge? GetRainGauge() + { + if (_rainGauge == null) + { + try + { + Logger?.Trace("Instantiating Rain Gauge"); + _rainGauge = new SwitchingRainGauge(_device.Pins.D16); + Resolver.SensorService.RegisterSensor(_rainGauge); + Logger?.Trace("Rain Gauge up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Rain Gauge: {ex.Message}"); + } + } + return _rainGauge; + } + + /// + protected override IAnemometer? GetAnemometer() + { + if (_anemometer == null) + { + try + { + Logger?.Trace("Instantiating Anemometer"); + _anemometer = new SwitchingAnemometer(_device.Pins.A01); + Resolver.SensorService.RegisterSensor(_anemometer); + Logger?.Trace("Anemometer up"); + } + catch (Exception ex) + { + Logger?.Error($"Unable to create the Anemometer: {ex.Message}"); + } + } + return _anemometer; + } +} \ No newline at end of file diff --git a/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs new file mode 100644 index 0000000..a68165f --- /dev/null +++ b/Source/Meadow.Clima/Hardware/ClimaHardwareV4.cs @@ -0,0 +1,38 @@ +using Meadow.Foundation.ICs.IOExpanders; +using Meadow.Hardware; + +namespace Meadow.Devices; + +/// +/// Represents the Clima v4.x hardware +/// +public class ClimaHardwareV4 : ClimaHardwareV3 +{ + /// + public override string RevisionString => "v4.x"; + + /// + /// Create a new ClimaHardwareV4 object + /// + /// The meadow device + /// The I2C bus + /// The Mcp23008 used to read version information + public ClimaHardwareV4(IF7CoreComputeMeadowDevice device, II2cBus i2cBus, Mcp23008 mcpVersion) + : base(device, i2cBus, mcpVersion) + { + } + + internal override I2cConnector? CreateQwiicConnector() + { + Logger?.Trace("Creating Qwiic I2C connector"); + + return new I2cConnector( + nameof(Qwiic), + new PinMapping + { + new PinMapping.PinAlias(I2cConnector.PinNames.SCL, _device.Pins.I2C1_SCL), + new PinMapping.PinAlias(I2cConnector.PinNames.SDA, _device.Pins.I2C1_SDA), + }, + new I2cBusMapping(_device, 1)); + } +} \ No newline at end of file diff --git a/Source/Meadow.Clima/Hardware/IClimaHardware.cs b/Source/Meadow.Clima/Hardware/IClimaHardware.cs new file mode 100644 index 0000000..2d9d42c --- /dev/null +++ b/Source/Meadow.Clima/Hardware/IClimaHardware.cs @@ -0,0 +1,90 @@ +using Meadow.Foundation.Sensors.Gnss; +using Meadow.Hardware; +using Meadow.Peripherals.Leds; +using Meadow.Peripherals.Sensors; +using Meadow.Peripherals.Sensors.Atmospheric; +using Meadow.Peripherals.Sensors.Environmental; +using Meadow.Peripherals.Sensors.Weather; + +namespace Meadow.Devices; + +/// +/// Contract for the Clima hardware definitions +/// +public interface IClimaHardware +{ + /// + /// The I2C Bus + /// + public II2cBus I2cBus { get; } + + /// + /// Gets the ITemperatureSensor on the Clima board + /// + public ITemperatureSensor? TemperatureSensor { get; } + + /// + /// Gets the IHumiditySensor on the Clima board + /// + public IHumiditySensor? HumiditySensor { get; } + + /// + /// Gets the IBarometricPressureSensor on the Clima board + /// + public IBarometricPressureSensor? BarometricPressureSensor { get; } + + /// + /// Gets the IGasResistanceSensor on the Clima board + /// + public IGasResistanceSensor? GasResistanceSensor { get; } + + /// + /// Gets the ICO2ConcentrationSensor on the Clima board + /// + public ICO2ConcentrationSensor? CO2ConcentrationSensor { get; } + + /// + /// The Neo GNSS sensor + /// + public NeoM8? Gnss { get; } + + /// + /// The Wind Vane on the Clima board + /// + public IWindVane? WindVane { get; } + + /// + /// The Switching Rain Gauge on the Clima board + /// + public IRainGauge? RainGauge { get; } + + /// + /// The Switching Anemometer on the Clima board + /// + public IAnemometer? Anemometer { get; } + + /// + /// The Solar Voltage Input on the Clima board + /// + public IAnalogInputPort? SolarVoltageInput { get; } + + /// + /// The Battery Voltage Input on the Clima board + /// + public IAnalogInputPort? BatteryVoltageInput { get; } + + /// + /// The RGB PWM LED on the Clima board + /// + public IRgbPwmLed? RgbLed { get; } + + /// + /// Gets the Qwiic connector on the Clima board. + /// + public I2cConnector? Qwiic { get; } + + /// + /// The hardware revision string for the Clima board + /// + public string RevisionString { get; } +} \ No newline at end of file diff --git a/Source/Meadow.Clima/IClimaHardware.cs b/Source/Meadow.Clima/IClimaHardware.cs deleted file mode 100644 index b9505df..0000000 --- a/Source/Meadow.Clima/IClimaHardware.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Meadow.Foundation.Sensors.Gnss; -using Meadow.Hardware; -using Meadow.Peripherals.Leds; -using Meadow.Peripherals.Sensors; -using Meadow.Peripherals.Sensors.Atmospheric; -using Meadow.Peripherals.Sensors.Environmental; -using Meadow.Peripherals.Sensors.Weather; - -namespace Meadow.Devices -{ - /// - /// Contract for the Clima hardware definitions - /// - public interface IClimaHardware - { - /// - /// The I2C Bus - /// - public II2cBus I2cBus { get; } - - /// - /// Gets the ITemperatureSensor on the Clima board - /// - public ITemperatureSensor? TemperatureSensor { get; } - - /// - /// Gets the IHumiditySensor on the Clima board - /// - public IHumiditySensor? HumiditySensor { get; } - - /// - /// Gets the IBarometricPressureSensor on the Clima board - /// - public IBarometricPressureSensor? BarometricPressureSensor { get; } - - /// - /// Gets the IGasResistanceSensor on the Clima board - /// - public IGasResistanceSensor? GasResistanceSensor { get; } - - /// - /// Gets the ICO2ConcentrationSensor on the Clima board - /// - public ICO2ConcentrationSensor? CO2ConcentrationSensor { get; } - - /// - /// The Neo GNSS sensor - /// - public NeoM8? Gnss { get; } - - /// - /// The Wind Vane on the Clima board - /// - public IWindVane? WindVane { get; } - - /// - /// The Switching Rain Gauge on the Clima board - /// - public IRainGauge? RainGauge { get; } - - /// - /// The Switching Anemometer on the Clima board - /// - public IAnemometer? Anemometer { get; } - - /// - /// The Solar Voltage Input on the Clima board - /// - public IAnalogInputPort? SolarVoltageInput { get; } - - /// - /// The Battery Voltage Input on the Clima board - /// - public IAnalogInputPort? BatteryVoltageInput { get; } - - /// - /// The RGB PWM LED on the Clima board - /// - public IRgbPwmLed? RgbLed { get; } - - /// - /// Gets the Qwiic connector on the Clima board. - /// - public I2cConnector? Qwiic { get; } - - /// - /// The hardware revision string for the Clima board - /// - public string RevisionString { get; } - } -} \ No newline at end of file From 16feaad138f298fff469d6b6da6b11645fa7535d Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 18 Apr 2024 19:38:35 -0500 Subject: [PATCH 2/3] Continued refactor into controllers --- Source/Clima_Demo/MeadowApp.cs | 150 +----------------- Source/Clima_Demo/app.config.yaml | 22 ++- Source/Clima_Demo/meadow.config.yaml | 71 ++------- Source/Clima_Demo/wifi.config.yaml | 6 +- .../Controllers/CloudController.cs | 35 ++-- .../Controllers/NotificationController.cs | 31 ++++ .../Controllers/PowerController.cs | 37 +++++ .../Controllers/SensorController.cs | 92 +++++++++++ 8 files changed, 217 insertions(+), 227 deletions(-) create mode 100644 Source/Meadow.Clima/Controllers/NotificationController.cs create mode 100644 Source/Meadow.Clima/Controllers/PowerController.cs create mode 100644 Source/Meadow.Clima/Controllers/SensorController.cs diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index d35c65e..34b2eed 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -2,8 +2,6 @@ using Meadow.Devices; using Meadow.Hardware; using Meadow.Peripherals.Sensors.Location.Gnss; -using Meadow.Units; -using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -13,6 +11,8 @@ public class MeadowApp : App { private IClimaHardware clima; private NotificationController notificationController; + private SensorController sensorController; + private PowerController powerController; public MeadowApp() { @@ -21,7 +21,7 @@ public MeadowApp() public override void OnBootFromCrash(IEnumerable crashReports) { - Resolver.Services.Get()?.LogEvent(CloudEventIds.BootFromCrash, "Device restarted after a crash"); + Resolver.Services.Get()?.LogAppStartupAfterCrash(); } public override Task Initialize() @@ -37,15 +37,16 @@ public override Task Initialize() notificationController.Starting(); - Resolver.Services.Get()?.LogEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {clima.RevisionString})"); + Resolver.Services.Get()?.LogAppStartup(clima.RevisionString); Resolver.Log.Info($"Running on Clima Hardware {clima.RevisionString}"); + sensorController = new SensorController(clima); + powerController = new PowerController(clima); + InitializeSensors(); Resolver.Log.Info("Initialization complete"); - clima.RgbLed.SetColor(Color.Yellow); - var wifi = Device.NetworkAdapters.Primary(); wifi.NetworkConnected += OnNetworkConnected; wifi.NetworkDisconnected += OnNetworkDisconnected; @@ -74,51 +75,6 @@ private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventAr private void InitializeSensors() { - if (clima.TemperatureSensor is { } temperatureSensor) - { - temperatureSensor.Updated += TemperatureUpdated; - } - - if (clima.BarometricPressureSensor is { } pressureSensor) - { - pressureSensor.Updated += PressureUpdated; - } - - if (clima.HumiditySensor is { } humiditySensor) - { - humiditySensor.Updated += HumidityUpdated; - } - - if (clima.CO2ConcentrationSensor is { } co2Sensor) - { - co2Sensor.Updated += Co2Updated; - } - - if (clima.WindVane is { } windVane) - { - windVane.Updated += WindvaneUpdated; - } - - if (clima.RainGauge is { } rainGuage) - { - rainGuage.Updated += RainGuageUpdated; - } - - if (clima.Anemometer is { } anemometer) - { - anemometer.Updated += AnemometerUpdated; - } - - if (clima.SolarVoltageInput is { } solarVoltage) - { - solarVoltage.Updated += SolarVoltageUpdated; - } - - if (clima.BatteryVoltageInput is { } batteryVoltage) - { - batteryVoltage.Updated += BatteryVoltageUpdated; - } - if (clima.Gnss is { } gnss) { //gnss.GsaReceived += GnssGsaReceived; @@ -170,53 +126,6 @@ public override Task Run() { Resolver.Log.Info("Run..."); - var updateInterval = TimeSpan.FromSeconds(5); - - if (clima.TemperatureSensor is { } temp) - { - temp.StartUpdating(updateInterval); - } - - if (clima.HumiditySensor is { } humidity) - { - humidity.StartUpdating(updateInterval); - } - - if (clima.BarometricPressureSensor is { } pressure) - { - pressure.StartUpdating(updateInterval); - } - - if (clima.CO2ConcentrationSensor is { } co2) - { - co2.StartUpdating(updateInterval); - } - - if (clima.WindVane is { } windVane) - { - windVane.StartUpdating(updateInterval); - } - - if (clima.RainGauge is { } rainGuage) - { - rainGuage.StartUpdating(updateInterval); - } - - if (clima.Anemometer is { } anemometer) - { - anemometer.StartUpdating(updateInterval); - } - - if (clima.SolarVoltageInput is { } solarVoltage) - { - solarVoltage.StartUpdating(updateInterval); - } - - if (clima.BatteryVoltageInput is { } batteryVoltage) - { - batteryVoltage.StartUpdating(updateInterval); - } - if (clima.Gnss is { } gnss) { gnss.StartUpdating(); @@ -224,49 +133,4 @@ public override Task Run() return base.Run(); } - - private void TemperatureUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Temperature: {e.New.Celsius:0.#}C"); - } - - private void PressureUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Pressure: {e.New.Millibar:0.#}mbar"); - } - - private void HumidityUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Humidity: {e.New.Percent:0.#}%"); - } - - private void Co2Updated(object sender, IChangeResult e) - { - Resolver.Log.Info($"CO2: {e.New.PartsPerMillion:0.#}ppm"); - } - - private void SolarVoltageUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Solar Voltage: {e.New.Volts:0.#} volts"); - } - - private void BatteryVoltageUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Battery Voltage: {e.New.Volts:0.#} volts"); - } - - private void AnemometerUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); - } - - private void RainGuageUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Rain Gauge: {e.New.Millimeters:0.#} mm"); - } - - private void WindvaneUpdated(object sender, IChangeResult e) - { - Resolver.Log.Info($"Wind Vane: {e.New.Compass16PointCardinalName} ({e.New.Radians:0.#} radians)"); - } } \ No newline at end of file diff --git a/Source/Clima_Demo/app.config.yaml b/Source/Clima_Demo/app.config.yaml index c16313e..08ab07f 100644 --- a/Source/Clima_Demo/app.config.yaml +++ b/Source/Clima_Demo/app.config.yaml @@ -4,11 +4,25 @@ Lifecycle: # Control whether Meadow will restart when an unhandled app exception occurs. Combine with Lifecycle > AppFailureRestartDelaySeconds to control restart timing. - RestartOnAppFailure: false + RestartOnAppFailure: true # # When app set to restart automatically on app failure, # AppFailureRestartDelaySeconds: 15 # # Adjust the level of logging detail. -# Logging: -# LogLevel: -# Default: "Trace" +Logging: + LogLevel: + Default: Trace + +MeadowCloud: + + # Enable Logging, Events, Command + Control + Enabled: true + + # Enable Over-the-air Updates +# EnableUpdates: false + + # Enable Health Metrics + EnableHealthMetrics: true + + # How often to send metrics to Meadow.Cloud + HealthMetricsIntervalMinutes: 60 diff --git a/Source/Clima_Demo/meadow.config.yaml b/Source/Clima_Demo/meadow.config.yaml index 9880162..ae6dfe8 100644 --- a/Source/Clima_Demo/meadow.config.yaml +++ b/Source/Clima_Demo/meadow.config.yaml @@ -1,55 +1,18 @@ -# Uncommented these options as needed. -# To learn more about these config options, check out the OS & Device Configuration documentation. -# http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/OS_Device_Configuration/ +Device: + Name: Clima + +Coprocessor: + AutomaticallyStartNetwork: true + AutomaticallyReconnect: true + MaximumRetryCount: 7 + +Network: + DefaultInterface: WiFi + GetNetworkTimeAtStartup: true + NtpRefreshPeriodSeconds: 600 + NtpServers: + - 0.pool.ntp.org + - 1.pool.ntp.org + - 2.pool.ntp.org + - 3.pool.ntp.org -# Device: -# # Name of the device on the network. -# Name: MeadowF7V2_ConfigSample -# -# # Uncomment if SD card hardware present on this hardware (e.g., Core-Compute module with SD add-on)? Optional; default value is `false`. -# SdStorageSupported: true -# # Control how the ESP coprocessor will start and operate. -# Coprocessor: -# # Should the ESP32 automatically attempt to connect to an access point at startup? -# # If set to true, wifi.config.yaml credentials must be stored in the device. -# AutomaticallyStartNetwork: true -# -# # Should the ESP32 automatically reconnect to the configured access point? -# AutomaticallyReconnect: true -# -# # Maximum number of retry attempts for connections etc. before an error code is returned. -# MaximumRetryCount: 7 -# # Network configuration. -# Network: -# Interfaces: -# - Name: Ethernet -# UseDHCP: false -# IPAddress: 192.168.1.60 -# NetMask: 255.255.255.0 -# Gateway: 192.168.1.254 -# - Name: WiFi -# UseDHCP: true -# IPAddress: -# NetMask: -# Gateway: -# -# # Which interface should be used? -# DefaultInterface: WiFi -# -# # Automatically attempt to get the time at startup? -# GetNetworkTimeAtStartup: true -# -# # Time synchronization period in seconds. -# NtpRefreshPeriodSeconds: 600 -# -# # Name of the NTP servers. -# NtpServers: -# - 0.pool.ntp.org -# - 1.pool.ntp.org -# - 2.pool.ntp.org -# - 3.pool.ntp.org -# -# # IP addresses of the DNS servers. -# DnsServers: -# - 1.1.1.1 -# - 8.8.8.8 \ No newline at end of file diff --git a/Source/Clima_Demo/wifi.config.yaml b/Source/Clima_Demo/wifi.config.yaml index fc7a427..748dc39 100644 --- a/Source/Clima_Demo/wifi.config.yaml +++ b/Source/Clima_Demo/wifi.config.yaml @@ -3,6 +3,6 @@ # http://developer.wildernesslabs.co/Meadow/Meadow.OS/Configuration/WiFi_Configuration/ # # To enable automatically connecting to a default network, make sure to enable the Coprocessor > AutomaticallyStartNetwork value in meadow.config.yaml. - Credentials: - Ssid: interwebs - Password: 1234567890 +Credentials: + Ssid: interwebs + Password: 1234567890 diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index 4f086f8..c4ff762 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -1,41 +1,30 @@ using Meadow; using Meadow.Cloud; -using Meadow.Peripherals.Leds; using System; namespace Clima_Demo; -public class NotificationController +public class CloudController { - private readonly IRgbPwmLed? rgbLed; - - public NotificationController(IRgbPwmLed? rgbLed) - { - this.rgbLed = rgbLed; - } - - public void Starting() + public void LogAppStartupAfterCrash() { - rgbLed?.SetColor(RgbLedColors.Red); + LogEvent(CloudEventIds.DeviceStarted, $"Device restarted after crash"); } - public void NetworkConnected() + public void LogAppStartup(string hardwareRevision) { - rgbLed?.SetColor(RgbLedColors.Green); + LogEvent(CloudEventIds.DeviceStarted, $"Device started (hardware {hardwareRevision})"); } - public void NetworkDisconnected() + private void LogEvent(CloudEventIds eventId, string message) { - rgbLed?.SetColor(RgbLedColors.Yellow); - } -} + if (Resolver.MeadowCloudService == null) { return; } -public class CloudController -{ - public void LogEvent(CloudEventIds eventId, string message) - { - if (Resolver.MeadowCloudService == null - || !Resolver.MeadowCloudService.IsEnabled) return; + if (!Resolver.MeadowCloudService.IsEnabled) + { + Resolver.Log.Warn($"CLOUD INTEGRATION IS DISABLED"); + return; + } Resolver.MeadowCloudService.SendEvent( new CloudEvent diff --git a/Source/Meadow.Clima/Controllers/NotificationController.cs b/Source/Meadow.Clima/Controllers/NotificationController.cs new file mode 100644 index 0000000..fbdb61f --- /dev/null +++ b/Source/Meadow.Clima/Controllers/NotificationController.cs @@ -0,0 +1,31 @@ +using Meadow; +using Meadow.Peripherals.Leds; + +namespace Clima_Demo; + +public class NotificationController +{ + private readonly IRgbPwmLed? rgbLed; + + public NotificationController(IRgbPwmLed? rgbLed) + { + this.rgbLed = rgbLed; + } + + public void Starting() + { + rgbLed?.SetColor(RgbLedColors.Red); + } + + public void NetworkConnected() + { + Resolver.Log.Info("Network connected"); + rgbLed?.SetColor(RgbLedColors.Green); + } + + public void NetworkDisconnected() + { + Resolver.Log.Info("Network disconnected"); + rgbLed?.SetColor(RgbLedColors.Yellow); + } +} diff --git a/Source/Meadow.Clima/Controllers/PowerController.cs b/Source/Meadow.Clima/Controllers/PowerController.cs new file mode 100644 index 0000000..998ef09 --- /dev/null +++ b/Source/Meadow.Clima/Controllers/PowerController.cs @@ -0,0 +1,37 @@ +using Meadow; +using Meadow.Devices; +using Meadow.Units; +using System; + +namespace Clima_Demo; + +public class PowerController +{ + public bool LogPowerData { get; set; } = false; + public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); + + public PowerController(IClimaHardware clima) + { + if (clima.SolarVoltageInput is { } solarVoltage) + { + solarVoltage.Updated += SolarVoltageUpdated; + solarVoltage.StartUpdating(UpdateInterval); + } + + if (clima.BatteryVoltageInput is { } batteryVoltage) + { + batteryVoltage.Updated += BatteryVoltageUpdated; + batteryVoltage.StartUpdating(UpdateInterval); + } + } + + private void SolarVoltageUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogPowerData, $"Solar Voltage: {e.New.Volts:0.#} volts"); + } + + private void BatteryVoltageUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogPowerData, $"Battery Voltage: {e.New.Volts:0.#} volts"); + } +} diff --git a/Source/Meadow.Clima/Controllers/SensorController.cs b/Source/Meadow.Clima/Controllers/SensorController.cs new file mode 100644 index 0000000..f67c47b --- /dev/null +++ b/Source/Meadow.Clima/Controllers/SensorController.cs @@ -0,0 +1,92 @@ +using Meadow; +using Meadow.Devices; +using Meadow.Units; +using System; + +namespace Clima_Demo; + +public class SensorController +{ + public bool LogSensorData { get; set; } = false; + public TimeSpan UpdateInterval { get; } = TimeSpan.FromSeconds(5); + + public SensorController(IClimaHardware clima) + { + if (clima.TemperatureSensor is { } temperatureSensor) + { + temperatureSensor.Updated += TemperatureUpdated; + temperatureSensor.StartUpdating(UpdateInterval); + } + + if (clima.BarometricPressureSensor is { } pressureSensor) + { + pressureSensor.Updated += PressureUpdated; + pressureSensor.StartUpdating(UpdateInterval); + } + + if (clima.HumiditySensor is { } humiditySensor) + { + humiditySensor.Updated += HumidityUpdated; + humiditySensor.StartUpdating(UpdateInterval); + } + + if (clima.CO2ConcentrationSensor is { } co2Sensor) + { + co2Sensor.Updated += Co2Updated; + co2Sensor.StartUpdating(UpdateInterval); + } + + if (clima.WindVane is { } windVane) + { + windVane.Updated += WindvaneUpdated; + windVane.StartUpdating(UpdateInterval); + } + + if (clima.RainGauge is { } rainGuage) + { + rainGuage.Updated += RainGuageUpdated; + rainGuage.StartUpdating(UpdateInterval); + } + + if (clima.Anemometer is { } anemometer) + { + anemometer.Updated += AnemometerUpdated; + anemometer.StartUpdating(UpdateInterval); + } + } + + private void TemperatureUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Temperature: {e.New.Celsius:0.#}C"); + } + + private void PressureUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Pressure: {e.New.Millibar:0.#}mbar"); + } + + private void HumidityUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Humidity: {e.New.Percent:0.#}%"); + } + + private void Co2Updated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"CO2: {e.New.PartsPerMillion:0.#}ppm"); + } + + private void AnemometerUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Anemometer: {e.New.MetersPerSecond:0.#} m/s"); + } + + private void RainGuageUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Rain Gauge: {e.New.Millimeters:0.#} mm"); + } + + private void WindvaneUpdated(object sender, IChangeResult e) + { + Resolver.Log.InfoIf(LogSensorData, $"Wind Vane: {e.New.Compass16PointCardinalName} ({e.New.Radians:0.#} radians)"); + } +} From a4185db33ac80f949ae212e0acbf78ddaf7e04b3 Mon Sep 17 00:00:00 2001 From: Chris Tacke Date: Thu, 18 Apr 2024 20:10:31 -0500 Subject: [PATCH 3/3] Added LocationController --- Source/Clima_Demo/MeadowApp.cs | 70 ++----------------- .../Controllers/CloudController.cs | 8 ++- .../Controllers/LocationController.cs | 60 ++++++++++++++++ 3 files changed, 71 insertions(+), 67 deletions(-) create mode 100644 Source/Meadow.Clima/Controllers/LocationController.cs diff --git a/Source/Clima_Demo/MeadowApp.cs b/Source/Clima_Demo/MeadowApp.cs index 34b2eed..1f91efd 100644 --- a/Source/Clima_Demo/MeadowApp.cs +++ b/Source/Clima_Demo/MeadowApp.cs @@ -1,7 +1,6 @@ using Meadow; using Meadow.Devices; using Meadow.Hardware; -using Meadow.Peripherals.Sensors.Location.Gnss; using System.Collections.Generic; using System.Threading.Tasks; @@ -13,6 +12,7 @@ public class MeadowApp : App private NotificationController notificationController; private SensorController sensorController; private PowerController powerController; + private LocationController locationController; public MeadowApp() { @@ -42,10 +42,7 @@ public override Task Initialize() sensorController = new SensorController(clima); powerController = new PowerController(clima); - - InitializeSensors(); - - Resolver.Log.Info("Initialization complete"); + locationController = new LocationController(clima); var wifi = Device.NetworkAdapters.Primary(); wifi.NetworkConnected += OnNetworkConnected; @@ -60,6 +57,8 @@ public override Task Initialize() notificationController.NetworkDisconnected(); } + Resolver.Log.Info("Initialization complete"); + return Task.CompletedTask; } @@ -72,65 +71,4 @@ private void OnNetworkConnected(INetworkAdapter sender, NetworkConnectionEventAr { notificationController.NetworkConnected(); } - - private void InitializeSensors() - { - if (clima.Gnss is { } gnss) - { - //gnss.GsaReceived += GnssGsaReceived; - //gnss.GsvReceived += GnssGsvReceived; - //gnss.VtgReceived += GnssVtgReceived; - gnss.RmcReceived += GnssRmcReceived; - gnss.GllReceived += GnssGllReceived; - } - } - - 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.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); - } - } - - private void GnssGllReceived(object _, GnssPositionInfo e) - { - if (e.Valid) - { - Resolver.Log.Info($"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); - } - } - - public override Task Run() - { - Resolver.Log.Info("Run..."); - - if (clima.Gnss is { } gnss) - { - gnss.StartUpdating(); - } - - return base.Run(); - } } \ No newline at end of file diff --git a/Source/Meadow.Clima/Controllers/CloudController.cs b/Source/Meadow.Clima/Controllers/CloudController.cs index c4ff762..060a58d 100644 --- a/Source/Meadow.Clima/Controllers/CloudController.cs +++ b/Source/Meadow.Clima/Controllers/CloudController.cs @@ -18,7 +18,11 @@ public void LogAppStartup(string hardwareRevision) private void LogEvent(CloudEventIds eventId, string message) { - if (Resolver.MeadowCloudService == null) { return; } + if (Resolver.MeadowCloudService == null) + { + Resolver.Log.Warn($"CLOUD SERVICE IS NULL"); + return; + } if (!Resolver.MeadowCloudService.IsEnabled) { @@ -26,6 +30,8 @@ private void LogEvent(CloudEventIds eventId, string message) return; } + Resolver.Log.Info($"Sending cloud event"); + Resolver.MeadowCloudService.SendEvent( new CloudEvent { diff --git a/Source/Meadow.Clima/Controllers/LocationController.cs b/Source/Meadow.Clima/Controllers/LocationController.cs new file mode 100644 index 0000000..63c7e35 --- /dev/null +++ b/Source/Meadow.Clima/Controllers/LocationController.cs @@ -0,0 +1,60 @@ +using Meadow; +using Meadow.Devices; +using Meadow.Peripherals.Sensors.Location.Gnss; + +namespace Clima_Demo; + +public class LocationController +{ + public bool LogData { get; set; } = false; + + 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}]"); + } + } + + private void GnssGllReceived(object _, GnssPositionInfo e) + { + if (e.Valid) + { + Resolver.Log.InfoIf(LogData, $"GNSS Position: lat: [{e.Position.Latitude}], long: [{e.Position.Longitude}]"); + } + } +}