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

Netduino and RC Receivers (I am using a Spektrum AR600)

RC Receiver

Best Answer JamieDixon, 19 March 2013 - 12:29 AM

I was thinking along the same lines.

I got it working with this code:

    public class Program    {        static OutputPort _forwardPort = new OutputPort(Pins.GPIO_PIN_D13, false);        static OutputPort _backwardsPort = new OutputPort(Pins.GPIO_PIN_D7, false);        public static void Main()        {            InterruptPort inputPort = new InterruptPort(Pins.GPIO_PIN_D0,                true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt);            Thread.Sleep(Timeout.Infinite);        }        static long leadingEdge = 0;        static void inputPort_OnInterrupt(uint data1, uint data2, DateTime time)        {            if (data2 == 1)            {                leadingEdge = time.Ticks;            }            else            {                long currentPulseWidth = (time.Ticks - leadingEdge) / 1000;                switch (currentPulseWidth)                {                    case 18:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 17:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 16:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 15:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 14:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(false);                            break;                        }                    case 13:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    case 12:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    case 11:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    default:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(false);                            break;                        }                }            }

 

You can change the output to the analog ports if you want to dim the LEDs.

 

Note that I found a really article on unit testing the Microframework here.

 

Thanks for your help.  I am now going to see if I can use the PWM class to fire to the servo.  Also, my oscilliator will be coming next week...

Go to the full post


  • Please log in to reply
29 replies to this topic

#21 JamieDixon

JamieDixon

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationCary, NC

Posted 17 March 2013 - 10:02 PM

Hanzibal:

 

I started doing some basic coding to analyze the PWM.  One intersting thing that I found was that using the 4.8V battery pack, the difference between the high edge and low edge was consistantly 12000 ticks.  When I changed the voltage to the Netduino 5.0V source, the difference in ticks was 206016 ticks.  This is actually closer to the articles that stated that RC WPM's periods are 20 MS.  My question - the amount of voltage will affect the length of the PWM period?



#22 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 18 March 2013 - 01:06 AM

The voltage should not affect the pulse width (nor its periodicity). If anything, voltage would only affect the power of the servo, meaning the servo will generally be able to pull a heavier load when fed 6V. Also, the servo will typically stop working if voltage drops below some 2 - 3 volts. Actual behavior is different among different servos of course and they are not as standardized as one might think at first. I don't know from the top of my head but I think there a 10 000 000 ticks per second which yields 10 000 ticks per millisecond. This would mean that your 12 000 ticks corresponds to 1.2ms. This makes sence since pulse width should vary between 1 and 1.5 ms. If, on the other hand, you measure the time difference in between 2 consecutive pulses this would be somewhere around 200 000 ticks which corresponds very well to your second measure.

#23 JamieDixon

JamieDixon

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationCary, NC

Posted 18 March 2013 - 12:31 PM

OK, so I got it <almost> working now that I have this knowledge.  I plugged the RC Recevier into the Netduino 5V (I got tired of the 4.8V battery back needing to be rechanged) and I added this code:

        static OutputPort outputPort = new OutputPort(Pins.ONBOARD_LED, false);                public static void Main()        {            InterruptPort inputPort = new InterruptPort(Pins.GPIO_PIN_D0,                true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt);            Thread.Sleep(Timeout.Infinite);        }        static long leadingEdge = 0;        static long priorPulseWidth = 0;        static void inputPort_OnInterrupt(uint data1, uint data2, DateTime time)        {            if (data2 == 1)            {                leadingEdge = time.Ticks;            }            else            {                long currentPulseWidth = (time.Ticks - leadingEdge) / 1000;                long pulseWidthChange = currentPulseWidth-priorPulseWidth;                if(pulseWidthChange > 0)                {                    outputPort.Write(true);                }                if(pulseWidthChange < 0)                {                    outputPort.Write(false);                }                priorPulseWidth = currentPulseWidth;            }        }

 

When I push the stick up, the LED lights (with an occasional flash depending if I stop when I am pulling it)

When I push and leave the stick motionless at max up, the LED flashes

When I pull the stick down, the LED truns off (with an occasional flash depending if I stop when I am pulling it)

When I pull the stcik down and leave the stick motionless at max down, the LED flashes.

 

I am thinking of doing an average to smooth out the noise when I am pulling and pushing the stick.  I can't explain the max and min range blinking though...



#24 JamieDixon

JamieDixon

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationCary, NC

Posted 18 March 2013 - 12:34 PM

One other question - your statement that:

 

voltage would only affect the power of the servo, meaning the servo will generally be able to pull a heavier load when fed 6V

 

That is not the voltage going down the signal wire - that is the voltage from the power to the servo.  The signal wire is a relay and its voltage should have nothing to do with the amount of strength the servo has.  Correct?



#25 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 18 March 2013 - 07:46 PM

That is correct, the voltage (or rather, the current) gets pulled through the red power line into the motor as it turns while as the signal input is of much higher impedance (built in resistance) so there's a relatively much smaller current going in that way. These two currents then unite inside the servo and their sum comes out through the ground wire and back to your power source. About the voltage of 5V vs 6V, we can consider the internal resistance of the servo (motor) as a constant R. According to Ohm's law, the current I going through a resistor is equal to the voltage U across it devided by the resistance R, i.e. I = U / R. The power P of a device is equal to the voltage across it times the current going through it, P = U x I. Since I = U / R we get P = U^2 / R. Since we assumed R to be constant, it follows that the power is proportional to U squared. Thus, a 20% voltage increase will result in a 44% power increase. Actual power increase is subject of device efficiency (usually given as a percentage), the rest dissipates as a combination (usually heat) of mechanical friction, magentic field discharge and a bunch of other stuff. Also, this relation between voltage and power is only valid to a certain point beyond which the device eventually breaks down (over voltage). I've never fullyunderstood what voltage and current really are but I picture voltage as a falling height (potential) while as current is a flow as in volume per unit time. Think of a water fall where fall height corresponds to voltage and the amount of water (gallons per second) as the current. I guess the Niagara falls of USA/Canada has a lot more power than the the Victoria falls of Africa. EDIT: I didn't notice your post containing code until know. You light the LED when the most recent pulse is wider than the previous and you turn the LED off when the most recent pulse is narrower than the previous. As you push the stick upwards, each new pulse wil always be a little bit wider than the previous and therefore you keep lighting the LED until you either stop or come to the extreme. Likewise, as you pull the stick downwards, each new pulse will be a little bit narrower than the last one and therefore you keep turning the LED off repeatedly until you either stop or come to the extreme. Whenever the stick is being held "still" (except for when resting in the idle center position), the pulse width will actually keep varying ever so slightly regardless if in either extreme or being stopped midways. Even though these pulse width variations are very small, most of them will still be detected by your Netduino, resulting in small differences varying around zero, effectively turning the LED on and off producing the flashing. I think you should require a minimum difference be exceeded (on either side of zero) before allowing the LED to change state. Say for instance that the difference must be at least +/- 1 000 ticks in (a tenth of a millisecond or 100us) for you to either light the LED or turn it off depending on the sign of the difference. This should prevent flashing when the stick is being held still. A cool thing might be to use the Netduino PWM output to dim two LEDs, one green and one red. When the stick is resting, both LEDs would be off. When you start pushing the stick up, you would start dimming the green LED brighter and brighter until fully lit in the upper extreme. Then you would do the same with the red LED as you pull the stick downwards. Sorry for this much text but you asked for it :-)

#26 JamieDixon

JamieDixon

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationCary, NC

Posted 19 March 2013 - 12:29 AM   Best Answer

I was thinking along the same lines.

I got it working with this code:

    public class Program    {        static OutputPort _forwardPort = new OutputPort(Pins.GPIO_PIN_D13, false);        static OutputPort _backwardsPort = new OutputPort(Pins.GPIO_PIN_D7, false);        public static void Main()        {            InterruptPort inputPort = new InterruptPort(Pins.GPIO_PIN_D0,                true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);            inputPort.OnInterrupt += new NativeEventHandler(inputPort_OnInterrupt);            Thread.Sleep(Timeout.Infinite);        }        static long leadingEdge = 0;        static void inputPort_OnInterrupt(uint data1, uint data2, DateTime time)        {            if (data2 == 1)            {                leadingEdge = time.Ticks;            }            else            {                long currentPulseWidth = (time.Ticks - leadingEdge) / 1000;                switch (currentPulseWidth)                {                    case 18:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 17:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 16:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 15:                        {                            _forwardPort.Write(true);                            _backwardsPort.Write(false);                            break;                        }                    case 14:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(false);                            break;                        }                    case 13:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    case 12:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    case 11:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(true);                            break;                        }                    default:                        {                            _forwardPort.Write(false);                            _backwardsPort.Write(false);                            break;                        }                }            }

 

You can change the output to the analog ports if you want to dim the LEDs.

 

Note that I found a really article on unit testing the Microframework here.

 

Thanks for your help.  I am now going to see if I can use the PWM class to fire to the servo.  Also, my oscilliator will be coming next week...



#27 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 19 March 2013 - 01:26 AM

Ok, good luck with that!

#28 ncostes

ncostes

    New Member

  • Members
  • Pip
  • 2 posts

Posted 19 March 2013 - 04:53 AM

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();
 
 
            }
 
        }
 
    }
}


#29 JamieDixon

JamieDixon

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationCary, NC

Posted 19 March 2013 - 08:00 PM

As a final wrap up, I blogged about it here

http://jamessdixon.w...-on-a-netduino/

and here

http://jamessdixon.w...etduino-part-2/



#30 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 20 March 2013 - 07:04 PM

Nice write up, it's both fun and exciting to investigate the inner workings of an "unknown" device based on obsevations of how it responds to certain stimuli. Thats's pretty much what science is all about. However, your conclusion as of how servo's react to a controlling pwm signal, does not seem entirely correct. As you said, the width of the pulse (typically) varies between 1 and 2 milliseconds, where 1.5ms is the "idle" state corresponding to the servo being in its center position or zero degree angle. The pulse width does not (as you imply) take discrete jumps between these three, rather it varies smoothly between the two extremes. Starting off at 1.5ms, the pulse width will gradually increase towards 2ms as you push the stick in one direction. When you let go of the stick, the width of the pulse will fall back to 1.5ms (this too is gradual). Likewise, when you pull the stick in the oposite direction, the pulse width will start to gradually decrease from 1.5ms towards 1ms. A standard servo will hold still at zero degrees and then start to move towards either -45 degrees or +45 degrees in proportion to in which direction and how fast you move the stick. If the pulse width is 1.75ms when the stick is held still midway up and, at this point, the servo is at +23 degrees, then the servo will always want to move to +23 degrees whenever the pulse width happens to be 1.75ms. So you see, the servo angle is proportional to the width of the pulse. It may not be a perfect linear relation but still. Some servos can move beyond the +/-45 degrees when the pulse width goes above or below 2 and 1 milliseconds respectively.




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.