Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Continuous Rotation Servo on the Shield Base controlled with Potentiometer

Shield Base Servo PWM Potentiometer Netduino Go Microsoft.SPOT.Hardware.PWM

  • Please log in to reply
15 replies to this topic

#1 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 21 June 2012 - 04:07 AM

Thanks to the latest PWM Shield Base update I now have a basic continuous rotation servo example to share.

For this example I am using a Parallax Continuous Rotation Servo and a Potentiometer module to control the speed and direction of the servo. To connect it to the Shield Base, attach the red wire to the 5V header, and the black wire to the GND on the Shield Base. The signal wire, in this case yellow, will connect to digital pin D5. Connect the Shield Base to the socket 4 of the Netduino Go! and the Potentiometer module to socket 6. Please note that while the Shield Base is still in beta, it will use an entire channel of sockets. Therefore if it's plugged into socket 4, you can only plug other modules into sockets 5-8.

After creating a new Netduino Go Application you will need to add references to the Potentiometer, Shield Base, and Microsoft.SPOT.Hardware.PWM. Since the Shield Base is still in beta, you will need browse to the location of the beta assembly files (download here), or if you have already used the Shield Base you can find it in the 'Recent' tab of the 'Add References' dialog screen. The Potentiometer and Microsoft.SPOT.Hardware.PWM (ver. 4.2.0.1) assemblies can be found in the '.Net' tab. Once you have added these as references we will add the following to the top of our project's template:
 

using NetduinoGo;

Our next step is to add to the Main method of our project the following code:

ShieldBase shieldbase = new ShieldBase(GoSockets.Socket4);Potentiometer pot = new Potentiometer(GoSockets.Socket6);

This will create two new objects for both the Shield Base and Potentiometer and tell the application to which sockets the modules will be connected.

Next we will create a new PWM object called servo. The new 4.2 .NetMF PWM class has two constructors; one that is mainly use for actuators such as a Piezo or LED, and the other used for the control of things like a servo. In our example we will initialize a new instance of the PWM class with the PWMChannel that the signal line is connected to, the period, duration, a scale factor, and a value that indicates whether the output is inverted, in our case it is false.

PWM servo = new PWM(shieldbase.PWMChannels.PWM_0, 20000, 1500, PWM.ScaleFactor.Microseconds, false);

Our first argument being passed is the PWMChannel.PWM_0 which is found on D3. The next two arguments set the Period and Duration properties of the servo object in microseconds, because we are using the Microseconds as a scale factor.

In the case of the Parallax servo, it requires a 1.5 ms pulse (1.5 ms is 1500 [color=rgb(68,68,68);font-family:arial, sans-serif;font-size:small;]?[/color]s), continually refreshed every 20 ms to centre it as shown in the following diagram.
Attached File  Servo-centre.gif   5.9KB   86 downloads

The servo will begin to turn clockwise with pulses of any value below 1.5 ms and will gradually increase speed until reaching 1.3 ms, while pausing in between pulses for 20 ms for the smoothest movement.
Attached File  Servo-clockwise.gif   9.67KB   87 downloads

In order to turn the servo counter-clockwise, we will pulse the servo any value above 1.5 ms, and up to the maximum speed at pulses of 1.7 ms.
Attached File  Servo-counterclockwise.gif   9.14KB   57 downloads

WARNING: Not all servos will work under the same conditions, however most do. You should find the appropriate pulse widths for your specific servo to prevent any potential damage.

So how does all this translate into code? Well let's jump into it and see. Our next step will be to add two values that will represent the maximum clockwise speed, and the maximum counter-clockwise speed. Then we'll start the PWM port for an indefinite amount of time with the Start method.

int maxClockwise = 1300;        // Full speed clockwise direction int maxCounterClockwise = 1700; // Full speed counter-clockwise directionservo.Start();   // Start the PWM port.

At this point if we ran our application the servo should stay in its centre position. If you notice that it doesn't you may need to calibrate the servo. To do this gently twist the potentiometer adjustment screw, found just above the wires, with a small Phillips screwdriver until the servo does not turn or vibrate.

The next portion of our code will be to control the servo with the input we receive from the Potentiometer module. The GetValue method of the Potentiometer class will return a float anywhere from 0.0 to 1.0. For our purposes we will want to convert that value to something in the range of 1300 and 1700. To do so we will use a Mapping method that re-maps a number from one range to another.

public static float Map(float input, float inMin, float inMax, float outMin, float outMax){   return ((input - inMin) * (outMax - outMin) / (inMax - inMin) + outMin);}

In our example the input will be the value we receive from GetValue(). The inMin and inMax values are the range of values we would get from the potentiometer, 0 and 1 respectively. The outMin, and outMax are the values that we want to see in our new range, so we will use 1300 and 1700. To see how we implement this in our code, let's setup up an infinite loop to repeatedly poll the potentiometer and set the servo speed and direction accordingly. We will also pause for 20ms before we change the duration of the pulse to the servo.

while (true){    float ptValue = Map(pot.GetValue(), 0, 1, maxClockwise, maxCounterClockwise);    servo.Duration = (uint)ptValue;  // A value from 1300-1700    Thread.Sleep(20);  // Sleep for 20 ms}

Now deploy the application and you should see your servo rotate in cordination with the twisting of your Potentiometer module. If you want to see the actual pulse value that your servo is receiving, you can add the following just before you set the Duration property of your servo.

Debug.Print("Pulse width: " + ptValue);

Enjoy, and have fun!
Steve

Attached Files



#2 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 21 June 2012 - 05:58 AM

Thanks to the latest PWM Shield Base update I now have a basic continuous rotation servo example to share.

Nice write up - you make me wish I had a servo to try it out :)

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#3 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 21 June 2012 - 06:10 AM

Oh awesome. I'm really glad that PWM is working for you now. Now we have to watch out for these dangers: motors, dimmable leds, robots! :) Chris

#4 carb

carb

    Advanced Member

  • Members
  • PipPipPip
  • 352 posts
  • LocationCrystal River, Florida

Posted 21 June 2012 - 10:16 AM

Great job Steve and a very good lesson on servos/programming. It would be perfect if the file contained a free servo in it. B) I will order one tonight. By the way, I was working on building a pinout card for the shield base but only identified 4 pwm channels on it so far. Do you know the pins for all 6 channels? Chuck

#5 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 21 June 2012 - 10:18 AM

Hey Chuck,

Woot, you just posted the 30,000th post!

We should celebrate. If you PM me your address, we'll send you one of the new Piezo Buzzer modules :)

Now, for more important things...

By the way, I was working on building a pinout card for the shield base but only identified 4 pwm channels on it so far. Do you know the pins for all 6 channels?

The two new PWM channels on Shield Base are on D3 and D11. These are consistent with the Arduino pinout...so this should give you enhanced compatibility with Arduino motor/servo shields.

Chris

#6 carb

carb

    Advanced Member

  • Members
  • PipPipPip
  • 352 posts
  • LocationCrystal River, Florida

Posted 21 June 2012 - 10:25 AM

Thanks Chris, You Have Mail! B) Chuck

#7 nakchak

nakchak

    Advanced Member

  • Members
  • PipPipPip
  • 404 posts
  • LocationBristol, UK

Posted 21 June 2012 - 11:04 AM

Really nice write up :) could be fun to build a servo controlled pen plotter/etch a sketch... Nak.

#8 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 21 June 2012 - 11:57 AM

Hey Chuck,

Woot, you just posted the 30,000th post!

We should celebrate. If you PM me your address, we'll send you one of the new Piezo Buzzer modules :)

Congrats Chuck! You certainly know how to create a buzz.

And thank you for the compliments. I wish I could attach a free servo for you, but unfortunately there is a limit on the attachment size. ;)

I will also be posting a pinout card later today for the shield base and I can really use your help to make sure I have them all properly assigned.

Cheers,
Steve

#9 beastyboy

beastyboy

    Advanced Member

  • Members
  • PipPipPip
  • 194 posts
  • LocationNetherlands

Posted 21 June 2012 - 09:04 PM

Hi Steve, After upgrading the firmware of the shield base I noticed my duration for the full left and right action has changed. Before I upgraded the values where as follows, full left duration 1020, full right 4720 However now the duration full left starts at around 720 and ends full right at around 2300. This is the 180 degrees servo, so basically the resolution between the two has changes. In the old firmware the resolution was 4720 - 1020 = 3700 New firmware 2300 - 720 = 1580 so basically halve the resolution! Any ideas? Cheers, Olaf

Van SchOten Ict Diensten en Services
http://www.voids.nl


#10 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 21 June 2012 - 10:53 PM

Hi Olaf, What is the PWM range specification on the servos? Some PWM clock settings got updated with the newest Shield Base beta firmware. Chris

#11 carb

carb

    Advanced Member

  • Members
  • PipPipPip
  • 352 posts
  • LocationCrystal River, Florida

Posted 21 June 2012 - 10:57 PM

I will also be posting a pinout card later today for the shield base and I can really use your help to make sure I have them all properly assigned.


The pin outs look good, by everything that I could find to check it against. May want to number the PWM & SPI channels.

#12 indimini

indimini

    Member

  • Members
  • PipPip
  • 15 posts

Posted 10 October 2012 - 07:24 PM

Great write up! This inspired me to pull out some old servos and start playing with the shield and PWM. (I'm so new to all this microcontroller stuff, but having far too much fun.) I ended up creating some classes to make it easier to play with the servos. I thought I'd post it to get feedback. (Who knows, others may even find it useful.)

First, I defined a class "Servo" that extends the Microsoft.SPOT.Hardware.PWM class. The class adds methods to center the servo, to rotate it given a specified angle, and to position it given a bounded value (e.g. POT value).

The servo class is designed to work with the ServoFactory static class. ServoFactory lets you configure settings for different servos and offers a single static method to construct a servo instance. I use this rather than instantiating the servo's directly. The servo type, defined in an enum, determines how the servo behaves in terms of allowed range of movement, pulse widths, etc.


  /// <summary>
  /// This class encapsulates the operation of a servo, allowing for easy operations to set angle and return-to-center.
  /// </summary>
  public class Servo : PWM
  {
    /// <summary>
    /// Constructs an instance of the servo.  Using the ServoFactory to create an instance of a specific servo type
    /// is the recommended method of servo instantiation
    /// </summary>
    /// <param name="channel">The PWM channel to which the servo is connected</param>
    /// <param name="info">Struct containing servo-specific functional parameters</param>
    /// <param name="reverse">Indicates whether or not the default servo rotation should be reversed</param>
    /// <param name="maxAngle">Sets the maximum angle the servo can travel.</param>
    public Servo(Cpu.PWMChannel channel, ServoFactory.ServoConfig info, bool reverse, double maxAngle)
      : base(channel, 20000, info.Neutral, ScaleFactor.Microseconds, info.Invert)
    {
      ReverseMult = reverse ? -1 : 1;
      MaxAngle = maxAngle;
      Settings = info;
    }

    /// <summary>
    /// A multiplier used to negate values if reverse mode is desired.
    /// </summary>
    private float ReverseMult;
    /// <summary>
    /// Holds the maximum rotation angle allowed
    /// </summary>
    private double MaxAngle;
    /// <summary>
    /// Holds a reference to the servo's parameter values
    /// </summary>
    private ServoFactory.ServoConfig Settings;

    /// <summary>
    /// Returns the servo to it's neutral position
    /// </summary>
    public void Center()
    {
      base.Duration = Settings.Neutral;
    }

    /// <summary>
    /// Moves the servo to the specified angle, or the max angle if outside those bounds
    /// </summary>
    /// <param name="angle">The position angle in degrees</param>
    public void TurnTo(double angle)
    {
      double a = System.Math.Min(System.Math.Abs(angle), Settings.MaxRotationAngle);
      if (angle < 0)
        a *= -1;
      base.Duration = (uint)(Settings.Neutral + (a / Settings.AngleFromNeutral * Settings.DegPulse * ReverseMult));
    }

    public void ReverseRotation()
    {
      ReverseMult *= -1;
    }

    /// <summary>
    /// Moves a servo to a position based on the input relative to the min and max bounds. inMin corresponds to the minimum
    /// servo angle and inMax the maximum angle.
    /// </summary>
    /// <param name="input">desired value</param>
    /// <param name="inMin">lower bound</param>
    /// <param name="inMax">upper bound</param>
    public void MoveTo(double input, double inMin, double inMax)
    {
      double outMin = Settings.Neutral - ReverseMult * (MaxAngle / Settings.AngleFromNeutral * Settings.DegPulse);
      double outMax = Settings.Neutral + ReverseMult * (MaxAngle / Settings.AngleFromNeutral * Settings.DegPulse);
      base.Duration = (uint)((input - inMin) * (outMax - outMin) / (inMax - inMin) + outMin);
    }
  }


  /// <summary>
  /// This is a static helper class that provides a factory method to construct a known set of servo types
  /// based on their configuration data.
  /// </summary>
  public static class ServoFactory
  {
    /// <summary>
    /// Defines parameters identifying operational performance of a servo
    /// </summary>
    public struct ServoConfig
    {
      /// <summary>
      /// Enumeration identifying the servo type
      /// </summary>
      public ServoType Type;
      /// <summary>
      /// Holds the pulse width in # of microseconds to set server neutral position
      /// </summary>
      public uint Neutral;
      /// <summary>
      /// Holds the pulse adjustment to rotate the servo the specified pulse angle
      /// </summary>
      public uint DegPulse;
      /// <summary>
      /// Holds the pulse angle set when the DegPulse value is applied to the neutral pulse width
      /// For example, 400 microseconds with a DegPulse of 45 Deg indicates that the servo will rotate
      /// 45 deg when the pulse is added to neutral
      /// </summary>
      public double AngleFromNeutral;
      /// <summary>
      /// Sets the maximum rotation angle for the servo - ensures that users don't try to over-rotate the servo
      /// </summary>
      public double MaxRotationAngle;
      /// <summary>
      /// Sets a boolean indicating whether or not to invert the PWM signal
      /// </summary>
      public bool Invert;
    }

    /// <summary>
    /// Array holding information for each registered servo type
    /// </summary>
    private static ServoConfig[] registeredServos;

    // To Add additional servo definitions, add an enum value above ServoCount and add a record
    // to the ServoInfo array in the initialize method

    /// <summary>
    /// Identifies the servos available.
    /// </summary>
    public enum ServoType
    {
      Airtronics_94102,
      Futuba_S3004,
      ServoCount
    }

    /// <summary>
    /// Initializes the parameters for the known registered servo types
    /// </summary>
    private static void Initialize()
    {
      registeredServos = new ServoConfig[(int)ServoType.ServoCount];
      registeredServos[(int)ServoType.Futuba_S3004] = new ServoConfig { Type = ServoType.Futuba_S3004, Neutral = 1520, DegPulse = 400, AngleFromNeutral = 45, MaxRotationAngle = 90, Invert = false };
      registeredServos[(int)ServoType.Airtronics_94102] = new ServoConfig { Type = ServoType.Airtronics_94102, Neutral = 1500, DegPulse = 390, AngleFromNeutral = 45, MaxRotationAngle = 90, Invert = false };
    }

    /// <summary>
    /// Factory method that creates an instance of the Servo class, initialized from the available servo information
    /// </summary>
    /// <param name="channel">The PWM channel the servo is connected to</param>
    /// <param name="type">The enumeration value for a known servo type</param>
    /// <param name="reverse">If true, the requests to move the servo are reversed from their default behavior</param>
    /// <param name="maxAngle">Sets the maximum allowed rotation angle - defaults to the maximum specified for the servo, but can be made to be less</param>
    /// <returns>An instance of the servo</returns>
    /// <exception cref="UnknownTypeException">Throws this if the servo type is not defined.</exception>
    public static Servo CreateServo(Cpu.PWMChannel channel, ServoType type, bool reverse = false, double maxAngle = double.NaN)
    {
      if (registeredServos == null)
        Initialize();

      foreach (var servoInfo in registeredServos)
      {
        if (servoInfo.Type == type)
          return new Servo(channel, 
            servoInfo, 
            reverse, 
            (maxAngle == double.NaN) ? servoInfo.MaxRotationAngle : System.Math.Min(servoInfo.MaxRotationAngle, maxAngle));
      }
      // If you got here, the servo type was not initialized.
      throw new UnknownTypeException();
    }
  }


An example of how I used this code is shown below.


namespace ServoFun
{
  public class Program
  {

    private static ShieldBase shieldBase;
    private static NetduinoGo.Button btn;
    private static Servo servo;

    static bool started;

    public static void Main()
    {
      btn = new NetduinoGo.Button(GoSockets.Socket1);
      btn.ButtonReleased += new NetduinoGo.Button.ButtonEventHandler(btn_ButtonReleased);
      shieldBase = new ShieldBase(GoSockets.Socket5);

      // Create our type-specific servo
      servo = ServoFactory.CreateServo(shieldBase.PWMChannels.PWM_PIN_D5, ServoFactory.ServoType.Futuba_S3004);

      servo.Start();
      servo.Center();
      started = true;

      while (true)
      {
        Thread.Sleep(100);
      }
    }

    static void btn_ButtonReleased(object sender, bool isPressed)
    {
      if (started)
      {
        if (ndx >= testAngles.Length)
        {
          // Once we get to last test data angle, stop the servo
          servo.Stop();
          started = false; 
        }
        else
        {
          // Test servo rotation.
          servo.TurnTo(testAngles[ndx++]);
        }
      }
      else
      {
        // Pressing after stop restarts the servo and re-centers it.
        servo.Start();
        servo.Center();
        ndx = 0;
        started = true;
      }
    }

    static float[] testAngles = new float[] { 18, 45,-45,-90}; // Specify a set of rotational angles
    static int ndx = 0; // Index into my test data.
  }
}


#13 Gutworks

Gutworks

    Advanced Member

  • Members
  • PipPipPip
  • 363 posts
  • LocationOttawa, Ontario

Posted 10 October 2012 - 09:23 PM

indimini, I'm glad you liked it! The class looks great. Hopefully I'll get a chance to try it out later tonight. Thanks! Steve

#14 eplaksienko

eplaksienko

    Advanced Member

  • Members
  • PipPipPip
  • 112 posts

Posted 12 May 2013 - 02:23 AM

I looks like have terrible problem with all this SB and servo, it just makes some just initial move, then stops like nothing happens

 

Any idea why, I tried all solutions above and what I can imagine, the same problem



#15 theTroll

theTroll

    Advanced Member

  • Members
  • PipPipPip
  • 54 posts

Posted 12 May 2013 - 08:46 PM

Normally if the servo isn't working it is because it is under powered. 



#16 eplaksienko

eplaksienko

    Advanced Member

  • Members
  • PipPipPip
  • 112 posts

Posted 13 May 2013 - 06:54 PM

I solved it, some reason servo I think was rusty inside, I put head on it, turned several times and now it's ok







Also tagged with one or more of these keywords: Shield Base, Servo, PWM, Potentiometer, Netduino Go, Microsoft.SPOT.Hardware.PWM

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.