Blazor IoT Servo Controller Meadow Maple

BLAZOR AND IOT: CONTROL SERVO MOTOR USING HTTP REQUESTS

by

In a connected Internet of Things (IoT) application, you may need to control your electronics from another device. This tutorial will demonstrate how a Blazor web app can make HTTP requests to a Meadow microcontroller, which can then set the angle of a servo motor.

Wiring the Circuit

This project will rely on a single servo motor connected to a Meadow F7 microcontroller board. For a refresher on controlling servo motors with .NET, refer to the previous tutorial.

As you can see, the electronics for this project are very simple. On the software side, create a solution in Visual Studio that contains two projects - one for the microcontroller and one for a web app.

Meadow Code

Create Servo Controller Singleton

Start by creating a new Meadow Application project. Add the Nuget Package Meadow.Foundation.ServoCore. Create a folder called Controllers. To control the servo, create a servo controller class called ServoController within the Controllers folder. For this demonstration, I decided to use a variation of a singleton pattern which only allows a single instance of itself to be created.

using Meadow.Foundation.Servos;
using Meadow.Units;
 
namespace MeadowServo.Mcu.Controllers
{
    public class ServoController
    {
        Servo servo;
 
        public static ServoController Instance { get; private set; }
 
        private ServoController() { }
 
        static ServoController()
        {
            Instance = new ServoController();
        }
 
        public void InitializeServo()
        {
            servo = new Servo(
                MeadowApp.Device.CreatePwmPort(MeadowApp.Device.Pins.D08), NamedServoConfigs.SG90);            
        }
 
        public void SetAngle(int angle)
        {
            servo.RotateTo(new Angle(angle));
        }
    }
}

You can use the instantiated object in both the MeadowApp.cs startup routine and from the Maple Server you will create next by referencing the public Instance property.

Your microcontroller project will invoke the SetAngle() method when an HTTP request is received from the Blazor web app. We will get to that piece soon, but first you need to actually initialize the servo by calling the InitializeServo() method. Open MeadowApp.cs and create a new async method called InitializeAsync().

async Task InitializeAsync()
{
    ServoController.Instance.InitializeServo();
}      

For now, there are no awaited calls, but next you will enable wireless connectivity and connect to the network asynchronously.

Starting the Maple Web Server

One neat feature about Meadow is the ability to run a lightweight web API server called Maple on the microcontroller. Start by declaring a class-level variable.

MapleServer server;

Before starting the Maple server, you will need to enable and configure WiFi.

async Task InitializeAsync()
{
    ServoController.Instance.InitializeServo();
 
    if (!Device.InitWiFiAdapter().Result)
    {
        throw new Exception("Could not initialize the WiFi adapter.");
    }
 
    var connectionResult = await Device.WiFiAdapter.Connect("SSID", "password");
    if (connectionResult.ConnectionStatus != ConnectionStatus.Success)
    {
        throw new Exception($"Cannot connect to network: {connectionResult.ConnectionStatus}");
    }
}  

This will get your device connected to the network. Be sure to replace the SSID and password placeholders with your wireless network name and password. You can now initialize the Maple web server at the end of the InitializeAsync() method.

server = new MapleServer(
    Device.WiFiAdapter.IpAddress, 5417
);

The server will listen for HTTP requests on port 5417. Simply invoke the initializer method, start the server, and set an initial angle as follows.

public MeadowApp()
{            
    InitializeAsync().Wait();            
    server.Start();
    ServoController.Instance.SetAngle(0);
}

Add Request Handler

Next, you must create a handler to respond to those HTTP requests. Create a new folder within your meadow application project called RequestHandlers. Then, add a class called ServoControllerRequestHandler.cs. This class should inherit from RequestHandlerBase contained in the Meadow.Foundation.Web.Maple.Server namespace.

using Meadow.Foundation.Web.Maple.Server;
using Meadow.Foundation.Web.Maple.Server.Routing;
using MeadowServo.Mcu.Controllers;
 
namespace MeadowServo.Mcu.RequestHandlers
{
    public class ServoControllerRequestHandler : RequestHandlerBase
    {
        [HttpGet]
        public void Servo()
        {
            int angle = 0;
            int.TryParse(QueryString["angle"] as string, out angle);
 
            ServoController.Instance.SetAngle(angle);
 
            Context.Response.AddHeader("Access-Control-Allow-Origin", "*");
            Context.Response.ContentType = ContentTypes.Application_Text;
            Context.Response.StatusCode = 200;
            Send($"{angle}").Wait();
        }
    }
}

I have created a single HttpGet request handler called Servo. If my Meadow device connects to the network as 192.168.1.5, for example, this method will handle GET requests sent to http://192.168.1.5:5417/Servo

The purpose of this handler method is to set the angle of the servo motor by invoking the SetAngle() method you wrote earlier in ServoController.cs.

Normally, I would prefer to perform this type of task through an HttpPost request, but there does not seem to be a way, in the current Maple Server implementation, to globally set CORS Access-Control-Allow-Origin in a way that passes pre-flight requests. Hopefully, in future versions, developers have more control over CORS, or at least the ability to handle OPTIONS request routing.

For now, we can use HttpGet request and pass the desired angle as a query parameter by appending something like ?angle=90 to the request URL. You will still need to set the Access-Control-Allow-Origin header in order to accept incoming requests from a different domain (Line 17).

Your microcontroller app is complete! Next you will write a quick Blazor web app to make GET requests to the server that is now running on your Meadow board.

Blazor Code

Add a new Blazor WebAssembly project to your solution. You will need to uncheck the Configure for HTTPS option. Maple Server on Meadow does not yet support SSL, and you will not be able to make calls to an unsecured (http://) resource from a secured (https://) resource.

In your new Blazor project, open Program.cs and change the BaseAddress of the scoped HttpClient included in the scaffolded project. If your Meadow board connects to the network as 192.168.1.5 and you configured your Maple server to listen on port 5417, for example, your code would look like the following.

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("http://192.168.1.5:5417/") });

Next, add a new Razor Component under the Pages folder called Servo.razor and add a range input with values from 0 to 180.

@page "/servo"
@using System.Net.Http
@inject HttpClient Http
 
<h3>Servo Control</h3>
<input type="range" class="form-control-range"       
       min="0"
       max="180"
       step="1"
       @onchange="SetAngle" />
 
@code {
 
}

When the value of the slider changes, a method called SetAngle() will be triggered. That method should make a GET request to the Meadow board's Servo endpoint, passing the current value of the range slider to the endpoint as a query parameter called angle.

@code {
    private int currentAngle = 0;
 
    private async Task SetAngle(ChangeEventArgs e)
    {
        int.TryParse(e.Value.ToString(), out currentAngle);
 
        await Http.GetAsync($"Servo?angle={currentAngle}");
    }
}

The Final Product

Deploy the microcontroller project to Meadow and launch the Blazor web app. Navigate to the page you just created, for example http://localhost:5000/servo. As you adjust the slider position, the angle of your servo motor will change.

In this tutorial, you learned how to communicate with a Meadow microcontroller from a Blazor web app by making HTTP requests. Complete source code for this project is available on GitHub. This technique enables endless possibilities for connected IoT applications. Instead of rotating a servo motor, try updating the text of a LCD character display or poll and log data from a temperature sensor. You could host the Blazor app as a Progressive Web App (PWA) and install it on a mobile device. If you configure a dynamic DNS for Meadow, you could then control the hardware from anywhere. What will you create with this tutorial?


Don't stop learning!

There is so much to discover about C#. That's why I am making my favorite tips and tricks available for free. Enter your email address below to become a better .NET developer.


Did you know?

Our beautiful, multi-column C# reference guides contain more than 150 tips and examples to make it even easier to write better code.

Get your cheat sheets