In this project, you're going to control a servo connected to a network-capable Netduino with Xamarin. Forms app on your phone, making go back and forth to its entire range, or rotate it to a specific angle.
To control a servo through the network, Maple makes it easy to build connected devices with Netduino by exposing control via a Web API. Maple is an open source, ultra-lightweight, JSON enabled, RESTful web server built specifically for Netduino devices with network capabilities.
Servos are integrated packages that usually include a DC electric motor, torque-increasing gearing, and electronics to control the motor. They gained popularity as parts of remote controlled cars, airplanes, etc. , but are now also very commonly used in robotics.
Servos can draw a lot of current, especially under load. Additionally, most common hobby servos require 6V. For this reason, an external power supply should be used when they’re used in practical applications.
Netduino.Foundation is a platform for quickly building connected things using the .NET MicroFramework on Netduino. Created by Wilderness Labs, it's open source and maintained by the Netduino community.
If you're new in Netduino development, I suggest you go to the Getting started with Netduino project to properly set up your development environment.
Step 1 - Assemble the circuitThe circuit for this project looks like this:
When powering with an external power source, you must connect the external GND to the GND rail on the Netduino (as shown in the following schematic and breadboard illustration), or the PWM control signal will not work:
Download and open the Firmware Updater and Configuration Tool for Windows or MacOS while your Netduino is connected in bootloader mode. Before using the Network Configuration section, make sure your device is selected automatically in the Model picker control to confirm that your device is properly connected.
If you're using a Netduino 3 WiFi, select the proper encryption, authentication, SSID and pass-phrase settings and finally click on Update to save those settings to the Netduino.Once the network settings are saved to the device, remember to disconnect and reconnect your Netduino back to your computer to leave bootloader mode and its ready to run the project once its completed.
Step 3 - Create a Netduino ProjectCreate a Netduino project in Visual Studio 2015 for Windows or the latest Visual Studio for Mac; name the project ServoHost.
Step 4 - Add the Netduino.Foundation Maple NuGet PackagesWindows
Right-click on your ServoHost project and click Manage NuGet Packages. In the Browse tab, search for Netduino.Foundation; it should be the first search result. Click the Install button.
Now search for Netduino.Foundation.Servo, Netduino.Foundation.Network and Maple add them to your project.
MacOS
Alt-click on your ServoHost project in the Solution Explorer, and click Add => Add Nuget Package to open the NuGet Package window. Search for the Netduino.Foundation package and click Add Package to add it to your project.
Now search for Netduino.Foundation.Servo and Netduino.Foundation.Network and Maple add them to your project.
Step 5 - Write the code for the ServoHost projectMaple web API endpoints are defined by creating custom classes that inherit from RequestHandlerBase
. Maple uses reflection to create URLs based on the method names in those custom classes. It supports both the get
and post
verbs and method names must be prefixed with either one of those strings in order to be automatically made into an endpoint.
Right click on your project and click Add new => Class, and name it RequestHandler. Here is where you will expose five URL endpoints: /StartSweep, /StopSweep
and /RotateTo
. Copy the following implementation of all these methods:
public class RequestHandler : RequestHandlerBase
{
public event EventHandler RotateTo = delegate { };
public event EventHandler StopSweep = delegate { };
public event EventHandler StartSweep = delegate { };
public RequestHandler() { }
public void postRotateTo()
{
try
{
int targetAngle = 0;
var param = "targetAngle";
try
{
var temp = this.Body?[param] ?? this.Form?[param] ?? this.QueryString?[param];
targetAngle = int.Parse(temp.ToString());
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
RotateTo(this, new ServoEventArgs(targetAngle));
StatusResponse();
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}
public void postStopSweep()
{
StopSweep(this, EventArgs.Empty);
StatusResponse();
}
public void postStartSweep()
{
StartSweep(this, EventArgs.Empty);
StatusResponse();
}
private void StatusResponse()
{
Context.Response.ContentType = "application/json";
Context.Response.StatusCode = 200;
Send();
}
}
When those endpoints are invoked, the appropriate method is called. On each method, two things are happening: trigger an event and send a response to confirm the client that the request has been received.
Add ServoController class
It is a good practice to create a Controller class for every peripheral involved to make the project more scalable, maintainable and cleaner. These Controller classes abstracts all the peripheral's logic so the main program logic will remain cleaner and easier to understand. Add a ServoController class with the following code:
public class ServoController
{
protected int _rotationAngle;
protected bool _isRotating;
protected Servo _servo;
public ServoController(Servo servo)
{
_servo = servo;
StartSweep();
}
public void RotateTo(int degrees)
{
StopSweep();
_servo.RotateTo(degrees);
}
public void StopSweep()
{
_isRotating = false;
}
public void StartSweep()
{
StopSweep();
_isRotating = true;
Thread _animationThread = new Thread(() =>
{
while (_isRotating)
{
while (_rotationAngle < 180)
{
if (!_isRotating)
break;
_rotationAngle++;
_servo.RotateTo(_rotationAngle);
Thread.Sleep(15);
}
while (_rotationAngle > 0)
{
if (!_isRotating)
break;
_rotationAngle--;
_servo.RotateTo(_rotationAngle);
Thread.Sleep(15);
}
}
});
_animationThread.Start();
}
public void NetworkConnected()
{
StopSweep();
_servo.RotateTo(0);
}
}
Add App class
For this project, we want to implement a common App software pattern to control all the Network and Controllers make things cleaner and robust. Add a new App class to your project, and copy the following code:
using Netduino.Foundation.Network;
using Maple;
using Netduino.Foundation.Servos;
using N = SecretLabs.NETMF.Hardware.Netduino;
using Microsoft.SPOT;
namespace ServoHost
{
public class App
{
protected MapleServer _server;
protected ServoController _servoController;
public App()
{
InitializePeripherals();
InitializeWebServer();
}
protected void InitializePeripherals()
{
var _servo = new Servo(N.PWMChannels.PWM_PIN_D11, NamedServoConfigs.Ideal180Servo);
_servoController = new ServoController(_servo);
}
protected void InitializeWebServer()
{
var handler = new RequestHandler();
handler.RotateTo += (s, e) => { _servoController.RotateTo(((ServoEventArgs)e).Angle); }; ;
handler.StopSweep += (s, e) => { _servoController.StopSweep(); };
handler.StartSweep += (s, e) => { _servoController.StartSweep(); };
_server = new MapleServer();
_server.AddHandler(handler);
}
public void Run()
{
Initializer.InitializeNetwork();
Initializer.NetworkConnected += InitializerNetworkConnected;
}
private void InitializerNetworkConnected(object sender, EventArgs e)
{
Debug.Print("InitializeNetwork()");
_server.Start("ServoHost", Initializer.CurrentNetworkInterface.IPAddress);
_servoController.NetworkConnected();
}
}
}
First thing that happens in this class is calling InitializePeripherals to instantiate all the Controllers that are connected to the Netduino, which in this project, is the ServoController object and make it rotate back and forth full range.
After setting up the peripherals, it now calls InitializeWebServer, and here you will use the RequestHandler. After instantiating it, register the event handlers for the four exposed methods explained previously: StartSweep, StopSweep and RotateTo:
- RotateTo - Rotate the servo to a specific angle.
- StopSweep - Stops the servo.
- StartSweep - Makes the servo rotate full range back and forth.
After all those event handlers, create a new Maple instance, and assign your RequestHandler object to its Handler property.
In the Run method, the first thing you need to do when starting the project is to use Initializer.InitializeNetwork();. Netduino boards need to initialize their network every time they're powered on, so before starting the Maple server, you need to make sure that the Netduino has connected to the network successfully, and that it has received a valid IP address.
When the Netduino has joined the network, you can now safely start the Maple server by doing server.Start() passing the name of the server and the IpAddress assigned to it, and set the stop the Servo from cycling to indicate that the program has launched successfully, and its ready to receive client requests.
Implement Main method in the Program class
Lastly, create a new App class object and invoke the Run method. Your code should look like this:
using System.Threading;
namespace ServoHost
{
public class Program
{
public static void Main()
{
App app = new App();
app.Run();
Thread.Sleep(Timeout.Infinite);
}
}
}
Run the ServoHost Project
That is everything that you need to do for the ServoHost project. When you run the project, you will see the servo rotating full range when finished initializing peripherals and its now waiting to receive an IP Address once its connected to the network. Once it has joined the network the servo will stop rotating and go to angle zero.
Next part in this project getting the Xamarin.Forms app running on your phone. This project can be found in the Netduino_Samples/Connected_Servo repo. The code that runs on the Netduino is in the ServoHost app folder. The Xamarin.Forms sample its in the ServoRemote folder. Go ahead and download the ServoRemote project to your computer/mac, and open it on the latest version of Visual Studio available, and the common code project should look like this:
The take away for this project is the how we connect to maple using the MapleClient NuGet Package, which has a simple implementation of a HttpClient to send/receive server requests to/from the Maple server, and a UdpClient to receive UDP broadcast messages coming from one or more Maple servers running, and the app will list them on a Picker so you can select to which Netduino you wish to connect to and send requests.
Lastly, the class where you send the API requests to maple is in the ServoClient class. Notice that it extends from MapleClient, and it has the three requests control your Servo on your Netduino.
public class ServoClient : MapleClient
{
public async Task<bool> RotateToAsync(ServerItem server, int degrees)
{
return (await SendCommandAsync("RotateTo?targetAngle="+degrees, server.IpAddress));
}
public async Task<bool> StartSweepAsync(ServerItem server)
{
return (await SendCommandAsync("StartSweep", server.IpAddress));
}
public async Task<bool> StopSweepAsync(ServerItem server)
{
return (await SendCommandAsync("StopSweep", server.IpAddress));
}
}
The SendCommandAsync function will return a boolean value to indicate if the request was processed correctly (true) or there was an error in the request or an exception has occurred (false).
Every time a request is sent, the app will display a loading screen, and once the response message returns successful, the loading screen disappears and the buttons are re-enabled, with the highlighted button reflecting which action is currently happening on the servo.
Run the ServoHost project first, and wait till the servo stops cycling to indicate that the server is now running and broadcasting the UDP message of its name and IP address.
Now run the ServoRemote project to your mobile device, and wait a moment till the app displays a Picker control, where you can choose to which server you wish to connect to. If the connection was successful, the setup dialog screen should disappear, and the servo should start cycling again.
This project is only the tip of the iceberg in terms of the extensive exciting things you can do with Netduino.Foundation.
- It comes with a huge peripheral driver library with drivers for the most common sensors and peripherals.
- The peripheral drivers encapsulate the core logic and expose a simple, clean, modern API.
- This project is backed by a growing community that is constantly working on building cool connected things and are always excited to help new-comers and discuss new projects.
Comments