Saturday, April 14, 2012

How To Read Multiple RC Channels

This post builds on previous posts to show a technique for reading multiple radio control receiver channels using an Arduino.

Update 27/03/2013 -  The Arduino Leonardo and Micro use the ATMega 32u4 chip which supports interrupts on fewer pins than the ATMega 328 used in Arduino UNOs and Minis. The pins used by the sketch have been rearranged so that the code can now be run the Leonardo and Micro as well.

If you have previously used the code, please ensure that you update your connecteds by swapping the input and output pins.


The following posts provide the necessary background to this post -

In the first post in this series we looked at the nature of an RC Receiver signal and provided a non blocking interrupt based approach for reading this signal with Arduino -

In the next post we looked at the Arduino servo library and how this allows us to generate the same signals that a radio control receiver generates. We can use this library to drive upto 12 servos or electronic speed controllers -
In the final post, we looked at the pinchangeint library which provides a convenient mechanism to access over twenty interrupts with a standard Arduino UNO. This overcomes the limitation of having only two external interrupts and allows us to use a standard Arduino to read more than twenty RC Channels -


Reading RC Channel Signals

Reading a single RC channel is relatively simple and can be outlined as -

1) Attach a pin change interrupt to the signal pin.
2) Create an interrupt service routine which will be called whenever the signal pin changes from high to low or low to high.

In the interrupt service routine we need to -

1) Check if the signal pin is high or low
2) If its high, this is the rising edge of the pulse, record the time using micros()
3) If its low, this is the falling edge. We are interested in the pulse duration, so if we call micros() to get the current time and then subtract from it the time we recorded in 2) for the rising edge we get the pulse duration.

The pulse duration calculated in 3) Above represents the input signal for the channel, refer to this post for a refresher - http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html

To attach the interrupt using the pin change interrupt library we call -

PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);

The interrupt service routine can be written as follows -

void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse,
  // so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time
    // of the rising edge this gives use the time between the rising and falling 
    // edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);

  }
}

So all we need to do is cut and paste the attachInterrupt and interrupt service routine code three times to read three channels ?

No. Its not that simple, but its really not very complicated either.

There are four considerations which apply when using interrupts -

1) Volatile Shared Variables
2) Variable Access
3) Fast Interrupt Service Routines
4) Timing

1) Volatile Shared Variables

When you upload a program with the Arduino IDE, your code will be compiled into an executable program that can be run on your Arduino. As part of this process the compiler will optimise the executable code to be smaller and/or faster. If we are not careful, this optimization can have unexpected results when using interrupts.

Example optimisations -

1) Replacing variables with constants
One of the optimisations a compiler will attempt is to look through your functions for any variables that are not being updated, the compiler can replace these with constant values.

2) Using registers to hold variables within functions
Another optimisation is to assign a variable to a register within a function rather than reading it from memory repeatedly.



The compiler makes optimisation decisions based on blocks of code, not entire projects. A common optimisation problem with interrupts occurs when a variable is updated in an interrupt service routine and read in the main code. Here the compiler may look at the main code and decide 'hey this variable never gets updated so I am going to replace it with a constant value'. In this case whatever the ISR does to the shared variable, your main code will never be effected because as far as the compiler could see, there was nothing that could update the variable within loop and so the executable code that was downloaded to you Arduino was generated to refer to a constant value - see 1) Above. 

A similar situation occurs when a variable is optimised to be held in a register rather than read from memory. One part of your executable code will be reading a value held in memory while another part will be updating a value held in a register. The end effect is that it appears as if your variable is not getting updated despite your ISR being called correctly and your code appearing correct - see 2) above.

Fortunately there is a keyword we can use in our code which tells the compiler 'never try and store this variable in a register, or replace it with a constant' the keyword is volatile. Any variables we wish to access from both our main code and an interrupt service routine must be declared volatile.

Really ?

Yes, I recently built an Audino, its an easy to build, fun to use synthesizer based on Arduino. The loop function reads five analogue inputs which control the sound. These variables are also used in a timer based interrupt service routine which actually creates the sound.

When I first built the project it didn't work. Reading through the source code I noticed that the analog inputs read in loop and used in the ISR were not declared as volatile. Adding the volatile declaration to these five variables fixed the problem.

Interestingly, the Audino has been around for years, it is likely that it worked perfectly with earlier versions of the Arduino compiler, but newer version may be optimizing differently. Whatever the case, the volatile keyword should always be used to tell the compiler not to optimize our shared variables.


In our code we must declare the channel input values as volatile in order that we can update them in the ISR and read them in loop -

volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;


2) Variable Access

New Arduino programmers are often aware of the volatile keyword and assume that it is all that is needed to access shared variables. It isn't.

An interrupt service routine can be run at any point in your main code. Its called an interrupt because it interrupts your main code to do something else. This can lead to strange and difficult to trace errors if you do not control when your variables are being accessed.

How do these strange and difficult to trace errors happen ?

The simplest explanation is that if your main code is reading a variable when it is interrupted and the interrupting service routine changes the variable value, your main code will be left with part of the old value and part of the new value which is not what you intended or your code expected. This is often experienced as glitches blamed on hardware, but is in fact an easily solved software problem.

As a simple example a two byte integer changing from 256 to 255 in an interrupt service routine could be seen in loop as 511.

                                      Byte 1      Byte 2
Original value = 256 = 00000001 00000000
Updated value = 255 = 00000000 11111111

Loop reads byte 1 = 00000001

The ISR is called and updates the 2 byte integer from 256 to 255

Loop reads byte 2 = 11111111

Loop sees byte 1 + byte 2 as integer 0000000111111111 = 511

This unexpected value is a result of reading byte 1 before the interrupt and byte 2 after the interrupt. We must always ensure that our multi byte shared variables are read, updated or compared without any interrupts occuring.

To prevent this problem we need to ensure that whenever we are reading, writting or comparing a values larger than a single byte we cannot be interrupted until the operation is complete. The data types int, long and float are all multi byte data types ( int = 2 bytes, long = 4 bytes, float = 4 bytes) and therefore access must be controlled between the your main code and loop. The recommended approach to controlling access is to temporarily disable interrupts in order that your main code can perform a read, write or compare without being interrupted (remember the danger is that the interrupt will change part of the value that we have already read,written or compared in which case the results will be unpredictable and most likley lead to program errors - disabling interrupts for the read, write or compare prevents this).
  
To disable interrupts we call nointerrupts(), to enable then again we call interrupts().

// turn interrupts off 
noInterrupts();
    
    // Access your shared variables here ...

// finished with the shared variables, so turn interrupts back on
interrupts();

Note - While interrupts are disabled we will not be able to read incoming signals and this can effect our accuracy. See the example code where local variables as used to minimize the time that interrupts are disabled.
Using the status register directly vs interrupts() and noInterrupts()

Note - There is some criticism of noInterrupts and interrupts on the basis that the interrupts function reenables interrupts regardless of whether they were enabled or disabled before noInterrupts was called. This could cause problems where your code is called by a library which requires interrupts to be disabled. The preferred technique is to record the global status register, update the status register to disable interrupts, then restore it to its previous value - this maintains the status that was inplace before your code rather than blindly (and unsafely) restoring interrupts.

byte sregRestore = SREG;
cli() // clear the global interrupt enable flag

// Access your shared variables here ....

SREG = sregRestore; // restore the status register to its previous value


3) Fast Interrupt Service Routines

When an interrupt is triggered the microprocessor disables further interrupts before calling the interrupt service routine. While we are inside our interrupt service routine, we cannot process any new incoming events, therefore it is important for the accuracy of our input signal that we keep our interrupt service routines as short as possible.

As a guideline the interrupt service routine should record the occurrence of an event, but should not attempt to perform any extended processing in response to the event.

In our example of reading three receiver channels and outputting 3 servo signals we are processing 500 interrupts per second. The difference between an idle throttle and full throttle is only 0.5 thousandths of a second - timing is going to be important.

If we want our main code to process an event, we need some mechanism for the interrupt service routine to flag that an event has occurred, one option is the use of bit flags.

Bit Flags

We can use bit flags to signal to our main code that new information is available from an ISR. Bit flags take advantage of the fact that a micro processor can read a single byte without being interrupted.

How do but flags work ?

If we treat a byte as being made up of eight bits, b0 to b7, we can use each bit as a signal between our main code and ISRs.The bits have the decimal values 1,2,4,8,16,32,64,128 which we can use to set, test and clear the individual bit flags.

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// a single byte we can use to hold the update bit flags defined above
volatile uint8_t bUpdateFlagsShared;

to test, set and unset bit flags we can use -

// test to see if a flag has been set - use the bitwise AND operator &
if(bUpdateFlags & THROTTLE_FLAG)
{
   // flag was set ...
}

// set a bit flag - use the bitwise OR operator |
// set the throttle flag to indicate that a new throttle signal has been received
bUpdateFlagsShared |= THROTTLE_FLAG;

// clear a bit flag - use the bitwise XOR operator ^
bUpdateFlagsShared ^= THROTTLE_FLAG;


Bit flags provide us with a simple mechanism to communicate new events between the ISRs and loop. This is particularly important where we need to perform an extended calculation in response to the event. In the example code, we use the bit flag to tell loop that a new signal is available from a channel, loop then uses this information to updated the output for the channel.

4) Timing

We have already covered two of the issues which will effect our accuracy, we can add library functions for a more complete list as presented below -

1) Library functions that temporarily disable interrupts - some library functions will temporarily disable interrupts we cant control this but we can be careful about where we use these functions
2) Disabling interrupts in our own code - we will need to do this, but we can take care to minimise the time that interrupts are disabled.
3) Interrupt Service Routines - the microprocessor automatically disables interrupts before calling them and reenables them afterwards. Again we have to live with this but can be careful to minimize the time spent inside the ISR so that interrupts are enabled again as quickly as possible.

Putting it all together

The following code sample can be used to read three RC Channels through one set of Arduino pins and output those same channels on another set of pins. The code uses dedicated interrupt service routines for each channel, this is for clarity and could be changed to use a single interrupt service routine for all channels.

The code demonstrates the use of -

 - Bit flags to communicated events between the ISRs and Loop
 - Interrupt service routines to read rc channel signals
 - Enabling and disabling interrupts
 - Volatile shared variables
 - Taking local copies of variables to minimise the time that interrupts are disabled
 - Using the servo library to output RC Channels to servos or electronic speed controllers
 - Using the pin change interrupt library to access additional interrupts.

The sample code below the video was used in creating the video. A step by step guide to connecting the ESC, Steering servo, RC Receiver and Arduino will be provided in a followup post.

video

With two channels - throttle and steering - being read from the receiver and output to the car by Arduino

video

Sample Code - MultiChannels rcarduino.blogspot.com

// MultiChannels
//

// rcarduino.blogspot.com
//
// A simple approach for reading three RC Channels using pin change interrupts
//
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
// http://rcarduino.blogspot.co.uk/2012/03/need-more-interrupts-to-read-more.html
// http://rcarduino.blogspot.co.uk/2012/01/can-i-control-more-than-x-servos-with.html
//
// rcarduino.blogspot.com
//

// include the pinchangeint library - see the links in the related topics section above for details
#include <PinChangeInt.h>

#include <Servo.h>

// Assign your channel in pins
#define THROTTLE_IN_PIN 8
#define STEERING_IN_PIN 9
#define AUX_IN_PIN 10

// Assign your channel out pins
#define THROTTLE_OUT_PIN 5
#define STEERING_OUT_PIN 6
#define AUX_OUT_PIN 7

// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoThrottle;
Servo servoSteering;
Servo servoAux;

// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
#define AUX_FLAG 4

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
volatile uint16_t unAuxInShared;

// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
uint32_t ulAuxStart;

void setup()
{
  Serial.begin(9600);
 
  Serial.println("multiChannels");

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers 
  servoThrottle.attach(THROTTLE_OUT_PIN);
  servoSteering.attach(STEERING_OUT_PIN);
  servoAux.attach(AUX_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  PCintPort::attachInterrupt(THROTTLE_IN_PIN, calcThrottle,CHANGE);
  PCintPort::attachInterrupt(STEERING_IN_PIN, calcSteering,CHANGE);
  PCintPort::attachInterrupt(AUX_IN_PIN, calcAux,CHANGE);
}

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t unAuxIn;
  // local copy of update flags
  static uint8_t bUpdateFlags;

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;
   
    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.
   
    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }
   
    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }
   
    if(bUpdateFlags & AUX_FLAG)
    {
      unAuxIn = unAuxInShared;
    }
    
    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;
   
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }
 
  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop
 
  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.
 
  // we are checking to see if the channel value has changed, this is indicated 
  // by the flags. For the simple pass through we don't really need this check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.
  if(bUpdateFlags & THROTTLE_FLAG)
  {
    if(servoThrottle.readMicroseconds() != unThrottleIn)
    {
      servoThrottle.writeMicroseconds(unThrottleIn);
    }
  }
 
  if(bUpdateFlags & STEERING_FLAG)
  {
    if(servoSteering.readMicroseconds() != unSteeringIn)
    {
      servoSteering.writeMicroseconds(unSteeringIn);
    }
  }
 
  if(bUpdateFlags & AUX_FLAG)
  {
    if(servoAux.readMicroseconds() != unAuxIn)
    {
      servoAux.writeMicroseconds(unAuxIn);
    }
  }
 
  bUpdateFlags = 0;
}


// simple interrupt service routine
void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
    // this gives use the time between the rising and falling edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart);
    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(digitalRead(STEERING_IN_PIN) == HIGH)
  {
    ulSteeringStart = micros();
  }
  else
  {
    unSteeringInShared = (uint16_t)(micros() - ulSteeringStart);
    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

void calcAux()
{
  if(digitalRead(AUX_IN_PIN) == HIGH)
  {
    ulAuxStart = micros();
  }
  else
  {
    unAuxInShared = (uint16_t)(micros() - ulAuxStart);
    bUpdateFlagsShared |= AUX_FLAG;
  }
}

Coming soon - A walk through connecting the three radio channels to the Arduino and then the processed signals back out to a car, robot or other application.

Duane B

Saturday, April 7, 2012

Lap Timer - Standalone Arduino

Finally some progress on the Lap Timer.

Some long awaited components have arrived allowing me to base the project on a standalone Arduino. I have now replaced this UNO based set up -



With this standalone/strip board Arduino which now fits nicely inside the project box.


The top left of the board has a six pin header available for serial programming with a USB to Serial cable, I am using this one from oomlout - http://www.oomlout.co.uk/usb-serial-converter-p-266.html

I still have a long hardware and software to do list -


1) At the moment the standalone system is powered through the six pin serial programming header, I need to decide on an external power source and whether this will be in the main project box with the Arduino or in the separate sensor module.

2) Provide power through a switching transistor or the buzzer for maximum volume

3) Build the sensor module

4) Add mode selection for a time based mode or a lap based mode

5) Add time/lap selection depending on the mode in 4)

6) Add a countdown timer for the start of 'time attack' mode.





Tuesday, April 3, 2012

Servo Problems - Part 2 - A Demonstration


If you have read the post Servo Problems With Arduino you will recognise the following as an apparently working circuit for driving one or more servos -


video


Lets have another look at this circuit -

In the following video I have replaced the 4 AA batteries that are powering the servos with a single 9 volt PP3 Battery. This battery is providing power to the servos and two LEDs through a 7805 regulator. This is a common regulator used in many Arduino's such as the Boarduino and standalone Arduinos. It is a reasonable replica of the 9V PP3 battery and on board regulator powering my Arduino UNO in the first video.

PP3 9 Volt Battery and The 7805 Regulator (red highlight) That Power A Boarduino

I have made one change to the circuit and introduced two series LEDs (and a current limiting resistor). Like all LEDs these two require a minimum voltage to light, in this case, combined they require around 4.5 volts. Thats about the minimum operating voltage of an Arduino so lets imagine that the LEDs are infact an Arduino sharing the same power circuit as the Servos.

Now lets see what happens under load -

Servo Under Static Load
video

When the LEDs go out, it is because the are no longer getting their minimum 4.5 volts.

When we apply a load to the servo, it draws more current, in this case it draws more current than the battery is able to deliver, when this happens, the battery voltage drops.

If this really was your Arduino it would have 'browned out' or at the very least your project would begin to behave oddly.

In the following video the servo is being instructed to sweep, even less effort is required to get the servo to take our imaginary Arduino out of service -

Loading Servos In Motion



Something else interesting is also happening, its easier to see in the following videos, but even without a load applied the servo is able to take down the LED Arduino -

In this circuit I have added another LED in the top left corner for comparison, this LED is driven by the Arduino power circuit (+5v -> 680 Omh resistor -> GND). The other two LEDs remain on the servo power circuit, note how the Arduino LED remains at a constant brightness whereas the imaginary Arduino LEDs flash noticeably -

Three Servos Overloading The Power Circuit

video


The camera is not fast enough to capture the full extent of the flashing in the LEDs, but if we can see it at all , you can be sure the Arduino is feeling it.

So what is this noise, where is it coming from and what can we do about it ?

Its not really noise, its our battery telling us that it just can't deliver the current we are asking for. The video is produced with no load, applying even a light load takes the LEDs out completely - thats not noise, thats your circuit telling you something.

The flashing is in sync with the servos changing direction at the end of their sweep. I don't have the means to investigate this further but suspect its down to the following effects -

1) The servos internal motor driver will be switching one set of transistors off and another on in order to reverse the motor
2) Momentum, the servo has to stop its internal motor and gears and get them moving in the opposite direction, this will require a burst of power.

Ok, but how do we get rid of the noise ?

As its not really noise, just our circuits way of telling us it doesn't have enough power, the solution is to add power.

Repeating the tests with the original four AA Battery pack provided improved results however it was not until I powered the circuit with a high power LIPO battery that the LEDs would remain lit at a constant brightness.


More on LIPOs and other batteries in a future post and some suggestions on using a similar LED set up to 'let the hardware do the work' in monitoring the power situation.

Duane B

Sunday, April 1, 2012

Servo Problems With Arduino - Part 1



Servo problems are one of the most frequently posted topics within the Arduino community. While problems may arise from programming, circuit design and faulty hardware, the vast majority or problems are a result of insufficient power or incorrectly connected power.

Control is nothing without power !

The Arduino itself is very good at controlling servos, the Servo library will allow a single Arduino to control upto 12 servos with no additional hardware.

http://rcarduino.blogspot.com/2012/01/can-i-control-more-than-x-servos-with.html

What the Arduino cannot do is deliver power to 12 Servos, its questionable whether an Arduino can reliably deliver power to even a single servo.

Power - Not All Servos Are Created Equal

While building the circuit for the pictures in this post, I decided to measure the current drawn by each of the test servos with no load applied. Each of the servos is the standard size used in radio controlled cars, they are all low end servos available for between 10 and 15 dollars. 

No Load Current Of Test Servos In mA

The Bluebird draws five times more current at no load than the Futaba, its also less smooth and the least expensive of the servos tested.

The current at no load is only relevant in a small number of applications, all of the servos drew more than the the maximum 250mA I was able to measure when subjected to a light load in the form of finger pressure.

I will be interested to follow up this test in a future post using a variety of repeatable loads to compare servo performance and current draw. The fact that the lowest cost servo draws five times more current than the Futaba servo suggest that this will be an important concern for larger autonomous projects which will need to operate both under their own power and under load.

Note that while controlling all four servos the Arduino drew only 10mA from its separate power source.



So How Can Successfully Drive Lots of Servos With An Arduino ?

The power problem is easily solved through the addition of a 'power circuit'. This is can be as simple as four disposable AA Batteries such as you might use in a camera or toy car.

4 Domestic AA Batteries in a holder

Rechargable AAs are an even better option, they store as much charge, deliver as much current and can be used again and again.


For large projects which need to operate under their own power the basic concept is the same however the choice of battery technology will be different. See the 'Performance Power' section in Part 2.

So why do we need two power circuits ?

The Arduino has a narrow operating voltage around 5 Volts (3.3 Volts in some) and is sensitive to variation in this voltage. The Arduino design is based on the assumption that a stable 5 Volt power source will be feeding the chip at the heart of the Arduino. In the case of the popular UNO, this regulated 5 Volt power is supplied by the USB Connection or through a regulator built into the board.

The onboard regulator is designed to provide power to the Arduino and supporting circuitry. It is not designed to power external devices and trying to do so is the single most common reason for failure with servo projects. This 'Not designed to power external devices' also applies to USB Connected projects.

The remainder of this post provides a walk through of setting up an Arduino servo example which will drive four servos from the Arduino using a separate power pack to meet the power requirements of the servos.


Arduino Servo Walkthrough -

1) Power The Arduino

For the walkthrough I am using an Arduino UNO powered by a PP3 9 Volt Battery.

The Arduino has been loaded with my Multi Sweep example sketch, a link is provided at the end.

While running the servos, the Arduino was drawing only 10mA. A good PP3 could power the Arduino for days, after all, they power smoke alarms for months.

 See the note below for more on the PP3 and why its not a good battery for use elsewhere in your projects.





 Note - The PP3 is a poor battery choice for most applications, it has a small charge capacity (run time) and cannot deliver the higher currents required to drive servos or motors, however the 9 volts it provides is great for powering an Arduino through the onboard regulator. As the Arduino makes so little demand on a battery the PP3 is a common and practical choice to power the Arduino - just not any shields, motors, servos, transmitters etc.

The PP3's small charge capacity and limited ability to deliver current make it an unsuitable choice for providing the power circuit in our projects, the common AA battery is a far better alternative. In the case of servos the 9 Volts supplied by an unregulated PP3 is over the 4.8 to 6 volt recommended operating range and will result in immediate damage to the servo. Again AA Batteries are a better choice as four will provide a usable 6 Volts for our servo power circuit and a better run time.

This wont work for me, I need USB for Serial Output -
 
This is no problem at all, you can simply connect the USB Cable to the Arduino as you normally would. This will provide power to the Arduino so you do not need to use the 9V PP3 Battery. 

You should still use the separate servo power and this will work provided that the ground from the battery pack is connected to the Arduino.

 
2) Power For The Servos

For servo power I am using four disposable AA Batteries. These are high capacity versions sold for cameras which will give us enough current and charge (run time) for our servos.

Each AA Battery provides 1.5 volts for a total of 6 volts (4*1.5).

Most servos are designed to operate with 4.8 to 6 volts. Powering them with more voltage can result in instant damage. The four AA Batteries give us a usable 6 volts.


I have connected the AA Batteries to the power (red) and ground (black) rails on my bread board.

Notice the black jumper running from the bread board to the Arduino ?

This is the next most common mistake in servo projects, when people introduce the servo power pack, they forget to connect a common ground.

You must connect the ground wire between the power circuit and the Arduino (control circuit) without this connection your circuit will not work. This applies whether you are powering your Arduino from a wall socket, USB port or a battery.

In the picture you can see where I have simply connected the two circuits through the black jumper wire running from the black ground rail of the bread board to the ground (GND) pin on the Arduino next to pin 13.

All of the ground pins on the Arduino are connected so use which GND pin is most convenient.

These two circuits now share a common ground allowing us to add some servos.


2) Connecting Individual Servo Power

I have added a 3-Pin section of PCB Header to make it easier to connect the female servo plug to female breadboard.

Next to this you can see that I have added a green jumper from the battery pack ground rail to pin1 of the header, this is the shared ground between the Arduino, battery pack and servo.

Next I have added a yellow jumper from the power rail - 6 Volts from the AA Battery pack - to the center pin of the header. This will provide power to our servo directly from the AA Battery pack, not from the Arduino or its 9 Volt PP3 Battery.

The final pin is the signal pin, this is the pin which the Arduino will use to tell the servo which position to hold.

Remember - Without the common ground between the Arduino and the Servo AA battery pack, your project will not work. In the picture, this common ground is provided by the black wire linking the ground rail of the breadboard with the GND pin next to pin 13 of the Arduino.


3) Adding a servo

All we need to do now is connect our first servo -
 You can see here that I have connected the first servo taking care to make sure that the black ground wire of the servo connector is connected to pin 1 of my header. This is then connected to the shared ground of the Arduino (and Battery pack ) through the green jumper wire.

You can also see that I have added headers for three more servos following the same convention of yellow for 6Volt power from the AA Battery pack and green for the shared Arduino/AA Battery pack ground.

The servo is receiving its position signals from the Arduino through the white jumper wire connected to pin 13. Remember - Without the common ground (black wire) connecting the Arduino GND Pin to the ground (-)  fo the battery pack through the black ground rail of the breadboard, this will not work !

If you have uploaded the multi sweep example sketch linked at the end of this post, you should see your servo sweeping back and forth.

4) Adding more servos

Once you have one servo up and running, you can add more by following the same convention - connect the servo plug to the header so that the end with the black wire attaches to the header pin with the green wire. You can then add a connection from the signal pin to any of the Arduino digital pins 2-13 and you should see your additional servos follow the same sweeping pattern as the first servo -

video

For the 'Multi Sweep' example sketch used in this video see the post - Can I control more than X Servos with an Arduino ?

Conclusions -

The Arduino can control large numbers of servos without additional hardware provided a separate power source is dedicated to the servos.

Four AA disposable or rechargeable batteries are a good choice for providing this power in smaller projects.

Large projects will require hobby batteries or fixed power supplies, part 2 will look at some of these options and the power demands of the test servos under a variety of loads.

Servo models used and current drawn at no load -

Servo                              Current mA

Futaba S3003  55
HITEC HS-322HD  75
ACE S1903  90
Bluebird BMS410  250

Duane B