Windows IoT Core is a young project – and whereas there are already a lot of good tutorials and examples on the internet, and there’s a lot more work to be done before the libraries available can compare with the work done by the Arduino community.

I’ve managed to make servo motors work in the past with Arduino – there’s already a servo project packaged with the Arduino development environment, and that just works out of the box. I was interested to see if I could do this in C# with the Raspberry Pi 3, and I couldn’t find any simple pre-existing code for this. Since I like an interesting technical challenge, I thought this would be a good problem to solve.

First – how do servos work?

A servo is more than just a simple motor which accepts a power supply and spins round – it’s possible to precisely control how much a servo turns. It’s able to do this because the servo is basically made up of a motor, a potentiometer, and a controller. A very simple explanation of how it works is:

  • The controller chip is given a signal – for example, turn the motor to the 90 degree position;
  • The motor’s output spindle is connected to a potentiometer – since the controller chip is able to measure the resitance between terminals of the potentiometer, therefore it’s able to infer the current position of the motor;
  • The controller only powers the motor until the resistance of the potentiometer matches the value it expects when the spindle is at the 90 degree position.

So this explains the mechanical operation of a servo – but what about the signal which is sent to the controller chip? How do we tell it to move to 0 degrees, 90 degrees, or 180 degrees?

It turns out there’s quite a simple answer to this – we send a series of pulses to the controller, which have different widths for different motor positions – this works like this:

  • The controller chip expects a series of digital pulses at a particular frequency;
  • The frequency describes how many pulses are sent per second – so for example, if the time between pulses starting needs to be 20ms, then we’d need to send 50 per second (50 cycles x 20ms = 1000ms).
    • There for the frequency is 50 pulses per second – also called 50Hz.
  • Each signal is made up of two logic states – logic 1 (5 volts) and logic 0 (0 volts);
    • The ratio of time in each cycle spent at logic 1 to the total length of the cycle is called the duty cycle.
    • For example, if the time between pulses starting is 20ms, and the pulse is 2ms at logic 1, then the duty cycle is 10% (2ms/20ms x 100%);

My research suggested that most servos expect pulses at a frequency of 50Hz. They will move to:

  • 0 degree position with a duty cycle of 5% (1ms of logic 1 in a 20ms pulse);
  • 180 degree position with a duty cycle of 10% (2ms of logic 1 in a 20ms pulse);

So my challenge was to find a way for the Raspberry Pi to generate a series of variable width pulses.

This technique of changing the width of pulses is is known as pulse width modulation (PWM).

This is easier said than done with the Raspberry Pi. Whereas the Arduino has several pins that output PWM signals, there are no pins in the Raspberry Pi that obviously output PWM.

Next – can I simulate PWM using C# code?

Well…I gave it a go. My theory was that I could set a pin to logic 1, and then wait for a certain number of milliseconds to pass before setting the pin back to logic zero.

I connected the three wires of the servo to my Raspberry Pi – the 5v wire to Pin 2, the ground wire to Pin 39, and the control wire went to Pin 29 (which was GPIO 5).

rp2_pinout

In order to develop a Windows app for the Raspberry Pi, I created a blank Windows UWP app, and added a reference to the Windows IoT Extensions.

screenshot.1462650773

I then added the code below to the MainPage.xaml.cs file.

This code didn’t work – I’m just including this as part of the path that I followed.

var gpioController = GpioController.GetDefault();
var gpioPin = gpioController.OpenPin(5);
gpioPin.SetDriveMode(GpioPinDriveMode.Output);
    
var _stopwatch = new Stopwatch();
_stopwatch.Start();
    
// number of system ticks in a single millisecond
var ticksPerMs = (ulong)(Stopwatch.Frequency) / 1000;
 
// length of pulse is 20ms (which equates to a frequency of 50Hz)
var pulseDuration = 20;
 
// let the pin sit at logic 1 until 2ms have passed
var logicOneDuration = 2;
 
while (true)
{
    var ticks = _stopwatch.ElapsedTicks;
 
    gpioPin.Write(GpioPinValue.High);
    
    while (true)
    {
        var timePassed = _stopwatch.ElapsedTicks - ticks;
 
        if ((ulong)(timePassed) >= logicOneDuration * ticksPerMs)
        {
            break;
        }
    }
    
    gpioPin.Write(GpioPinValue.Low);
    
    while (true)
    {
        var timePassed = _stopwatch.ElapsedTicks - ticks;
 
        if ((ulong)(timePassed) >= pulseDuration* ticksPerMs)
        {
            break;
        }
    }
}

This experiment wasn’t really successful – theoretically it was sound, but practically I don’t think this method of “bitbanging” is really good enough to give the accuracy necessary for a servo controller. I found this made the servo twitch, but not much else.

I tried a different way – rather than looping until a certain time passed, I thought I’d try blocking the thread for the a number of milliseconds after setting the GPIO pin to high or low…this didn’t really work either, giving more-or-less the same results as the original code (i.e. the servo twitched, but didn’t consistently move in the way I expected it to).

public MainPage()
{
    this.InitializeComponent();
 
    var gpioController = GpioController.GetDefault();
    var gpioPin = gpioController.OpenPin(5);
    gpioPin.SetDriveMode(GpioPinDriveMode.Output);
            
    while (true)
    {
        gpioPin.Write(GpioPinValue.High);
        Task.Delay(2).Wait();
        gpioPin.Write(GpioPinValue.Low);
        Task.Delay(18).Wait();
    }
}

I needed to find another way to generate PWM from a Raspberry Pi 3. Fortunately, Microsoft have provided a technology which solves this problem.

Using Microsoft Lightning Providers to generate PWM

Lightning is new software from Microsoft that implement some new functions, including SPI and PWM support. It’s pretty easy to enable this software – there’s few simple steps.

This learning process was helped by the set-up guide from Microsoft and from the blog post from Lee P. Richardson here.

Change the default controller driver

I opened the online administrative interface for the Pi at http://minwinpc:8080, and navigated to the Devices tab of this interface. This has a dropdown at the top of the page showing the “Default Controller Driver”, which was set to “Inbox Driver”. I opened this dropdown, and selected the second value which is “Direct Memory Mapped Driver”. Once I selected this, I clicked on the button titled “Update Driver”, and was prompted to reboot my Pi.

screenshot.1462663123

When I rebooted the Pi, I looked at the Devices tab of the interface again, and saw that my option was selected.

Download the lightning providers from Nuget

I right clicked on the Windows app project in VS2015, and selected “Manage Nuget Packages…”. This opened the Nuget package manager, and I searched for “Microsoft.IoT.Lightning”. This returned two packages:

  • Microsoft.IoT.Lightning (presently v1.0.4), and
  • Microsoft.IoT.Lightning.Providers (presently v1.0.0);

screenshot.1462663411

Change the package.appxmanifest file to add the new capabilities

I had to make a couple more changes to enable device capabilities. There were changes to the package.appxmanifest file. I needed to make these changes directly to the XML, so I right clicked on the file in VS2015, and selected “View Code”.

First, add the IOT property to the Package node, and add “iot” to the ignoreable namespaces.

<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" 
         xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" 
         xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" 
         xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10" 
         IgnorableNamespaces="uap mp iot">

Next, add the new iot and DeviceCapabilities.

  <Capabilities>
    <Capability Name="internetClient" />
    <iot:Capability Name="lowLevelDevices" />
    <DeviceCapability Name="109b86ad-f53d-4b76-aa5f-821e2ddf2141" />
  </Capabilities>

Add the PWM code for a servo

I found the code worked well – obviously this is proof of concept code, but I found it moved the servo from 0 degrees, to 90 degrees, and then to 180 degrees.

public MainPage()
{
    this.InitializeComponent();
 
    Servo();
}
        
private async void Servo()
{
    if (LightningProvider.IsLightningEnabled)
    {
        LowLevelDevicesController.DefaultProvider = LightningProvider.GetAggregateProvider();
    }
 
    var pwmControllers = await PwmController.GetControllersAsync(LightningPwmProvider.GetPwmProvider());
    if (pwmControllers != null)
    {
        // use the on-device controller
        var pwmController = pwmControllers[1];
 
        // Set the frequency, defaulted to 50Hz
        pwmController.SetDesiredFrequency(50);
 
        // Open pin 5 for pulse width modulation
        var servoGpioPin = pwmController.OpenPin(5);
 
        // Set the Duty Cycle - 0.05 will set the servo to its 0 degree position
        servoGpioPin.SetActiveDutyCyclePercentage(0.05);
 
        // Start PWN from pin 5, and give the servo a second to move to position
        servoGpioPin.Start();
        Task.Delay(1000).Wait();
        servoGpioPin.Stop();
 
        // Set the Duty Cycle - 0.1 will set the servo to its 180 degree position
        servoGpioPin.SetActiveDutyCyclePercentage(0.1);
 
        // Start PWN from pin 5, and give the servo a second to move to position
        servoGpioPin.Start();
        Task.Delay(1000).Wait();
        servoGpioPin.Stop();
    }
}

In the Part 2, I’ll design an interface for the servo library and refine the implementation code.