Servo problem, scope verified

preface: I am a complete newb with much more equiptment than experience, so mention the dumb things! I wont take offense I swear

So I am using a sanguino, trying to control a servo, using RC input. I have massivly simplified my code to weed out the issues, and here is where I sit. I got the sanguino specific Servo library, and the sweep example works perfectly, both with a servo hooked up and when looking at the pulses on a scope. I had to edit the code as servo only works on pins 12 and 13, aside from that, it works swell!

// Sweep
// by BARRAGAN <http://barraganstudio.com> 

#include <Servo.h> 
 
Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 
 
int pos = 0;    // variable to store the servo position 
 
void setup() 
{ 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
} 
 
 
void loop() 
{ 
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}

now my code I take the output from an RC remote, which is meant to drive a servo, and convert the pulse duration to a servo position, and set the servo to that position. I threw in a delay for my sanity. both the value of the pulse length and desired servo position are perfectly sensible (86-84 deg with the stick neutral) BUT the servo goes nuts, and the scope shows arbritary pulse lengths for any angle given. meaning a 84 deg write always produces the same output pulse, but it is NOT of the correct length, and its not a scale issue, as it jumps around from angle to angle. originally I was using longs, so i cast things to ints as they are in the EX, and there was 0 change in the behavior

//RC pass through


#include <Servo.h>  //only works for pins 12 &  13 on sanguino
 
Servo LDrive;
Servo RDrive;

int ch0Pin = 15;        //first channel
int ch1Pin = 16;        
int ch2Pin = 17;        
int ch3Pin = 18;        
int ch4Pin = 19;
int ch5Pin = 20;    
int LControllPin = 12;  //to motor controlls
int RControllPin = 13;

unsigned long RCin[7];   //8 channel RC remote

void setup()
{
  for (int ii = 0; ii < 8; ii++){
  RCin[ii]=1500;}
  
  pinMode(ch0Pin, INPUT);
  pinMode(ch1Pin, INPUT);
  pinMode(ch2Pin, INPUT);
  pinMode(ch3Pin, INPUT);
  pinMode(ch4Pin, INPUT);
  pinMode(ch5Pin, INPUT);
  
  LDrive.attach(LControllPin);
  RDrive.attach(RControllPin);

  Serial.begin(9600); 

  // prints initial value on chan 0 and says hi
  Serial.println(RCin[0]); 
  Serial.println("Setup Complete"); 
}

void loop(){
    
  int lServ = 90;
  int rServ = 90;
  
  RCupdate();

  lServ = map(RCin[0], 1000, 2000, 0, 180);  //maps left drive percent (signed) to servo degrees, assumes 90 deg = 1500 us pulse
  rServ = map(RCin[2], 1000, 2000, 0, 180);  //defaults set servo max min to 544 2400
  
  int lsint = (int) lServ;
  int rsint = (int) rServ;
  
  LDrive.write(lsint);
  RDrive.write(rsint);      //86 deg real short pulse, 84 deg absurd short pulse grows and shrinks
  Serial.println(rsint);    //looks fine
  Serial.println(rServ);    //
  delay(1000);
}

void RCupdate(){
  RCin[0] = pulseIn(ch0Pin, HIGH);  //throttle
//  RCin[1] = pulseIn(ch1Pin, HIGH);  //aile
  RCin[2] = pulseIn(ch2Pin, HIGH);  //elev
//  RCin[3] = pulseIn(ch3Pin, HIGH);  
//  RCin[4] = pulseIn(ch4Pin, HIGH);  //gear
//  RCin[5] = pulseIn(ch5Pin, HIGH);  
}

any advice? I may be shelving the damn boards and buying a MEGA and just hope its a compatibility thing

What happens if you change "RDrive.write(rsint);" to "RDrive.write(86);"?

Should be able to narrow in on the issue by incrementally simplifying like that, each time getting closer to your simple working version. Or you could go the other way and start with the simple one.

Hey, you said no offense for basic advice :p.

Hi,
I have all the advice you need having recently been through the same process myself.

  1. Make sure you are connecting to the Receiver and the Servo signal wire through 1K resistors. Without this my setup was drawing too much current leading to some very odd readings.

  2. Make sure that your Arduino, Receiver and Servos all have a common ground connection.

  3. Next up, if you don't need angles, don't use them, use Servo.writeMicroseconds to write out the pulse width you read in.

  4. Use a power source capable of driving the Servos, I am using a two cell LIPO that powers my RC Race Car.

  5. Twist the servo wires, not in nice big loops, but something like the following picture, I also did this for power and receiver wires -

My Arduino is in the white box -

  1. My set up has to live next to a noisy brushed racing motor so I went a bit overboard with decoupling capacitors, I added .01 .1 1 and 10uF capacitors across the power coming into my Arduino

  2. Lastly, DONT USE PULSE IN !!!! - its going to get you so far and then get you stuck there.

I would also suggest that you read these two posts from my blog and try the code posted there -

Try these suggestions and let us know how you get on, I am successfully controlling the Throttle in my RC Race Car by reading a signal in from the receiver, transforming it and sending it out to the Electronic Speed Controller.

It works in my car at 40Kh/m so well I can't tell its there.

Duane B

Hi,

I will update my blog over the coming days with a description of the hardware, its basically as I have outlined in the response above, but will post a few pictures or diagrams on the blog for clarity.

Duane.

rcarduino.blogspot.com

kaploink: the first code block works fine, the second doesnt. I will try hand feeding it an int and see what happens

duaneb: thats a lot of good stuff! I am not using interrupts (yet) since I am trying to simplify my code, and I don't really need to catch every pulse, 4 or 5 times a second update rate may be fine for what I am doing. also I plan on going serial on my RC inputs (IE I currently have plans to use 5 channels, very possibly more). I have thought about the whole waiting for a pulse issue, but once I have 5+ pulses to read, the time is gonna be long anyways and I may go to a dedicated el cheapo micro controller to do nothing but read the RC input and feed it to the main controller on request in a quick digital fashion.
I think i am going to page thru ur blog tonight and see just what i can learn! this is a shiny new world for me.

passing numbers directly into it gets me the exact same wrong pulse output. I thought maybe having certain pins set as input could be the problem, so i commented out line by line stuff i didnt need, nothing fixed it. also after each revesion i uploaded the example code, and it works fine. putting 84 directly into the example code gave me a 84 degree pulse width, DIFFERENT than the result of putting 84 into my code. here is my code with all the commented stuff deleted to it is clear what has survived and yet the problem remains

//RC pass through


#include <Servo.h>  //only works for pins 12 &  13 on sanguino
 
Servo LDrive;
Servo rdrive;

int ch0Pin = 15;        //first channel
int ch1Pin = 16;        
int ch2Pin = 17;        
int ch3Pin = 18;        
int ch4Pin = 19;
int ch5Pin = 20;    
int LControllPin = 12;  //to motor controlls
int RControllPin = 13;

unsigned long RCin[7];   //8 channel RC remote

void setup()
{
  for (int ii = 0; ii < 8; ii++){
  RCin[ii]=1500;}
 
  LDrive.attach(LControllPin);
  rdrive.attach(13);
}

void loop(){
    
  int lServ = 90;
  int rServ = 90;
  
  int lsint = (int) lServ;
  int rsint = (int) rServ;
  
  LDrive.write(84);
  delay(15);
  rdrive.write(84);
  delay(15);

  delay(1000);
}

well I found the problem, can someone please explain WHY it was the problem?

//RC pass through


#include <Servo.h>  //only works for pins 12 &  13 on sanguino
 
Servo LDrive;
Servo RDrive;

int ch0Pin = 15;        //first channel
int ch1Pin = 16;        
int ch2Pin = 17;        
int ch3Pin = 18;        
int ch4Pin = 19;
int ch5Pin = 20;    
int LControllPin = 12;  //to motor controlls
int RControllPin = 13;

unsigned long RCin[7];   //8 channel RC remote

void setup()
{
//  for (int ii = 0; ii < 8; ii++){  //ITS ME IM A JERK WTF??
//  RCin[ii]=1500;}
  
  pinMode(ch0Pin, INPUT);
  pinMode(ch1Pin, INPUT);
  pinMode(ch2Pin, INPUT);
  pinMode(ch3Pin, INPUT);
  pinMode(ch4Pin, INPUT);
  pinMode(ch5Pin, INPUT);
  
  LDrive.attach(LControllPin);
  RDrive.attach(RControllPin);

  Serial.begin(9600); 

  // prints initial value on chan 0 and says hi
  Serial.println(RCin[0]); 
  Serial.println("Setup Complete"); 
}

void loop(){
    
  int lServ = 90;
  int rServ = 90;
  
  RCupdate();

  lServ = map(RCin[0], 1000, 2000, 0, 180);  //maps left drive percent (signed) to servo degrees, assumes 90 deg = 1500 us pulse
  rServ = map(RCin[2], 1000, 2000, 0, 180);  //defaults set servo max min to 544 2400
  
  int lsint = (int) lServ;
  int rsint = (int) rServ;
  
  LDrive.write(lsint);
  RDrive.write(rsint);      //86 deg real short pulse, 84 deg absurd short pulse grows and shrinks
  Serial.println(rsint);    //looks fine
  Serial.println(rServ);    //
  delay(1000);
}

void RCupdate(){
  RCin[0] = pulseIn(ch0Pin, HIGH);  //throttle
//  RCin[1] = pulseIn(ch1Pin, HIGH);  //aile
  RCin[2] = pulseIn(ch2Pin, HIGH);  //elev
//  RCin[3] = pulseIn(ch3Pin, HIGH);  
//  RCin[4] = pulseIn(ch4Pin, HIGH);  //gear
//  RCin[5] = pulseIn(ch5Pin, HIGH);  
}

sorry something wierd happened there. the issue was the for loop in the setup(). I commented it out, at which point there was about 8 lines of code left uncommented, and it works fine now. I revereted to the very original version with ONLY that for loop commented out, and voila it worked fine. I moved the for loop to loop() and it works fine, but putting it in setup() breaks everything.

any insight?

Total frame time cannot exceed 20mS. Frame times are based for a design limit of 11 channels, So a servo at the reciever output sees its dead time as 20mS-its pulse time. Shorter frame times will make the servo act squirrely at best and burn out on the worst case. Your 1500 microseconds is out of band(the servo library assumes anything out of bounds for an angle as microseconds).

the nominal pulse range for a servo pulse is 1000 to 2000 microseconds, with the the period being 20000 microseconds or 20 mS, so that is not the issue. although afaican tell RC is hardly consitent with that, fancy remotes can be set to something like 750 uS to 2400 uS. but they never seem to default range all the way down to 1000, but that is niehter here nor there.

The issue is putting a for loop to establish my initial initial servo signals of all neutral till a real signal is recorded in the setup() function breaks the servo library, and putting it in the loop() function is fine. why on earth would that happen? SHould I start a new thread on this problem, and should I do it in a different forum?

Hi,
I would suggest keep it in this thread, but can you post the full updated code that you are seeing this problem with and the code you have used which fixes it - even if its not the fix you want.

Duane B

rcarduino.blogspot.com

Summary of problem and solution
program reads a RC controller output, which is a pulse meant for a servo. It then passes it back out on another pin to a servo. I copy pasted this code to find a problem in a bigger chunk of code that played with the pulse but refused to work. I immediatly found the output pulse was directly related to the input pulse (IE a certain input always yeilds the same output) However the output pulse did not in any way resemble a servo pulse and varied randomly as far as I could tell. Both pulse width and period were not correct for a servo input.
the ultimate solution path was to comment out lines one by one till it worked and hand code the variables as they got commented out. Ultimatly the issue was ONLY two lines.
Solution
Moving the for loop below OUT of the setup() function and into the loop() function, without modifying it, fixed everything.

   for (int ii = 0; ii < 8; ii++){
  RCin[ii]=1500;}

WORKING CODE

//RC pass through
//For use on a sanguino board, will work on mega, all other boards require pin changes

#include <Servo.h>  //only works for pins 12 &  13 on sanguino
 
Servo LDrive;
Servo RDrive;

int ch0Pin = 15;        //first channel
int ch1Pin = 16;        
int ch2Pin = 17;        
int ch3Pin = 18;        
int ch4Pin = 19;
int ch5Pin = 20;    
int LControllPin = 12;  //to motor controlls
int RControllPin = 13;

unsigned long RCin[7];   //8 channel RC remote

void setup()
{  
  pinMode(ch0Pin, INPUT);
  pinMode(ch1Pin, INPUT);
  pinMode(ch2Pin, INPUT);
  pinMode(ch3Pin, INPUT);
  pinMode(ch4Pin, INPUT);
  pinMode(ch5Pin, INPUT);
  
  LDrive.attach(LControllPin);
  RDrive.attach(RControllPin);

  Serial.begin(9600); 

  // prints initial value on chan 0 and says hi
  Serial.println(RCin[0]); 
  Serial.println("Setup Complete"); 
}

void loop(){
  for (int ii = 0; ii < 8; ii++){
  RCin[ii]=1500;}
  
  int lServ = 90;
  int rServ = 90;
  
  RCupdate();

  lServ = map(RCin[0], 1000, 2000, 90, 150);  //maps left drive percent (signed) to servo degrees, assumes 90 deg = 1500 us pulse
  rServ = map(RCin[2], 1000, 2000, 90, 150);  
 
  LDrive.write(lServ);
  RDrive.write(rServ);
  Serial.print(rServ);
  Serial.print(" ");
  Serial.print(RCin[2]);
  Serial.print(" ");
  Serial.println(millis());
}

void RCupdate(){
  RCin[0] = pulseIn(ch0Pin, HIGH);  //throttle
  RCin[2] = pulseIn(ch2Pin, HIGH);  //elev
}

to break this code, put the for loop quoted above into the setup() function and comment it out in the loop() function, and it will babbel at the servo. Can someone verify this for me? I tested it repeatedly and know its an issue on my hardware, and I think i tested this both on a Sanguino and a Sanguino Pro, which is just a SMT copy of the sanguino. 85% sure, been fighting this a while. Please note I deleted a few confusing comments out of the code as well as lines that were commented out and therefore just confuse a reader.

Hardware and software
*Sanguino pro (smt sanguino copy)
*Sanguino
*breadboarded
*spectrum RC remote and reciever
*20MHz Leader scope
*high torque servo of some sort
*arduino IDE 0022ubuntu0.1, setup on linux for sanguino
*Sanguino servo library, can be found here: googlecode.com

Summary
NOTE: This is my first project with arduino and ANY kind of microcontroller, so take my posts with rock salt.
for loops can not be used in the setup() function or it will break the sanguino servo.h library, which I believe is the same as the servo.h library with a tiny bit of additions to accommodate the minor differences in the sanguino. The output will be incorrectly but consistently translated into incorrect servo control pulses. I did not explore this problem further than as stated above.

Hi,
I don't have a Sanguino so can't verify the code, but I am not sure you need the for loop anywhere, as far as I can tell the servo library initializes all servos to 1500 anyway.

from servo.h

#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached

and then in the servo constructor in servo.cpp

Servo::Servo()
{
if( ServoCount < MAX_SERVOS) {
this->servoIndex = ServoCount++; // assign a servo index to this instance
servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values - 12 Aug 2009
}
else
this->servoIndex = INVALID_SERVO ; // too many servos
}

Try it without the loop and let us know

Duane B

i modified my last post, please re-read it and let me know if it makes sense. for the record my code now works fine WITH the for loop, but moved.

Do you have access to a arduino of ANY kind and a servo? if so I would really appreciate it if you tried this code on it, with the pins changed to whatever you use, and then copy the for loop from loop() to setup() and see if its goes crazy on you as well.

that way we can know if this is a sanguino only issue or all arduinos.

Funnily enough I have just walked back into the room with a servo in hand, not for this, but as you ask I will give it a quick try.

Duane B.

Look at your array RCin, and the "for" loop you use to write past the end of it.

Must admit, I was not expecting that but yes I have the same problem.

And now I know why, It was not obvious, but your creating an array size [7] which has the indexes 0-6, but your for loop initialises 0<8 which is 0-7 so your going one position past the end of the array in your loop and overwritting the next two bytes.

Try this to fix it

for (int ii = 0; ii < 7; ii++)
{
RCin[ii]=1500;
}

Duane B

rcarduino.blogspot.com

so you are saying that the issue was not the For loop, but the fact that I was trying to put 8 lbs of, uhhh, charge, into a 7 lb bag? doing this in setup() was overwritting something important, doing this in loop() got lucky and overwrote something less important. I didn't know arduino could do arrays, and a homebrew write up told me that

int thisarray[7];  //makes an array with 8 elements
thisarray[7];       //access the 8th element

^THIS IS WRONG^
the first line creates a 7 element array, and the second line tries to access an element that doesn't exist.

int thisarray[8];  //makes an array with 8 elements
thisarray[7];       //access the 8th element

^THIS IS CORRECT^
for the record putting that for loop you just posted into setup() fixes everything? can you answer that, all my stuff is spread out, doing some reorgainze and re-boxing. 2 cats and 2 dogs means nothing can be left out, ever. esp if it has dangly wires.

Hi,
Yes to everything, arrays are 0 indexed so an array of size X has X Elements starting at 0 and ending at X-1.

It will save you a lifetime of wasted time if you replace any numbers in your code with meaningful text using defines, like this -

#define NUMBER_OF_CHANNELS 8

int aChannels[NUMBER_OF_CHANNELS];

for(int nIndex = 0;nIndex < NUMBER_OF_CHANNELS;nIndex ++)
{
...
}

Notice that is nIndex < NUMBER_OF_CHANNELS not nIndex == NUMBER_OF_CHANNELS, this is standard technique used in most programming languages.

if you want 10 channels, there is only one line of code the change and everything else just works.

MODIFIED : Code updated to use NUMBER_OF_CHANNELS in place of 8 in the array declaration.

Duane B.