From 028c5e0bac5c691f20268d96cc392964ff63b22d Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 09:14:26 -0700 Subject: [PATCH 1/9] Meadow.Blazor (#841) * Add Blazor + Mac docs * cleanup * Finish Blazor docs setup instructions * Fix hello world links * Cleanup * Typo fix * Minor cleanup --- .../Meadow_Desktop/Meadow_Blazor/index.md | 216 ++++++++++++++++++ .../Meadow_Desktop/Meadow_Linux/index.md | 2 +- .../Meadow/Meadow_Desktop/Meadow_Mac/index.md | 138 +++++++++++ .../Meadow_Desktop/Meadow_Windows/index.md | 4 +- src/components/GettingStarted.tsx | 1 + 5 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md new file mode 100644 index 000000000..3c6a89547 --- /dev/null +++ b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md @@ -0,0 +1,216 @@ +--- +layout: Meadow +title: Get started with Meadow.Blazor +subtitle: "To get up and running with Meadow.Blazor, follow these steps:" +--- + +Meadow.Blazor in combination with Meadow.Desktop offers an environment for developing Meadow applications that can run on Windows, Mac and Linux. Developing with Meadow.Blazor requires setting up your development machine with some prerequisites. Then, after connecting any external components, you can deploy and run your Meadow application. + +Meadow Blazor enables you to combine a Blazor front-end while accessing physical components by way of an FTDI breakout board such as FT232H that provide GPIO, SPI, and I2C. + +## Prerequisites + +Before you can get started with Meadow.Blazor, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). + +If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). + +### Using GPIO and SPI + +With an additional accessory, you can add GPIO and SPI capabilities to your desktop device. You can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264) to provide GPIO and SPI capabilities. + +## Create your first Meadow.Blazor app + +1. Create a new dotnet app on your development machine and navigate to that new project. + + ```command + dotnet new blazorserver -n MeadowBlazorSampleApp + cd MeadowBlazorSampleApp + ``` + +1. Add the Meadow.Blazor NuGet reference to your project. + + ```command + dotnet add package Meadow.Blazor + ``` + +1. Create a new file named `MeadowApplication` in your project with the following code: + + ```csharp + using Meadow; + + internal class MeadowApplication : App + { + public override Task Initialize() + { + //initialize hardware here and add to sensor serivce + return base.Initialize(); + } + + public override Task Run() + { + return base.Run(); + } + } + +1. If using external hardware, you'll need to add the relevant Meadow nuget packages and initialize the drivers in the `Initialize` method - for example: + + ```csharp + using Meadow; + using Meadow.Foundation.ICs.IOExpanders; + using Meadow.Foundation.Leds; + using Meadow.Foundation.Sensors.Atmospheric; + using Meadow.Peripherals.Leds; + + internal class MeadowApplication : App + { + public override Task Initialize() + { + FtdiExpanderCollection.Devices.Refresh(); + var ftdi = FtdiExpanderCollection.Devices[0]; + var output = ftdi.Pins.D7.CreateDigitalOutputPort(false); + Resolver.Services.Add(output); + + var bme680 = new Bme680(ftdi.CreateSpiBus(), ftdi.Pins.C7); + Resolver.Services.Add(bme680); + + var led = new Led(ftdi.Pins.C0); + Resolver.Services.Add(led); + + return base.Initialize(); + } + + public override Task Run() + { + return base.Run(); + } + } + ``` + +1. Meadow.Desktop is initialized via an extension method on `WebApplication` included with Meadow.Blazor method named `UseMeadow`. Add `UseMethod` to your program.cs file. + + ```csharp + using Meadow.Blazor; + using Meadow.Blazor.Services; + + var builder = WebApplication.CreateBuilder(args); + + ... + + var app = builder.Build(); + + ... + + app.UseMeadow(); + + ... + + app.Run(); + ``` + +1. Build the app for your development machine using either Visual Studio or the `dotnet` tool. + + ```command + dotnet build + ``` + +1. Run the app. + + ```command + dotnet run + ``` + +## Access peripherals from a Blazor server app + +You'll decide on the architecture of your Blazor application but a common pattern is to create view models to support razor pages. + +Here's an example view model that presents sensor data via `string` properties for a `BME680` atmospheric sensor. Note the `StateChanged?.Invoke()` call to notify the UI of updates. + + ```csharp + using Meadow.Foundation.Sensors.Atmospheric; + using Meadow.Peripherals.Leds; + using Meadow.Units; + + namespace Meadow.Blazor.Services + { + public class SensorViewModel : IDisposable + { + private readonly Bme680 _bme680; + + public string TemperatureValue { get; private set; } = "0°C"; + public string PressureValue { get; private set; } = "0atm"; + + public event Action? StateChanged; + + public SensorViewModel() + { + _bme680 = Resolver.Services.Get() ?? throw new Exception("BME68x not found"); + _bme680.Updated += Bme680Updated; + _bme680.StartUpdating(TimeSpan.FromSeconds(2)); + } + + private void Bme680Updated(object? sender, IChangeResult<(Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure, Resistance? GasResistance)> e) + { + TemperatureValue = $"{e.New.Temperature?.Celsius:n0}°C"; + PressureValue = $"{e.New.Pressure?.StandardAtmosphere:n2}atm"; + + _led.IsOn = false; + + StateChanged?.Invoke(); + } + + public void Dispose() + { + _bme680?.StopUpdating(); + } + } + } + ``` + +And here's example razor page that uses the view model above: + + ``` + @page "/" + @inject Meadow.Blazor.Services.SensorViewModel ViewModel + +
+
+
+ Meadow +
+

Meadow on Blazor

+

Atmospheric readings from a BME680

+ +
+
+ Temperature: + @ViewModel.TemperatureValue +
+
+ Pressure: + @ViewModel.PressureValue +
+
+
+
+ + @code { + protected override void OnInitialized() + { + ViewModel.StateChanged += OnViewModelStateChanged; + } + + private void OnViewModelStateChanged() + { + InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + ViewModel.StateChanged -= OnViewModelStateChanged; + } + } + ``` + +## Next steps + +Now that you have your Meadow.Blazor device set up and your first Meadow app running on it, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/). diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Linux/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Linux/index.md index 8ac46c95f..c6a2af344 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Linux/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Linux/index.md @@ -27,7 +27,7 @@ You can also find any supported boards in the [Meadow.Linux implementation pinou ## Prerequisites -Before you can get started with Meadow.Linux, make sure your [development machine is set up for Meadow development](../../Hello_World/). +Before you can get started with Meadow.Linux, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). To get started with Meadow.Linux, you will need to install the following prerequisites on your host Linux machine. diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md index e69de29bb..7e133a97f 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md @@ -0,0 +1,138 @@ +--- +layout: Meadow +title: Get started with Meadow.Mac +subtitle: "To get up and running with Meadow.Mac, follow these steps:" +--- + +Meadow.Mac offers an environment for developing Meadow code that can run on macOS, and with an extra add-on can even access general-purpose input/output (GPIO) pins. Developing with Meadow.Mac requires setting up your development machine with some prerequisites. Then, after connecting any external components, you can deploy and run your Meadow application. + +Running Meadow applications on Mac can provide a very convenient development loop for prototyping and testing your Meadow applications, quickly iterating and seeing the result of code changes, potentially using the same components you would use on a Meadow Feather or Core-Compute module by way of an FTDI breakout board such as FT232H that provide GPIO, SPI, and I2C. (In this early stage, Meadow.Mac does not yet support I2C.) + +You can also quickly prototype graphics using an emulated IDisplay object that renders to a standard window on your Mac machine before deploying them to component displays. Additionally, running Meadow applications on more extensive hardware can also provide capabilities for intensive workloads requiring much more processing power. + +## Prerequisites + +Before you can get started with Meadow.Mac, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). + +If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK for Mac from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). + +### Using GPIO and SPI + +With an additional accessory, you can add GPIO and SPI capabilities to your Mac device. You can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264) to provide GPIO and SPI capabilities. (The FT232H also supports I2C, but in this early stage, Meadow.Mac does not yet support it.) + +## Create your first Meadow.Mac app + +1. Create a new dotnet app on your development machine and navigate to that new project. + + ```command + dotnet new console --output MeadowMacSampleApp + cd MeadowMacSampleApp + ``` + + This will ensure the Meadow app has the project settings that will work within Meadow.Mac. + +1. Add the Meadow.Mac NuGet reference to your project. + + ```command + dotnet add package Meadow.Mac + ``` + +1. Replace the contents of the `Program.cs` file in your project with the following. + + ```csharp + using Meadow; + using Meadow.Devices; + + public class MeadowApp : App + { + static async Task Main(string[] args) + { + await MeadowOS.Start(args); + } + + public override Task Initialize() + { + Resolver.Log.Info("Initialize..."); + + return base.Initialize(); + } + + public override Task Run() + { + Resolver.Log.Info("Run..."); + + Resolver.Log.Info("Hello, Meadow.Mac!"); + + return base.Run(); + } + } + ``` + + This is a simple Meadow.Mac app that will output some messages to the console at various stages of the Meadow app. + +1. Build the app for your development machine using either Visual Studio or the `dotnet` tool. + + ```command + dotnet build + ``` + +1. Run the app. + + ```command + dotnet run + ``` + + At the end of the Meadow app launch output, you should see the following output from your app. + + ```console + Initialize... + Run... + Hello, Meadow.Mac! + ``` + +You have a Meadow.Mac app running on your Mac development machine. You can continue to develop and test your Meadow app on your development machine. When you are ready, you can deploy your Meadow app to other Mac device like any other .NET app. + +## Adapt a Meadow app for Meadow.Mac + +You can also modify an existing Meadow app to run on Meadow.Mac by adjusting the `App` type and adding the following static `Main` method to your `MeadowApp` class. + +For example, here are the changes to make the MeadowApp class work on Meadow.Mac, configured for Mac. + +1. Configure the project type to be an executable by changing the project output type to a .NET 7 executable in the project file. + + ```xml + + + Exe + net7.0 + enable + enable + + + ... + + ``` + +1. Within the Meadow app's class file, change the `App` type to align with your target Meadow.Mac device: `App`. + + ```csharp + public class MeadowApp : App + { + ... + } + ``` + +1. Within the `MeadowApp` class, or whatever your app's class name is, add a new `Main` method to give the app a target to launch when the app is run. + + ```csharp + public static async Task Main(string[] args) + { + await MeadowOS.Start(args); + } + ``` + +Your Meadow app should now be able to run on Meadow.Mac, calling into the usual `Initialize` and `Run` methods of your app. + +## Next steps + +Now that you have your Meadow.Mac device set up and your first Meadow app running on it, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/Mac). diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md index 95670236e..d211d3fef 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md @@ -12,9 +12,9 @@ You can also quickly prototype graphics using an emulated IDisplay object that r ## Prerequisites -Before you can get started with Meadow.Windows, make sure your [development machine is set up for Meadow development](../../Hello_World/). +Before you can get started with Meadow.Windows, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). -If you don't already have it, you may want to install the .NET 7.0 SDK. You can find the latest version of the .NET SDK for Windows from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). (Currently, all Meadow.Windows samples are targeting .NET 7.0, though they also support targeting .NET 6.0.) +If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK for Windows from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). ### Using GPIO and SPI diff --git a/src/components/GettingStarted.tsx b/src/components/GettingStarted.tsx index dfa1862bb..6d969b0d1 100644 --- a/src/components/GettingStarted.tsx +++ b/src/components/GettingStarted.tsx @@ -69,6 +69,7 @@ export default function GettingStarted(): JSX.Element {
  • Windows
  • Linux
  • Mac
  • +
  • Blazor
  • From 11b6312ba5d2a23af2d74a107fbb4c49789f1a09 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 12:57:56 -0700 Subject: [PATCH 2/9] Blazor cleanup + screen shot --- .../Meadow_Desktop/Meadow_Blazor/index.md | 38 +++++++++++------- .../Meadow_Blazor/meadow_blazor.jpg | Bin 0 -> 46958 bytes 2 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 docs/Meadow/Meadow_Desktop/Meadow_Blazor/meadow_blazor.jpg diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md index 3c6a89547..ddc80afd0 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md @@ -123,7 +123,11 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk You'll decide on the architecture of your Blazor application but a common pattern is to create view models to support razor pages. -Here's an example view model that presents sensor data via `string` properties for a `BME680` atmospheric sensor. Note the `StateChanged?.Invoke()` call to notify the UI of updates. +Below is example code to present sensor for a `BME680` atmospheric sensor. + +![Meadow.Blazor running in a web browser](meadow_blazor.jpg) + +1. Create a view model that exposes your data as public properties along with a `StateChanged` `Action`. Note the use `Resolver.Services` to access the peripherals you registed in `MeadowApplication`. ```csharp using Meadow.Foundation.Sensors.Atmospheric; @@ -137,13 +141,14 @@ Here's an example view model that presents sensor data via `string` properties f private readonly Bme680 _bme680; public string TemperatureValue { get; private set; } = "0°C"; + public string HumidityValue { get; private set; } = "0%"; public string PressureValue { get; private set; } = "0atm"; public event Action? StateChanged; public SensorViewModel() { - _bme680 = Resolver.Services.Get() ?? throw new Exception("BME68x not found"); + _bme680 = Resolver.Services.Get() ?? throw new Exception("BME680 not found"); _bme680.Updated += Bme680Updated; _bme680.StartUpdating(TimeSpan.FromSeconds(2)); } @@ -151,10 +156,9 @@ Here's an example view model that presents sensor data via `string` properties f private void Bme680Updated(object? sender, IChangeResult<(Temperature? Temperature, RelativeHumidity? Humidity, Pressure? Pressure, Resistance? GasResistance)> e) { TemperatureValue = $"{e.New.Temperature?.Celsius:n0}°C"; + HumidityValue = $"{e.New.Humidity?.Percent:n0}%"; PressureValue = $"{e.New.Pressure?.StandardAtmosphere:n2}atm"; - _led.IsOn = false; - StateChanged?.Invoke(); } @@ -166,9 +170,9 @@ Here's an example view model that presents sensor data via `string` properties f } ``` -And here's example razor page that uses the view model above: +2. Create a razor page that uses the view model: - ``` + ```razor @page "/" @inject Meadow.Blazor.Services.SensorViewModel ViewModel @@ -177,18 +181,22 @@ And here's example razor page that uses the view model above:
    Meadow
    -

    Meadow on Blazor

    +

    Meadow Blazor

    Atmospheric readings from a BME680

    -
    - Temperature: - @ViewModel.TemperatureValue -
    -
    - Pressure: - @ViewModel.PressureValue -
    +
    + Temperature: + @ViewModel.TemperatureValue +
    +
    + Pressure: + @ViewModel.PressureValue +
    +
    + Humidity: + @ViewModel.HumidityValue +
    diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/meadow_blazor.jpg b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/meadow_blazor.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a814075b1f6c8c4a95420de02bc96270b48dd47 GIT binary patch literal 46958 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 U%huzDW_C|ajo%`UYy24dHy;OPcmMzZ literal 0 HcmV?d00001 From 0d2e727fc52bb276bd463c8e6dd3e44f575d0cb7 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 13:39:47 -0700 Subject: [PATCH 3/9] Typos + cleanup --- docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md index ddc80afd0..1a497b983 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md @@ -42,7 +42,7 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk { public override Task Initialize() { - //initialize hardware here and add to sensor serivce + //initialize hardware here and add to sensor service return base.Initialize(); } @@ -123,7 +123,7 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk You'll decide on the architecture of your Blazor application but a common pattern is to create view models to support razor pages. -Below is example code to present sensor for a `BME680` atmospheric sensor. +Below is example code to present sensor data for a `BME680` atmospheric sensor. ![Meadow.Blazor running in a web browser](meadow_blazor.jpg) @@ -172,7 +172,7 @@ Below is example code to present sensor for a `BME680` atmospheric sensor. 2. Create a razor page that uses the view model: - ```razor + ```csharp @page "/" @inject Meadow.Blazor.Services.SensorViewModel ViewModel @@ -221,4 +221,4 @@ Below is example code to present sensor for a `BME680` atmospheric sensor. ## Next steps -Now that you have your Meadow.Blazor device set up and your first Meadow app running on it, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/). +Now that you have your Meadow.Blazor project and hardware set up and app running, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add additional functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/). From a8dc9794b82e411cd2db92b3aa483941096daff5 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 13:51:55 -0700 Subject: [PATCH 4/9] AI cleanup pass --- .../Meadow_Desktop/Meadow_Blazor/index.md | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md index 1a497b983..138fb5193 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md @@ -6,28 +6,28 @@ subtitle: "To get up and running with Meadow.Blazor, follow these steps:" Meadow.Blazor in combination with Meadow.Desktop offers an environment for developing Meadow applications that can run on Windows, Mac and Linux. Developing with Meadow.Blazor requires setting up your development machine with some prerequisites. Then, after connecting any external components, you can deploy and run your Meadow application. -Meadow Blazor enables you to combine a Blazor front-end while accessing physical components by way of an FTDI breakout board such as FT232H that provide GPIO, SPI, and I2C. +Meadow Blazor enables you to combine a Blazor front-end using Blazor Server while accessing physical components by way of an FTDI breakout board such as FT232H that provides GPIO, SPI, and I2C. ## Prerequisites Before you can get started with Meadow.Blazor, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). -If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). +If it is not already installed, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). ### Using GPIO and SPI -With an additional accessory, you can add GPIO and SPI capabilities to your desktop device. You can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264) to provide GPIO and SPI capabilities. +With an FTDI breakout board like the [FT232H](https://www.adafruit.com/product/2264), you can add GPIO and SPI capabilities to your desktop device. ## Create your first Meadow.Blazor app -1. Create a new dotnet app on your development machine and navigate to that new project. +1. Create a new Blazor Server dotnet app on your development machine and navigate to the project folder. ```command dotnet new blazorserver -n MeadowBlazorSampleApp cd MeadowBlazorSampleApp ``` -1. Add the Meadow.Blazor NuGet reference to your project. +1. Add the Meadow.Blazor NuGet package to your project. ```command dotnet add package Meadow.Blazor @@ -51,8 +51,9 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk return base.Run(); } } + ``` -1. If using external hardware, you'll need to add the relevant Meadow nuget packages and initialize the drivers in the `Initialize` method - for example: +1. If using external hardware, you'll need to add the relevant Meadow nuget packages and initialize the drivers in the `Initialize` method: ```csharp using Meadow; @@ -86,7 +87,7 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk } ``` -1. Meadow.Desktop is initialized via an extension method on `WebApplication` included with Meadow.Blazor method named `UseMeadow`. Add `UseMethod` to your program.cs file. +1. Meadow.Desktop is initialized via an extension method on `WebApplication` included with Meadow.Blazor named `UseMeadow`. Call `UseMeadow` in your program.cs. ```csharp using Meadow.Blazor; @@ -119,15 +120,15 @@ With an additional accessory, you can add GPIO and SPI capabilities to your desk dotnet run ``` -## Access peripherals from a Blazor server app +## Access peripherals from a Blazor Server app -You'll decide on the architecture of your Blazor application but a common pattern is to create view models to support razor pages. +When structuring your Blazor Server application, a common pattern is to create view models to support Razor pages. -Below is example code to present sensor data for a `BME680` atmospheric sensor. +Below is an example to present sensor data for a `BME680` atmospheric sensor. ![Meadow.Blazor running in a web browser](meadow_blazor.jpg) -1. Create a view model that exposes your data as public properties along with a `StateChanged` `Action`. Note the use `Resolver.Services` to access the peripherals you registed in `MeadowApplication`. +1. Create a view model that exposes sensor data as public properties and includes a `StateChanged` `Action`. Note the use of `Resolver.Services` to access the peripherals you registered in `MeadowApplication`. ```csharp using Meadow.Foundation.Sensors.Atmospheric; @@ -221,4 +222,4 @@ Below is example code to present sensor data for a `BME680` atmospheric sensor. ## Next steps -Now that you have your Meadow.Blazor project and hardware set up and app running, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add additional functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/). +Now that you have your Meadow.Blazor project and hardware set up and your app running, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add additional functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/). From 4649d127817f89aabe0cfa7fc0ddb26533ab632a Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 14:01:39 -0700 Subject: [PATCH 5/9] Cleanup --- .../Meadow_Desktop/Meadow_Blazor/index.md | 4 +- .../Meadow/Meadow_Desktop/Meadow_Mac/index.md | 44 +++++++++---------- .../Meadow_Desktop/Meadow_Windows/index.md | 36 +++++++-------- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md index 138fb5193..a5c6b92a4 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Blazor/index.md @@ -108,13 +108,13 @@ With an FTDI breakout board like the [FT232H](https://www.adafruit.com/product/2 app.Run(); ``` -1. Build the app for your development machine using either Visual Studio or the `dotnet` tool. +1. Build the app: ```command dotnet build ``` -1. Run the app. +1. Run the app: ```command dotnet run diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md index 7e133a97f..59291ddd9 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Mac/index.md @@ -4,25 +4,25 @@ title: Get started with Meadow.Mac subtitle: "To get up and running with Meadow.Mac, follow these steps:" --- -Meadow.Mac offers an environment for developing Meadow code that can run on macOS, and with an extra add-on can even access general-purpose input/output (GPIO) pins. Developing with Meadow.Mac requires setting up your development machine with some prerequisites. Then, after connecting any external components, you can deploy and run your Meadow application. +Meadow.Mac provides an environment for developing Meadow applications on macOS, with optional GPIO support through an add-on accessory. Setting up Meadow.Mac requires configuring your development machine and connecting external components before you can deploy and run your Meadow application. -Running Meadow applications on Mac can provide a very convenient development loop for prototyping and testing your Meadow applications, quickly iterating and seeing the result of code changes, potentially using the same components you would use on a Meadow Feather or Core-Compute module by way of an FTDI breakout board such as FT232H that provide GPIO, SPI, and I2C. (In this early stage, Meadow.Mac does not yet support I2C.) +unning Meadow applications on a Mac offers a convenient and fast development loop for prototyping and testing. You can use the same components you would on a Meadow Feather or Core-Compute module by connecting an FTDI breakout board like the FT232H, which supports GPIO, SPI, and (in the future) I2C. -You can also quickly prototype graphics using an emulated IDisplay object that renders to a standard window on your Mac machine before deploying them to component displays. Additionally, running Meadow applications on more extensive hardware can also provide capabilities for intensive workloads requiring much more processing power. +Meadow.Mac also allows for prototyping graphics using an emulated IDisplay object, rendering to a standard window on your Mac. Additionally, running Meadow apps on macOS can leverage the hardware's increased processing power for intensive workloads. ## Prerequisites -Before you can get started with Meadow.Mac, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). +Before starting with Meadow.Mac, ensure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). -If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK for Mac from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). +If you haven't already, install the .NET 8.0 SDK for macOS. You can find the latest version on the [.NET downloads page](https://dotnet.microsoft.com/download/dotnet/). ### Using GPIO and SPI -With an additional accessory, you can add GPIO and SPI capabilities to your Mac device. You can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264) to provide GPIO and SPI capabilities. (The FT232H also supports I2C, but in this early stage, Meadow.Mac does not yet support it.) +To add GPIO and SPI capabilities to your Mac, you can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264). ## Create your first Meadow.Mac app -1. Create a new dotnet app on your development machine and navigate to that new project. +1. Create a new .NET console application: ```command dotnet new console --output MeadowMacSampleApp @@ -31,13 +31,13 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Mac This will ensure the Meadow app has the project settings that will work within Meadow.Mac. -1. Add the Meadow.Mac NuGet reference to your project. +1. Add the Meadow.Mac NuGet package: ```command dotnet add package Meadow.Mac ``` -1. Replace the contents of the `Program.cs` file in your project with the following. +1. Replace the contents of the `Program.cs` file: ```csharp using Meadow; @@ -68,21 +68,21 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Mac } ``` - This is a simple Meadow.Mac app that will output some messages to the console at various stages of the Meadow app. + This simple app logs messages at various stages of the Meadow application. -1. Build the app for your development machine using either Visual Studio or the `dotnet` tool. +1. Build the app: ```command dotnet build ``` -1. Run the app. +1. Run the app: ```command dotnet run ``` - At the end of the Meadow app launch output, you should see the following output from your app. + The console output should display: ```console Initialize... @@ -90,21 +90,19 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Mac Hello, Meadow.Mac! ``` -You have a Meadow.Mac app running on your Mac development machine. You can continue to develop and test your Meadow app on your development machine. When you are ready, you can deploy your Meadow app to other Mac device like any other .NET app. +You now have a Meadow.Mac app running on your Mac development machine. ## Adapt a Meadow app for Meadow.Mac -You can also modify an existing Meadow app to run on Meadow.Mac by adjusting the `App` type and adding the following static `Main` method to your `MeadowApp` class. +To update an existing Meadow app to target a Mac, adjust the app’s structure as follows: -For example, here are the changes to make the MeadowApp class work on Meadow.Mac, configured for Mac. - -1. Configure the project type to be an executable by changing the project output type to a .NET 7 executable in the project file. +1. Update the project file to target an executable: ```xml Exe - net7.0 + net8.0 enable enable @@ -113,7 +111,7 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Mac ``` -1. Within the Meadow app's class file, change the `App` type to align with your target Meadow.Mac device: `App`. +1. Change the app type to Meadow.Mac: ```csharp public class MeadowApp : App @@ -122,7 +120,7 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Mac } ``` -1. Within the `MeadowApp` class, or whatever your app's class name is, add a new `Main` method to give the app a target to launch when the app is run. +1. Add the Main method to the MeadowApp class: ```csharp public static async Task Main(string[] args) @@ -131,8 +129,8 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Mac } ``` -Your Meadow app should now be able to run on Meadow.Mac, calling into the usual `Initialize` and `Run` methods of your app. +Your Meadow app should now be able to run on macOS, calling into the usual `Initialize` and `Run` methods of your app. ## Next steps -Now that you have your Meadow.Mac device set up and your first Meadow app running on it, you can start working with the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add functionality to your Meadow app. Check out the other [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/Mac). +With your Meadow.Mac setup complete, start exploring the [Meadow.Foundation](../../../Meadow.Foundation/Getting_Started/) libraries to add functionality to your Meadow app. You can also find additional examples in the [samples in the Meadow.Desktop.Samples](https://github.com/WildernessLabs/Meadow.Samples/tree/main/Source/Mac). diff --git a/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md b/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md index d211d3fef..fbe3fa8f7 100644 --- a/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md +++ b/docs/Meadow/Meadow_Desktop/Meadow_Windows/index.md @@ -12,17 +12,17 @@ You can also quickly prototype graphics using an emulated IDisplay object that r ## Prerequisites -Before you can get started with Meadow.Windows, make sure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). +Before starting with Meadow.Windows, ensure your [development machine is set up for Meadow development](/Meadow/Getting_Started/Hello_World/). -If not installed already, install the .NET 8.0 SDK. You can find the latest version of the .NET SDK for Windows from the [.NET downloads](https://dotnet.microsoft.com/download/dotnet/). +If you haven't already, install the .NET 8.0 SDK for macOS. You can find the latest version on the [.NET downloads page](https://dotnet.microsoft.com/download/dotnet/). ### Using GPIO and SPI -With an additional accessory, you can add GPIO and SPI capabilities to your Windows device. You can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264) to provide GPIO and SPI capabilities. (The FT232H also supports I2C, but in this early stage, Meadow.Windows does not yet support it.) +To add GPIO and SPI capabilities to your Windows PC, you can use an [FTDI breakout board such as FT232H](https://www.adafruit.com/product/2264). ## Create your first Meadow.Windows app -1. Create a new dotnet app on your development machine and navigate to that new project. +1. Create a new .NET console application: ```command dotnet new console --output MeadowWindowsSampleApp @@ -31,13 +31,13 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Wind This will ensure the Meadow app has the project settings that will work within Meadow.Windows. -1. Add the Meadow.Windows NuGet reference to your project. +1. Add the Meadow.Windows NuGet package: ```command dotnet add package Meadow.Windows ``` -1. Replace the contents of the `Program.cs` file in your project with the following. +1. Replace the contents of the `Program.cs` file: ```csharp using Meadow; @@ -68,21 +68,21 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Wind } ``` - This is a simple Meadow.Windows app that will output some messages to the console at various stages of the Meadow app. + This simple app logs messages at various stages of the Meadow application. -1. Build the app for your development machine using either Visual Studio or the `dotnet` tool. +1. Build the app: ```command dotnet build ``` -1. Run the app. +1. Run the app: ```command dotnet run ``` - At the end of the Meadow app launch output, you should see the following output from your app. + The console output should display: ```console Initialize... @@ -90,21 +90,19 @@ With an additional accessory, you can add GPIO and SPI capabilities to your Wind Hello, Meadow.Windows! ``` -You have a Meadow.Windows app running on your Windows development machine. You can continue to develop and test your Meadow app on your development machine. When you are ready, you can deploy your Meadow app to other Windows device like any other .NET app. +You now have a Meadow.Windows app running on your Windows development machine. ## Adapt a Meadow app for Meadow.Windows -You can also modify an existing Meadow app to run on Meadow.Windows by adjusting the `App` type and adding the following static `Main` method to your `MeadowApp` class. +To update an existing Meadow app to target a Windows PC, adjust the apps structure as follows: -For example, here are the changes to make the MeadowApp class work on Meadow.Windows, configured for Windows. - -1. Configure the project type to be an executable by changing the project output type to a .NET 7 executable in the project file. +1. Update the project file to target an executable: ```xml Exe - net7.0 + net8.0 enable enable @@ -113,7 +111,7 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Win ``` -1. Within the Meadow app's class file, change the `App` type to align with your target Meadow.Windows device: `App`. +1. Change the app type to Meadow.Windows: ```csharp public class MeadowApp : App @@ -122,7 +120,7 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Win } ``` -1. Within the `MeadowApp` class, or whatever your app's class name is, add a new `Main` method to give the app a target to launch when the app is run. +1. Add the Main method to the MeadowApp class: ```csharp public static async Task Main(string[] args) @@ -131,7 +129,7 @@ For example, here are the changes to make the MeadowApp class work on Meadow.Win } ``` -Your Meadow app should now be able to run on Meadow.Windows, calling into the usual `Initialize` and `Run` methods of your app. +Your Meadow app should now be able to run on Windows, calling into the usual `Initialize` and `Run` methods of your app. ## Next steps From 0c2b90681403cb9752ebbefbb17e254112f0d05c Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 13 May 2025 15:15:36 -0700 Subject: [PATCH 6/9] Update v2.2.0 release notes --- docs/Meadow/Release_Notes/v2/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Meadow/Release_Notes/v2/index.md b/docs/Meadow/Release_Notes/v2/index.md index 573db4000..1ab045a0d 100644 --- a/docs/Meadow/Release_Notes/v2/index.md +++ b/docs/Meadow/Release_Notes/v2/index.md @@ -12,7 +12,7 @@ subtitle: Release Notes ## v2.2.0.0 -This is a full stack release that brings general stability improvements and fixes to the Meadow platform. +This is a full stack release that brings general stability improvements and fixes to the Meadow platform along with Blazor Server support via [Meadow.Blazor](https://developer.wildernesslabs.dev/Meadow/Meadow_Desktop/Meadow_Blazor/)! ### Meadow.OS @@ -21,7 +21,7 @@ This is a full stack release that brings general stability improvements and fixe ### Meadow.Core -* Added support for Blazor Server +* Added [Meadow.Blazor](https://developer.wildernesslabs.dev/Meadow/Meadow_Desktop/Meadow_Blazor/) which brings support for Meadow with Blazor Server * Sequential OTA update packages are now correctly cleared from local store after being applied * Analog inputs have been refactored and separated into `IAnalogInputPort` and `IObservableAnalogInputPort` interfaces. If you were using `IAnalogInputPort` before and no longer compile due to missing polling methods, swap to the `IObservalbleAnalogInputPort` interface. * `Meadow.Units` can now be serialized to and deserialized from JSON(`MicroJson` and `System.Text.Json` are supported) From d02a37275703bd758eaeac23a1730a4f3b62a82c Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 15 Jul 2025 14:55:44 -0700 Subject: [PATCH 7/9] Update release notes --- docs/Meadow/Release_Notes/v2/index.md | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/Meadow/Release_Notes/v2/index.md b/docs/Meadow/Release_Notes/v2/index.md index 1ab045a0d..8277b1c06 100644 --- a/docs/Meadow/Release_Notes/v2/index.md +++ b/docs/Meadow/Release_Notes/v2/index.md @@ -10,6 +10,40 @@ subtitle: Release Notes * [Meadow.CLI](/Meadow/Meadow_Tools/Meadow_CLI/) * [Meadow.OS](/Meadow/Getting_Started/Deploying_Meadow%2EOS/) +## v2.3.0.0 + +This is a full-stack (Meadow.OS + managed stack) that includes a huge rework of the OtA functionality of Meadow.OS, making it far simpler and easier to use. It also includes a number of performance enhancements, bug fixes, and a host of new industrial peripheral drivers. Some of the major improvements include: + +* **Automatic OtA** - Over-the-Air (OtA) updates just got a whole lot simpler to use. Instead of having to opt-in to updates and approve the update notification in app code, when you send an update now, it will automatically be applied as long as the device is registered with Meadow.Cloud and has cloud integration turned on. The device can still opt out, or apply the update at a later time, but by default, they will now get applied. This reduces the amount of code needed to integrate OtA into your application to effectively zero. +* **Performance Updates** - Both the file system and TCP/IP stack got big performance updates. +* **Industrial Peripheral Drivers** - A number of peripheral drivers for industrial applications have been added. + +### Meadow.OS + +* TCP/IP write buffering is now also enabled for non-WiFi adapters, bringing enhanced performance. +* `Path.GetTempPath()` and `Path.GetTempFileName()` now work correctly on Meadow. +* `File.Move` now uses `Rename()` instead of doing a copy+delete. +* Fixed “Unable to monitor frequency on pins PB14 or PB15” issue. +* Flash storage driver was improved for better system responsiveness while writing to the filesystem. +* Removed extraneous platform support in the .NET BCL port resolving several runtime issues and reducing the size of the runtime. + +### Meadow.Core + +* Fully reworked the Meadow.Cloud update service logic for improved reliability and resiliency and updates by default +* Fixed F7 CPU temp occasionally wildly wrong +* Separated and extracted `IAnalogInput` port and `IObservableAnalogInputPort` +* Added new Bluetooth events for `ClientConnected` and `ClientDisconnected` + +### Meadow.Foundation + +* Added **Temco SPM1-X** driver +* Added **Temco T3-22i** driver +* Added **AB0805** RTC driver +* Added **MicroScheduler** library +* **MicroGraphics** performance optimizations +* **MicroLayout** API cleanup +* **FT232H** API fixes + ## v2.2.0.0 This is a full stack release that brings general stability improvements and fixes to the Meadow platform along with Blazor Server support via [Meadow.Blazor](https://developer.wildernesslabs.dev/Meadow/Meadow_Desktop/Meadow_Blazor/)! From a89cd291f3411f36b39057dc0fc19ee405944614 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 15 Jul 2025 14:56:31 -0700 Subject: [PATCH 8/9] Update peripheral docs --- .../Meadow.Foundation.ICs.ADC.Ads7128.md | 1 - .../Meadow.Foundation.ICs.ADC.Hp4067.md | 12 ++ .../Meadow.Foundation.ICs.ADC.Mcp3001.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3002.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3004.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3008.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3201.md | 68 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3202.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3204.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Mcp3208.md | 66 +++++++++ .../Meadow.Foundation.ICs.ADC.Nxp74HC4051.md | 57 ++++++++ .../Meadow.Foundation.ICs.ADC.Nxp74HC4067.md | 12 ++ ...dow.Foundation.ICs.IOExpanders.Mcp23017.md | 39 +++--- ...adow.Foundation.ICs.IOExpanders.Tca9535.md | 1 - ....Foundation.IOExpanders.SimulatedT322ai.md | 12 ++ .../Meadow.Foundation.IOExpanders.T322ai.md | 12 ++ ...Meadow.Foundation.IOExpanders.T38i8o6do.md | 12 ++ .../Meadow.Foundation.RTCs.Ab0805.md | 130 ++++++++++++++++++ .../Meadow.Foundation.RTCs.Pcf8523.md | 38 ++++- .../Meadow.Foundation.Sensors.Power.Spm1x.md | 12 ++ ....Foundation.Sensors.Temperature.Max6675.md | 1 - ...Foundation.Sensors.Temperature.Mlx90614.md | 1 - 22 files changed, 841 insertions(+), 29 deletions(-) create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Hp4067.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4051.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4067.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.SimulatedT322ai.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805.md create mode 100644 docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x.md diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads7128.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads7128.md index efbd70c96..6e751b14c 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads7128.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads7128.md @@ -44,4 +44,3 @@ public override async Task Run() [Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Ads7128/Samples/Ads7128_Sample) - diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Hp4067.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Hp4067.md new file mode 100644 index 000000000..b96ac8b0c --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Hp4067.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Hp4067 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Hp4067 +--- + +| Hp4067 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.AnalogMux | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001.md new file mode 100644 index 000000000..5771f197d --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3001 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001 +--- + +| Mcp3001 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3001 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3001(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3001_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002.md new file mode 100644 index 000000000..64433b4e8 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3002 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002 +--- + +| Mcp3002 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3202 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3202(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3002_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004.md new file mode 100644 index 000000000..5e091482b --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3004 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004 +--- + +| Mcp3004 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3004 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3004(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3004_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008.md new file mode 100644 index 000000000..465c0d83f --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3008 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008 +--- + +| Mcp3008 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3008 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3008(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3008_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201.md new file mode 100644 index 000000000..e3822501a --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201.md @@ -0,0 +1,68 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3201 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201 +--- + +| Mcp3201 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3201 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D14); + + mcp = new Mcp3201(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts}V, old: {result.Old?.Volts}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts}V, old: {result.Old?.Volts}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + Resolver.Log.Info("Run complete"); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3201_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202.md new file mode 100644 index 000000000..d6c154256 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3202 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202 +--- + +| Mcp3202 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3202 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3202(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3202_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204.md new file mode 100644 index 000000000..0acf959c0 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3204 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204 +--- + +| Mcp3204 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3204 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3204(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3204_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208.md new file mode 100644 index 000000000..d530f4851 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208.md @@ -0,0 +1,66 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Mcp3208 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208 +--- + +| Mcp3208 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.Mcp3xxx | + ### Code Example + +```csharp +Mcp3208 mcp; + +IObservableAnalogInputPort port; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize"); + + IDigitalOutputPort chipSelectPort = Device.CreateDigitalOutputPort(Device.Pins.D01); + + mcp = new Mcp3208(Device.CreateSpiBus(), chipSelectPort); + + port = mcp.CreateAnalogInputPort(mcp.Pins.CH0, 32, TimeSpan.FromSeconds(1), new Voltage(3.3, Voltage.UnitType.Volts), Mcp3xxx.InputType.SingleEnded); + + port.Updated += (s, result) => + { + Resolver.Log.Info($"Analog event, new voltage: {result.New.Volts:N2}V, old: {result.Old?.Volts:N2}V"); + }; + + var observer = IObservableAnalogInputPort.CreateObserver( + handler: result => + { + Resolver.Log.Info($"Analog observer triggered; new: {result.New.Volts:n2}V, old: {result.Old?.Volts:n2}V"); + }, + filter: result => + { + if (result.Old is { } oldValue) + { + return (result.New - oldValue).Abs().Volts > 0.1; + } + else { return false; } + } + ); + port.Subscribe(observer); + + return base.Initialize(); +} + +public override Task Run() +{ + Resolver.Log.Info("Run"); + + port.StartUpdating(); + + return Task.CompletedTask; +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.Mcp3xxx/Samples/Mcp3208_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4051.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4051.md new file mode 100644 index 000000000..ea0d01231 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4051.md @@ -0,0 +1,57 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Nxp74HC4051 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4051 +--- + +| Nxp74HC4051 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.AnalogMux | + ### Code Example + +```csharp +private Nxp74HC4051 mux; + +public override Task Initialize() +{ + Resolver.Log.Info("Initialize..."); + + mux = new Nxp74HC4051( + Device.CreateAnalogInputPort(Device.Pins.A00), // input + Device.CreateDigitalOutputPort(Device.Pins.D00), // s0 + Device.CreateDigitalOutputPort(Device.Pins.D01), // s1 + Device.CreateDigitalOutputPort(Device.Pins.D02), // s2 + Device.CreateDigitalOutputPort(Device.Pins.D03) // enable + ); + + return base.Initialize(); +} + +public override Task Run() +{ + Task.Run(ReadRoundRobin); + + return base.Run(); +} + +public async Task ReadRoundRobin() +{ + while (true) + { + for (var channel = 0; channel < 8; channel++) + { + mux.SetInputChannel(channel); + var read = await mux.Signal.Read(); + Resolver.Log.Info($"ADC Channel {channel} = {read.Volts:0.0}V"); + await Task.Delay(1000); + } + } +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux/Samples/Nxp74HC4051_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4067.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4067.md new file mode 100644 index 000000000..5e57b1eb3 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4067.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.ICs.ADC.Nxp74HC4067 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Nxp74HC4067 +--- + +| Nxp74HC4067 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.ADC.AnalogMux/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.ICs.ADC.AnalogMux | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp23017.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp23017.md index 2aaab451b..211372683 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp23017.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp23017.md @@ -29,8 +29,8 @@ public override Task Run() { while (true) { - TestBulkDigitalOutputPortWrites(20); - TestDigitalOutputPorts(2); + //TestBulkDigitalOutputPortWrites(20); + TestDigitalOutputPorts(20); } } @@ -45,30 +45,31 @@ private void TestDigitalOutputPorts(int loopCount) var out06 = mcp.CreateDigitalOutputPort(mcp.Pins.GPA6); var out07 = mcp.CreateDigitalOutputPort(mcp.Pins.GPA7); + var out10 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB0); + var out11 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB1); + var out12 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB2); + var out13 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB3); + var out14 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB4); + var out15 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB5); + var out16 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB6); + var out17 = mcp.CreateDigitalOutputPort(mcp.Pins.GPB7); + var outputPorts = new List() { - out00, out01, out02, out03, out04, out05, out06, out07 + out00, out01, out02, out03, out04, out05, out06, out07, + out10, out11, out12, out13, out14, out15, out16, out17 }; - foreach (var outputPort in outputPorts) - { - outputPort.State = true; - } - for (int l = 0; l < loopCount; l++) { - // loop through all the outputs - for (int i = 0; i < outputPorts.Count; i++) + foreach (var outputPort in outputPorts) { - // turn them all off - foreach (var outputPort in outputPorts) - { - outputPort.State = false; - } - - // turn on just one - outputPorts[i].State = true; - Thread.Sleep(250); + Resolver.Log.Info($"{outputPort.Pin.Name} on"); + outputPort.State = true; + Thread.Sleep(500); + Resolver.Log.Info($"{outputPort.Pin.Name} off"); + outputPort.State = false; + Thread.Sleep(500); } } diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9535.md b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9535.md index bd5093b02..202dde15c 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9535.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9535.md @@ -50,4 +50,3 @@ public override async Task Run() [Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/ICs.IOExpanders.Tca95x5/Samples/Tca9535_Sample) - diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.SimulatedT322ai.md b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.SimulatedT322ai.md new file mode 100644 index 000000000..67f5ef882 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.SimulatedT322ai.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.IOExpanders.SimulatedT322ai +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.SimulatedT322ai +--- + +| SimulatedT322ai | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.IOExpanders.Temco.T3xxx | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai.md b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai.md new file mode 100644 index 000000000..5896f1bdd --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.IOExpanders.T322ai +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai +--- + +| T322ai | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.IOExpanders.Temco.T3xxx | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do.md b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do.md new file mode 100644 index 000000000..9b026766f --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.IOExpanders.T38i8o6do +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do +--- + +| T38i8o6do | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/IOExpanders.T3xxx/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.IOExpanders.Temco.T3xxx | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805.md b/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805.md new file mode 100644 index 000000000..e632e8115 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805.md @@ -0,0 +1,130 @@ +--- +uid: Meadow.Foundation.RTCs.Ab0805 +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805 +--- + +| Ab0805 | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/RTCs.Ab0805) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/RTCs.Ab0805/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.RTCs.Ab0805 | + ### Code Example + +```csharp +private Ab0805 rtc; + +public override Task Initialize() +{ + Resolver.Log.Info("Initializing..."); + + rtc = new Ab0805(Device.CreateI2cBus()); + + return base.Initialize(); +} + +public override async Task Run() +{ + // Test basic RTC functionality + await TestBasicRTC(); + + // Test countdown timers + await TestCountdownTimers(); +} + +private async Task TestBasicRTC() +{ + Resolver.Log.Info("=== Testing Basic RTC Functionality ==="); + + var running = rtc.IsRunning; + Resolver.Log.Info($"RTC {(running ? "is running" : "is not running")}"); + + if (!running) + { + Resolver.Log.Info("Starting RTC..."); + rtc.IsRunning = true; + } + + var currentTime = rtc.GetTime(); + Resolver.Log.Info($"RTC current time: {currentTime:MM/dd/yy HH:mm:ss}"); + + // Set RTC to a known time for testing + var testTime = new DateTime(2025, 6, 15, 14, 30, 0); + Resolver.Log.Info($"Setting RTC to: {testTime:MM/dd/yy HH:mm:ss}"); + rtc.SetTime(testTime); + + currentTime = rtc.GetTime(); + Resolver.Log.Info($"RTC time after setting: {currentTime:MM/dd/yy HH:mm:ss}"); + + await Task.Delay(2000); + + currentTime = rtc.GetTime(); + Resolver.Log.Info($"RTC time after 2 second delay: {currentTime:MM/dd/yy HH:mm:ss}"); +} + +private async Task TestCountdownTimers() +{ + Resolver.Log.Info("\n=== Testing Countdown Timer Functionality ==="); + + await TestAlarm(); + await Task.Delay(1000); + await TestBasicTimer(); +} + +private async Task TestBasicTimer() +{ + Resolver.Log.Info("\n--- Test 1: Basic 2-second countdown timer ---"); + + rtc.ResetTimer(); + + Resolver.Log.Info("Starting 2-second countdown timer..."); + rtc.StartTimer(5, Ab0805.DelayTimeUnit.Seconds); + + var startTime = DateTime.Now; + TimeSpan elapsed; + + while (rtc.HasTimerEnded == false) + { + elapsed = DateTime.Now - startTime; + Resolver.Log.Info($"Elapsed: {elapsed.TotalSeconds:F1}s"); + + await Task.Delay(1000); + } + + elapsed = DateTime.Now - startTime; + Resolver.Log.Info($"✓ Timer completed! Interrupt fired after {elapsed.TotalSeconds:F1}s"); + rtc.ResetTimer(); +} + +private async Task TestAlarm() +{ + Resolver.Log.Info("\n--- Test 2: Alarm 5 seconds in the future ---"); + + DateTimeOffset alarmTime = rtc.GetTime().AddSeconds(5); + + Resolver.Log.Info("Monitoring alarm..."); + rtc.SetAlarm(alarmTime); + + var startTime = DateTime.Now; + TimeSpan elapsed; + + while (rtc.HasAlarmTriggered == false) + { + elapsed = DateTime.Now - startTime; + Resolver.Log.Info($"Elapsed: {elapsed.TotalSeconds:F1}s"); + + await Task.Delay(1000); + } + + elapsed = DateTime.Now - startTime; + Resolver.Log.Info($"✓ Alarm triggered! Interrupt fired after {elapsed.TotalSeconds:F1}s"); + + await Task.Delay(5000); + rtc.ResetAlarm(); +} + +``` + +[Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/RTCs.Ab0805/Samples/Ab0805_Sample) + + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Pcf8523.md b/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Pcf8523.md index 801ba9085..80b7434ef 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Pcf8523.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Pcf8523.md @@ -23,9 +23,9 @@ public override Task Initialize() return base.Initialize(); } -public override Task Run() +public override async Task Run() { - var dateTime = new DateTimeOffset(); + DateTimeOffset dateTime; var running = rtc.IsRunning; Resolver.Log.Info($"{(running ? "is running" : "is not running")}"); @@ -37,16 +37,42 @@ public override Task Run() } dateTime = rtc.GetTime(); - Resolver.Log.Info($" RTC current time is: {dateTime.ToString("MM/dd/yy HH:mm:ss")}"); + Resolver.Log.Info($" RTC current time is: {dateTime:MM/dd/yy HH:mm:ss}"); - Resolver.Log.Info($" Setting RTC to : {dateTime.ToString("MM/dd/yy HH:mm:ss")}"); dateTime = new DateTime(2030, 2, 15); + Resolver.Log.Info($" Setting RTC to : {dateTime:MM/dd/yy HH:mm:ss}"); rtc.SetTime(dateTime); dateTime = rtc.GetTime(); - Resolver.Log.Info($" RTC current time is: {dateTime.ToString("MM/dd/yy HH:mm:ss")}"); + Resolver.Log.Info($" RTC current time is: {dateTime:MM/dd/yy HH:mm:ss}"); - return base.Run(); + // Test Timer A + Resolver.Log.Info("Setting Timer A for 5 seconds..."); + rtc.SetTimerA(5, DelayTimeUnit.Seconds); + + // Test Timer B + Resolver.Log.Info("Setting Timer B for 2 seconds..."); + rtc.SetTimerB(2, DelayTimeUnit.Seconds); + + await Task.Delay(2000); + + if (rtc.HasTimerAInterruptTriggered) + { + Resolver.Log.Info("Timer A SUCCESS"); + } + else + { + Resolver.Log.Info("Timer A FAILED"); + } + + if (rtc.HasTimerBInterruptTriggered) + { + Resolver.Log.Info("Timer B SUCCESS"); + } + else + { + Resolver.Log.Info("Timer B FAILED"); + } } ``` diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x.md b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x.md new file mode 100644 index 000000000..c18a8d3a3 --- /dev/null +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x.md @@ -0,0 +1,12 @@ +--- +uid: Meadow.Foundation.Sensors.Power.Spm1x +slug: /docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x +--- + +| Spm1x | | +|--------|--------| +| Status | Status badge: working | +| Source code | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/Sensors.Power.Spm1x) | +| Datasheet(s) | [GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/Sensors.Power.Spm1x/Datasheet) | +| NuGet package | NuGet Gallery for Meadow.Foundation.Sensors.Power.Spm1x | + diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Max6675.md b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Max6675.md index 6252d70aa..4143aac5e 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Max6675.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Max6675.md @@ -45,4 +45,3 @@ public override async Task Run() [Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/Sensors.Temperature.Max6675/Samples/Max6675_Sample) - diff --git a/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Mlx90614.md b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Mlx90614.md index 0f4b870a8..506acc317 100644 --- a/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Mlx90614.md +++ b/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Temperature.Mlx90614.md @@ -30,4 +30,3 @@ public override async Task Run() [Sample project(s) available on GitHub](https://github.com/WildernessLabs/Meadow.Foundation/tree/main/Source/Meadow.Foundation.Peripherals/Sensors.Temperature.Mlx90614/Samples/Mlx90614_Sample) - From d0e1d2c0c8cb7bb3fec4d379a135fc384907e846 Mon Sep 17 00:00:00 2001 From: Adrian Stevens Date: Tue, 15 Jul 2025 15:04:24 -0700 Subject: [PATCH 9/9] Update peripherals table --- .../Meadow.Foundation/Peripherals/index.md | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/Meadow/Meadow.Foundation/Peripherals/index.md b/docs/Meadow/Meadow.Foundation/Peripherals/index.md index 3a37239cc..673ca4abb 100644 --- a/docs/Meadow/Meadow.Foundation/Peripherals/index.md +++ b/docs/Meadow/Meadow.Foundation/Peripherals/index.md @@ -218,18 +218,18 @@ External peripheral drivers can be added to Meadow projects individually and are | Status badge: working | [Ads1015](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads1015) | Ads1015 analog digital converter driver | | Status badge: working | [Ads1115](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Ads1115) | Ads1115 analog digital converter driver | -#### ICs.ADCs.Mcp3xxx +#### ICs.ADC.Mcp3xxx | Status | Driver | Description | |--------|--------|-------------| -| Status badge: working | [Mcp3001](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3001) | Mcp3001 analog digital converter driver | -| Status badge: working | [Mcp3002](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3002) | Mcp3002 analog digital converter driver | -| Status badge: working | [Mcp3004](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3004) | Mcp3004 analog digital converter driver | -| Status badge: working | [Mcp3008](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3008) | Mcp3008 analog digital converter driver | -| Status badge: working | [Mcp3201](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3201) | Mcp3201 analog digital converter driver | -| Status badge: working | [Mcp3202](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3202) | Mcp3202 analog digital converter driver | -| Status badge: working | [Mcp3204](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3204) | Mcp3204 analog digital converter driver | -| Status badge: working | [Mcp3208](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Mcp3208) | Mcp3208 analog digital converter driver | +| Status badge: working | [Mcp3001](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3001) | Mcp3001 analog digital converter driver | +| Status badge: working | [Mcp3002](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3002) | Mcp3002 analog digital converter driver | +| Status badge: working | [Mcp3004](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3004) | Mcp3004 analog digital converter driver | +| Status badge: working | [Mcp3008](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3008) | Mcp3008 analog digital converter driver | +| Status badge: working | [Mcp3201](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3201) | Mcp3201 analog digital converter driver | +| Status badge: working | [Mcp3202](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3202) | Mcp3202 analog digital converter driver | +| Status badge: working | [Mcp3204](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3204) | Mcp3204 analog digital converter driver | +| Status badge: working | [Mcp3208](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.ADC.Mcp3208) | Mcp3208 analog digital converter driver | #### ICs.DAC.Mcp492x @@ -313,6 +313,14 @@ External peripheral drivers can be added to Meadow projects individually and are | Status badge: working | [Tca9535](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9535) | Tca9535 IO expander driver | | Status badge: working | [Tca9555](/docs/api/Meadow.Foundation/Meadow.Foundation.ICs.IOExpanders.Tca9555) | Tca9555 IO expander driver | + +#### IOExpanders.Temco.T3xxx + +| Status | Driver | Description | +|--------|--------|-------------| +| Status badge: working | [T322ai](/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T322ai) | T322ai temco driver | +| Status badge: working | [T38i8o6do](/docs/api/Meadow.Foundation/Meadow.Foundation.IOExpanders.T38i8o6do) | T38i8o6do temco driver | + ### Leds | Status | Driver | Description | @@ -343,6 +351,7 @@ External peripheral drivers can be added to Meadow projects individually and are | Status | Driver | Description | |--------|--------|-------------| +| Status badge: working | [RTCs.Ab0805](/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ab0805) | Ab0805 I2C real time clock | | Status badge: working | [RTCs.Ds1307](/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Ds1307) | DS1307 I2C real time clock | | Status badge: working | [RTCs.Pcf8523](/docs/api/Meadow.Foundation/Meadow.Foundation.RTCs.Pcf8523) | Pcf8523 I2C real time clock | @@ -603,6 +612,7 @@ External peripheral drivers can be added to Meadow projects individually and are | Status | Driver | Description | |--------|--------|-------------| | Status badge: working | [Sensors.Power.CurrentTransducer](/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.CurrentTransducer) | Current transducer library | +| Status badge: working | [Sensors.Power.Spm1x](/docs/api/Meadow.Foundation/Meadow.Foundation.Sensors.Power.Spm1x) | Temco Controls' SPM1-X Modbus Single-Phase Power Meter library | #### Sensors.Power.Ina2xx