Saturday, January 28, 2012

Can I Control More Than X Servos With An Arduino ?

Yes, Yes, Yes, infact 'Yes' Twelve Times.

The Servo library for the Arduino UNO allows the control of upto 12 Servos on a single Arduino UNO. As the electronic speed controllers or ESC's used in remote controlled cars also use the same type of signal, you can control any combination of servos and speed controllers up to a total of 12 devices.

If you really need more devices you can -

1) Interface to an external servo controller - these are generally additional micro controllers but programmed purely to take servo related instructions from another source and manage the corresponding bank of servos.

2) Interface to another Arduino - this is the same as option 1) above but gives you some additional flexibility for instance you can share the collection and processing of sensor data across the two devices without having to learn a new micro controller.

3) Use one of the larger Arduinos - always an option, but when you can build a basic Arduino for less than 10 dollars option 2 looks hard to beat.

How does it work ?

If you have read the previous posts on interfacing to an RC Receiver you will understand that a servo signal is not at all what you might have been expecting. If you haven't read those posts, here they are -

How To Read An RC Receiver With A Microcontroller - Part 1

How To Read An RC Receiver With A Microcontroller - Part 2

So now that we understand that a servo expects a pulse of between 1 and 2 milliseconds to be sent about every 20 milliseconds, how does the Arduino control up to 12 servos all expecting these signals 50 times a second ?


Fortunately someone by the name of Michael Margolis has created the Arduino Servo library so that all we have to do is call two functions -

Arduino Servo Library


The Main Servo Library Functions -

1)  aServo.attach(pin_number); - This tells the library that there is a servo attached to pin number 'pin_number'. When the library attaches it will initially begin pulsing the servo with 1500us, this is the centered position for a servo, which is also the neutral position for a (RC Car) electronic speed controller.

2) aServo.writeMicroseconds(uS) - This tells the library to update the pulse width for your servo. There is another version 'write' which takes an angle instead of a microseconds argument. This version converts the angle to micro seconds and then calls writeMicroseconds, so if you do not need angles in your application, don't use them.

And now for an example, forget sweep, try 'multi sweep' -

Quick warning - Don't connect 12 Servos to the Arduino power pins, use external power or to just test the code using a single servo, connecting its signal pin to each of the arduino digital pins 2-13 in turn.

// Multi Sweep, Duane B
// Using the servo library created by Michael Margolis 
// to control 12 Servos with one Arduino Uno

#include <Servo.h>
// Sample sketch for driving 12 Servos from an Arduino UNO, servos are attached to digital pins 2,3,4,5,6,7,8,9,10,11,12,13

#define CONNECTED_SERVOS 12

// macro just adds two - the first servo is attached to digital pin 2, this gives us upto 12 servos - digital 2 to 13
#define SERVO_TO_PIN(x) (x+2)

Servo myServos[CONNECTED_SERVOS];

#define COUNT_DOWN -1
#define COUNT_UP +1
#define INCREMENT 10 // move in steps of 10 milliseconds

int nPulseWidth = DEFAULT_PULSE_WIDTH ; // 1500, defined in servo.h
int nDirection = COUNT_UP;

volatile unsigned long ulStart = 0;
volatile unsigned long ulStartToEnd = 0;

void setup()
{
  // attach the servos
  for(int nServo = 0;nServo < CONNECTED_SERVOS;nServo++)
  {
    myServos[nServo].attach(SERVO_TO_PIN(nServo));
  }
 
  // the library sets all servos to 1500 ms pulse width by default, this is center for a steering servo
  Serial.begin(9600);
  Serial.println("Completed setup"); 
}

void loop()
{
  delay(10);  // give the servos time to move after each update
 
  if(ulStartToEnd)
  {
    Serial.println(ulStartToEnd);
    ulStartToEnd = 0;
  }
 
  nPulseWidth += nDirection * INCREMENT;
 
  if(nPulseWidth >= 2000)
  {
    nPulseWidth = 2000;
    nDirection = COUNT_DOWN;
  }
 
  if(nPulseWidth <= 1000)
  {
    nPulseWidth = 1000;
    nDirection = COUNT_UP;
  }
 
  for(int nServo = 0;nServo < CONNECTED_SERVOS;nServo++)
  {
    myServos[nServo].writeMicroseconds(nPulseWidth);
  }
}

Great, so that works, but how and what does it cost us ?

The library makes extensive use of the 16 bit timer/counter, this has a cost to us. By taking over the timer, the library prevents us using analogWrite on digital pins 9 and 10, however on the up side we can control upto 12 Servos and there are still pins 3,5,6 and 11 to use with analogWrite. 

So how does the library manage to control 12 servos with a single timer ? is it a production quality approach or just a proof of concept ?

Note : Moderate simplification used to aid understanding and descriptive clarity below.

The library attaches an interrupt function to the timer which is called whenever the timer value matches its output compare register value. Think of this like the red hand on an alarm clock, when the hour hand matches the red hand, the alarm goes off. When the timer matches the output compare value, the interrupt function gets called.

In addition to this interrupt function, the library maintains an array of servo objects. Each servo object has two main fields, the pin it is attached to and the pulse width. Each time the interrupt function is called, it uses an array index to find the current servo object, then sets its pin LOW.

Next the array index is incremented to point at the next servo object, the code immediately sets this servos pin HIGH. These few lines of code represent the end of the pulse for the first servo and the start of the pulse on the next servo.

Next the function adds together the current timer value and the pulse width required for the current servo, this value is then put into the output compare register. This has the effect of ensuring that the interrupt will be called again in servo.pulsewidth microseconds from now.

This code has an effect a bit like the snooze button on the alarm, its basically saying, can you get back to me in a short while. In our case the short while is whatever the current servos pulsewidth is set to.

Note - This allows your code to carry on doing whatever it needs to, the timers and interrupts will work in the background to keep your servos where you want them.
 
At this point, however many microseconds later, the cycle starts again, the interrupt is called, the current pin it taken low, the next pin is set high, the pulse width of the new pin is added to the current time and then set in the timers output compare register to start the cycle again and so it goes on.

The table below provides a vastly simplified view of what happens each time the interrupt function is called -

Three servos 1,2,3 with pulsewidths of 1000,1500 and 2000

TIMER Servo 1 Servo 2 Servo 3 New value to set in output compare register OCRA1
0   HIGH LOW LOW current time 0 + pulse width 1000 1000
1000   LOW HIGH LOW current time 1000 + pulse width 2500 2500
2500   LOW LOW HIGH current time 2500 + pulse width 2000 4500

 
As far as I am concerned this is both elegant and simple as all the best code should be. Thanks to Michael Margolis for creating the library.

I hope this post will help clear some of the confusion around Arduino and mulitple servos using this great library.


In a future post I plan to cover servo trouble shooting.

Duane B

Stay Tuned ... and in the meantime let me know if you have found any of this useful.

Wednesday, January 25, 2012

Extra Channels On The Cheap

Ever wanted to add an extra channel to your car, boat, robot ?

One of the guys commenting on the blog mentioned that he was working on a tugboat project. This brought to mind the one or two marine models that I have seen, from memory these models are very big on scale detail and moving parts.

This set me to thinking what could a micro controller do in this environment ?

Something very quickly came to mind that could be used in this application or any other where you would like to add one or two 'toggle' channels to a model. These are channels that are either on or off, and are ideal for setting off signalling sequences, turning lights on and off, switching mix modes remotely, activating a scale winch or basically any other application you can think of that requires the ability to remotely switch something between two states or positions.

This is a very simple trick which I have used to remotely toggle between dad mode and child mode in one of my projects. The trick is to use the small zone between neutral throttle and the point at which the motor overcomes stall inertia.

If neutral throttle is a pulse of 1500, you can use 1510-1525 to toggle one action and 1475 to 1490 to toggle another action. The values may need adjusting in particular applications, but the concept is the same -


If the throttle is held in this region, switch an indicator light on and take a time stamp.

If the throttle moves out of this region, switch the indicator light off and clear the time stamp.

If the throttle remains in the region, check the time stamp against the current time, if the difference exceeds your switch period, then toggle your mode/lights/winch whatever.


Its the method I use to switch modes in the child mode project, its incredibly useful to be able to switch parts of your code on and off remotely while the model is running. But the same approach applies equally well to all sorts of scale model applications such as I have suggested above.

As we are using the stall space around neutral, our model will not be moving, so there is no reason why we could not combine this with the steering channels to add even more channels.

By my calculation using throttle and steering channels gives 2 (up,down) * 3 (little left, little right, centered) = 6 on/off channels.


How much do these extra toggle channels cost ? An Arduino UNO is around 20 GBP but you can assemble your own from components for 7 GBP and the best part is the Arduino can do a lot more than just switch something on and off, any one of those six toggle channels could set off pre programmed sequences across multiple servos, lights, speakers and other actuators.

Simple Example - Toggling One Mode Using Throttle Channel

  if(nThrottleIn <= THROTTLE_THRESHOLD && nThrottleIn >= (THROTTLE_THRESHOLD-20))
   {
     unsigned long ulCurrentCounter = millis();
     if(ulModeSwitchCounter == 0)
     {
       ulModeSwitchCounter = ulCurrentCounter;
     }
     else
     {
       if((ulCurrentCounter - ulModeSwitchCounter) > MODE_SWITCH_DURATION)
       {
         
ulModeSwitchCounter = 0;
          // SWITCH YOUR MODE HERE

       }
     }
   }




Duane B.





Friday, January 20, 2012

How To Read an RC Receiver With A Microcontroller - Part 2

If you read 'How To Read an RC Receiver With A Microcontroller - Part 1' you will probably have guessed that my approach is based around interrupts, you may even be thinking 'So what, there is nothing new in any of this'.

Your right on the first part, but writing the code to take control of the throttle signal on my 40Km/h RC Race car was a very long way from reading a signal and writing it to the serial port.

What Could Possibly Go Wrong ?

The real world quickly gets in the way - underground cables, vibration, noise from the RC Components in the car, mobile phone transmitters, the built up environment, low transmitter batteries, even passing traffic effects the signal.

During the design and test of the code, I had two hardware problems with a dry solder joint on the power capacitor attached to the electronic speed controller and a track on my low quality strip board which crumbled away. At the time I was not aware that these hardware issues were the cause and so I investigated every conceivable cause of interference.


Power Capacitor Soldered to ESC Power Terminals
Strip board track repaired after crumbling away!


One simple test repeated since the hardware issues were resolved involved propping the RC Car wheels off the ground and then walking away from the car with the transmitter. I repeated this many times, increasing the distance or changing the line of sight to include/exclude features in the built up environment. I was very surprised to see just how badly the signal degrades as you move the transmitter away from the car.



Transmitter and Receiver Adjacent To Each Other

Transmitter and Receiver 20 Meters Apart.



The charts show the pulse width variation over time with the transmitter set at a constant neutral signal. A straight line would indicate there is no variation in the pulse width, this is the result we might initially expect.

The two charts are generated using the code presented in the preceding post 'How To Read an RC Receiver With A Microcontroller - Part 1'


To create the charts, the serial output was cut and paste into Microsoft Excel.

The charts clearly show that as the distance between the transmitter and receiver increases, so the variation in the signal increases. My tests have shown that the built up environment has an additional effect, further compounding the effects of distance.


With no controller input and the transmitter positioned just 20 meters away, the signal output by the receiver would randomly wander by as much as 30 milliseconds either side of the transmitter signal. Some of this would be due to the built up environment, moving the transmitter to a position occluded by a steel reinforced concrete pillar would cause chaos in the servo at only 20 meters.

So this isn't going to work is it ?

Not only does it work, but it works brilliantly. The best thing I can say about using the Arduino to control the throttle signal is that you can't tell its there.

The first few times I drove the current version of the software I kept having to bring the car back in to check that I hadn't shorted something and bypassed the Arduino completely.

So how does it work ?

First of all the mechanical nature of the servos and motors in our equipment provides a certain amount of signal smoothing, put simply, they just cannot move fast enough to react to the variation in the signal and so their position at any point in time reflects an average value of the signal in the preceding milli seconds.

Sample signal variation over 1 second - a random walk around the value 1540 - the value output by the transmitter.


In addition to the smoothing provided by the mechanical nature of the car, I have included a 'confidence' algorithm in the code. This algorithm compares the current input to the previous output, if the variation is small it ranks the new signal with a low value, if the variation is larger, the signal scores a higher degree of confidence. If the signal returns to its original value the confidence is reset to zero. The basic concept is that if we receive a vastly different signal, the chances are that its a new command from the driver, but if we receive a series of wandering signals they can generally be ignored provided that they do not creep in one direction which would indicate a subtle but valid input from the driver.


// Confidence Version 2, smaller, better, faster ?

int nDifference = nThrottleIn - nThrottleOut;

 if(nDifference == 0)
 {
    nConfidence = 0;
 }
 else
 {
   nConfidence += (nDifference/4);
 }
  
 if((nConfidence >= SENSITIVITY) || (nConfidence <= -SENSITIVITY)) // SENSITIVITY = 8
 {
   nConfidence = 0;
  
   // do something with this pulse ....
 }



I am currently open minded as to the value of the confidence algorithm. Standard RC Equipment works well enough without the addition of this algorithm, however it is at a cost of battery drain, heat and mechanical wear. The real value of the algorithm in a mircocontroller environment is potentially in providing a lightweight, easily tuned mechanism to condition the signal for input into subsequent algorithms without introducing the lag that results from smoothing across multiple samples.

The following charts show a badly degraded signal before and after the simple confidence algorithm -

Signal Before Algorithm


Signal After Algorithm


In the charts above, the confidence algorithm clearly smooths a large amount of the signal noise, however it is too sensitive to cope with the extremes.

One feature of the algorithm is that the sensitivity can be easily changed through a single variable. 

Test Conditions 
The tests were performed with the transmitter and receiver at opposite ends of a steel reinforced concrete villa  -

The line of sight was occluded by at least two walls
The receiver was adjacent to and transmitting serial data to a laptop. 
The line of sight included an LCD TV and a kitchen appliance.

The reinforced walls, LCD TV and Kitchen Appliance have all been observed to cause a large degree of interference to RC equipment and signals. 

Ideally the tests should be repeated with a higher value sensitivity however the test conditions are extreme and do not provide any qualification of the algorithms real world usefulness.

At present the algorithm has very little performance impact and clearly removes some noise, however before the algorithm can be qualified into or out of the RC Arduino projects it will need to be tested in the two most common RC Applications -

1) Racing - This is a tough environment with multiple transmitters sharing a fairly narrow physical and electro magnetic space. 
2) Street Driving - This has its own challenges in the form of distorted signals reflecting from the build up environment.

In use to date, the confidence algorithm has no detrimental effect so it stays 'in' with one small change - 'Sensitivity' actually has the opposite effect and should really be renamed 'Damping' to reflect it effect on the signal variation.

Next up - Bad Pulses And What To Do With Them