Hi Guys,
I fly RC helis and program microcontrollers
The servos can be run from your netduino as you've seen, no problem there, but you do need to power them properly. The specs of the servo will tell you how much current they can draw (worst case is stall current). Just to mess around with you can buy some micro servos for a few bucks each from valuehobby.com.
Spot on with the analysis of how the signals work to control the servos, typically 1500 usecs is center/idle, 2500 max and 500 min, but you do have to adjust that per servo, some will have more range than others. You don't want to drive it to the end of its range because it will buzz and stall and get hot and burn out.
You're right about how to measure the PWM outputs from the RX, you want an interrupt on start of pulse, start a timer, then when the pulse ends stop the timer. you know the frame time, so you know the duty cycle, so you can calculate what the RX was sending.
Next you can start driving your netduino around with continuous rotation servos which have been modified so they don't go to a position, rather the PWM signal controls their speed and the just keep turning (good for drive wheels).
If you're interested in robotics, lynxmotion.com has some nice kits and also a "servo erector set" which is exactly what it sounds like. You can build your own arm with the parts. They sell a $39 servo control board that can control 12 servos, but there's no reason you can't just buy the mechanics and control them from your arduino/netduino.
If you need source to control a servo post and I'll paste some. I couldn't figure out how to get the "legacy" PWM classes to be found by VS but I just used the new ones anyway, it works fine.
Edit: Here's some code that may help. My son wanted to make a pancake flipper that was controlled by a switch (to flip the pancakes) and a pot to control the level of the flame (just a servo with a drawing of a flame that rotates).
Then he wanted to control it from the internet, so here's his code. THere are 2 servos (the flipper which is a one shot, you send the flip command and it moves the servo from min to max to min position) and then the other one just has 3 fixed positions.
The sleeps after setting the duration of the pulse (servo position) are there to give the servo time to move. While you might think that sending a constant stream of desired positions to the servo would be best, that's not the case. You're bascially a trajectory generator and the servo has its own internal controller. Now you can try to smooth your trajectory etc. but you do want to just let the servo get to where it's going and it will move smoothly.
On helis, there's a gyro that is constantly moving the tail rotor servo, and we typically use digital servos which tend to cost quite a bit more because they're have to respond faster and are constantly moving around (as opposed to your cyclic/collective servos which generally move very smoothly under your control).
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
namespace blinkynetwork
{
public class Program
{
public static void Main()
{
//this program will control a pancake flipper and stove burner from the internet
// write your code here
// configure the port # (the standerd web server port is 80)
int port = 80;
//servo setup
PWM servo0 = new PWM(PWMChannels.PWM_PIN_D5, 20000, 1500, PWM.ScaleFactor.Microseconds, false);
PWM fire_servo = new PWM(PWMChannels.PWM_PIN_D6, 20000, 1500, PWM.ScaleFactor.Microseconds, false);
servo0.Start();
fire_servo.Start();
uint fire_servo_minpos = 600;
uint fire_servo_maxpos = 2200;
//still servo setup
uint minpos = 500;
uint maxpos = 2000;
uint pos = minpos;
uint fire_pos = minpos;
servo0.Duration = minpos;
fire_servo.Duration = fire_servo_minpos;
// wait a few seconds for the Netduino Plus to get a network address.
Thread.Sleep(5000);
// display the IP address
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface
networkInterface =
Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.
GetAllNetworkInterfaces()[0];
Debug.Print("my IP address: " + networkInterface.IPAddress.ToString());
int pancake_count = 0;
//create a socket to listen for incoming connections
Socket listenerSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint listenerEndPoint = new IPEndPoint(IPAddress.Any, port);
// bind to the listening socket
listenerSocket.Bind(listenerEndPoint);
// and start listening for incoming connections
listenerSocket.Listen(1);
Debug.Print("v1.1");
// listen for and process incoming requests
while (true)
{
// wait for a client to connect
Socket clientSocket = listenerSocket.Accept();
// wait for data to arrive
bool dataReady = clientSocket.Poll(5000000, SelectMode.SelectRead);
// if dataReady is true and there are bytes available to read,
// then you have a good connection.
if (dataReady && clientSocket.Available > 0)
{
byte[] buffer = new byte[clientSocket.Available];
int bytesRead = clientSocket.Receive(buffer);
Debug.Print(pancake_count.ToString());
string request =
new string(System.Text.Encoding.UTF8.GetChars(buffer));
fire_pos = fire_servo.Duration;
if (request.IndexOf("low") >= 0)
{
fire_servo.Duration = fire_servo_minpos;
Thread.Sleep(500);
}
fire_pos = fire_servo.Duration;
if (request.IndexOf("off") >= 0)
{
fire_servo.Duration = 840;
Thread.Sleep(500);
}
fire_pos = fire_servo.Duration;
if (request.IndexOf("med") >= 0)
{
fire_servo.Duration = 1465;
Thread.Sleep(500);
}
fire_pos = fire_servo.Duration;
if (request.IndexOf("high") >= 0)
{
fire_servo.Duration = fire_servo_maxpos;
Thread.Sleep(500);
}
if (request.IndexOf("flip") >= 0)
{
pancake_count++;
servo0.Duration = maxpos;
Thread.Sleep(500);
servo0.Duration = minpos;
Thread.Sleep(500);
}
fire_pos = fire_servo.Duration;
string statusText = "Pancake flip " + pancake_count;
// return a message to the client letting it
//know if the pancake was flipped.
string response =
"HTTP/1.1 200 OKrn" +
"Content-Type: text/html; charset=utf-8rnrn" +
"<html><head><title>Netduino Plus LED Sample</title><head>" +
"<body>" + statusText + "</body></html>";
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
if (fire_pos > fire_servo_maxpos)
fire_servo.Duration = fire_servo_maxpos;
else
if (fire_pos < minpos)
fire_pos = minpos;
}
// important: close the client socket
clientSocket.Close();
}
}
}
}