Servo Randomly Turns to 0 degrees - SOLVED

Hi,

Preface:
I have been trying to build a car powered by an Arduino Uno + USB host shield connected to a PS3 controller. I use an L298N dual h-bridge board to drive the motor in 2 directions and control the speed with PWM. That is working fine (after a little finessing so to speak). Moving the RightHatY up or down turns the motor forward or backward.

The problem is with the servo to control steering… I had a larger Standard Size digital Servo the TowerPro MG995 but even with the example sweep code that didnt work… (I think that one is dead/faulty as it is totally uncontrollable…) so I reverted for the time being a cheapo TowerPro SG90 9g servo just to attempt to get the steering working. I am powering the servo via the H-Bridge board which has a 5V output and tested it with a meter while the servo is running and it holds steady at 5V. Running the example sweep code works fine no problem (Arduino and H-Bridge share a common ground). The problem arises when I use the PS3 controllers to turn it…

-I first get the value of the stick PS3.getAnalogHat(RightHatX); and store that in servoVal
-If statements determine whether or not that value is above 130 or below 110 (built in a little dead area in case the stick is moved slightly) and if above or below those values it maps to the specific degrees in that case below 110 => 115-145 and above 130=>115-85 degrees in servo.write();
-I then print out “turning left” or “turning right” followed by the servoVal after being mapped.

Problem Starts Here:
This does work and other than a slight jitter is ok (I believe jitter is a slight delay due to all the Serial prints for debugging and 10ms delay I put in for now), however after doing this a few times the servo will randomly max out at I believe 180 degrees (or the 0)and start trying to go past its maximum/minimum value… I do not write anywhere to send the servo that far or anything… if I dont touch the stick it will stay there clicking and grinding (badness) but if I touch the stick again it will reset to that value in the center of the degree area i specified as it is supposed to and I can then control its movement again for a bit till it does that again… I cannot figure out why it maxes out sometimes… oddest thing to me.

Source Code::

#include <PS3BT.h>                                                  
#include <Servo.h>

USB Usb;
BTD Btd(&Usb);
PS3BT PS3(&Btd); 

Servo servo1;

int in3 = 5;
int in4 = 6;
int enb = 9;
int servoPin = 3;

void setup()
{
   Serial.begin(115200);                                              
  if (Usb.Init() == -1) 
    {                                            
    Serial.print(F("\r\nOSC did not start"));
    while(1); //halt
    }
    Serial.print(F("\r\nPS3 Bluetooth Library Started"));  
  
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(enb, OUTPUT);
  pinMode(servoPin, OUTPUT);
  
  servo1.attach(servoPin); 
  //servo1.write(90);
}

void loop()
{
   Usb.Task();
   
  if(PS3.PS3Connected || PS3.PS3NavigationConnected)
  {
    //Get values for drive, and steering
   int servoVal = PS3.getAnalogHat(RightHatX);
 //  Serial.print("\r\nServoVal: ");
 //  Serial.print(servoVal);
    int driveVal = PS3.getAnalogHat(RightHatY);
    Serial.print("\r\nDriveVal: ");
    Serial.print(driveVal);
    int brakeVal = PS3.getAnalogButton(R2_ANALOG);
    Serial.print("\r\nBrakeVal: ");
    Serial.print(brakeVal);
    //Check Steering Values
    if(servoVal < 110)
    {
     // Serial.print("\r\nServoVal:");
      //Serial.print(servoVal);
     // servo1.write(map(PS3.getAnalogHat(RightHatX), 110, 0, 115, 130));
     servoVal = map(servoVal, 110, 0, 115, 145);
      Serial.print("\r\nTurning Left!");
      Serial.print(servoVal);
      servo1.write(servoVal);
      delay(10);
    }
    else if ( servoVal > 140)
    {
      servoVal = map(servoVal, 140, 255, 85, 115);
      Serial.print("\r\nTurning Right: ");
      Serial.print(servoVal);
      servo1.write(servoVal);
      delay(10);
    }
    else
    {
     servo1.write(115);
    }
    
    //Check drive values
    if(driveVal < 120)
    {
      digitalWrite(in3, HIGH);
      digitalWrite(in4, LOW);
      driveVal = map(driveVal, 120, 0, 0, 255);
      Serial.print("\r\nForward: ");
      Serial.print(driveVal);
      analogWrite(enb, driveVal);
      delay(10);
    }
    else if(driveVal > 130)
    {
     digitalWrite(in3, LOW);
     digitalWrite(in4, HIGH);
     driveVal = map(driveVal, 130, 255, 0, 255);
     Serial.print("\r\nReverse: ");
     Serial.print(driveVal);
     analogWrite(enb, driveVal);
     delay(10);
    }
    else
    {
    digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);
    Serial.print("\r\nStand By: ");
    Serial.print(driveVal);
    analogWrite(enb, 0);
    delay(10); 
     }
     
     if (brakeVal > 10);
     {
      digitalWrite(in3, HIGH);
     digitalWrite(in4, HIGH);
    Serial.print("\r\nBraking: ");
    Serial.print(brakeVal);
     analogWrite(enb, brakeVal);
    delay(10); 
     }
  }
  else //if BT is not connected
  {
  }
}

My apologies it isn’t the cleanest code out there yet still working on it… Any ideas why this is doing this are greatly appreciated…

Thank You,

-Pyro

     if (brakeVal > 10);

Oops

AWOL,

Thanks for that catch... I took out the ";" re-compiled and uplaoded it... that may have helped a tinsy bit but it still freaks out mostly when I turn the stick right.... (values > 127.5), although values lower do still tend to have it shoot all the way to max and click a bit until i touch the stick again and it comes back to the acceptable degree area.

-Pyro

Get the code to check for out-of-range values coming over BT and print some debugging if so - that will narrow down the problem.

You can also get the code to filter out unreasonable values if that is the problem (unreliable BT channel).

Hi, how are you powering the servo?

Can you please post a copy of your circuit, in CAD or picture of hand drawn circuit in jpg, png or pdf format.

Thankyou…Tom… :slight_smile:

I will attach a Fritzing to this (My apologies that it isnt the cleanest out there I will explain the connections in case they are hard to see).

Dual H-Bridge Used: http://www.amazon.com/DROK-H-Bridge-Arduino-Stepper-Control/dp/B00CAG6GX2/ref=sr_1_1?ie=UTF8&qid=1407158653&sr=8-1&keywords=dual+h+bridge

  • H-Bridge takes in 7.2V from a NiCd into 12V+ (datasheet allows 5V-35V)
  • GND is connected to Arduino GND & Servo GND (common ground)
  • 5V+ out is connected to Servo 5V
  • OUT3 is connected to Motor+
  • OUT4 is connected to Motor-
  • in3 => Pin5
  • in4 => pin6
    -ENB => pin9
    -Servo Data => Pin3

-Arduino is powered by PC/USB for debugging however is occasionally tested with its own 9V battery power.
-I have added Serial.print(); lines to view the output of the BT AnalogHatX connections for steering and pre-mapping the values are as expected. I tested the stock PS3BT script before modifying it with more or less perfect output results. I doubt it is a bad BT connection… I will double check all of that.
-Servo is powered via H-Bridge 5V+ output (stated in first post).

I am wondering if there isnt some interference with the PS3BT library and the Servo library, as running either seperately works fine… Or if my Servo datapin3 isnt being used somehow by the SainSmart USB host shield?

Thanks for all the help,

-Pyro

UPDATE: Re-worked the code a bit (will post) to use functions and also adjusted exception values… In addition switched data types from Int to Double and this seems to have helped overall… it still occasionally seems to be getting interference and shoots all the way to the 0 degree mark. Usually happens when I move the sticks around a lot for whatever reason.

-Pyro

#include <PS3BT.h>                                                  
#include <Servo.h>

USB Usb;
BTD Btd(&Usb);
PS3BT PS3(&Btd); 

int in3 = 5;
int in4 = 6;
int enb = 9;
int servoPin = 3;

Servo servo1;

double servoVal = 0;
double driveVal = 0;
double brakeVal = 0;

void setup()
{
  Serial.begin(115200);                                              
  if (Usb.Init() == -1) 
    {                                            
    Serial.print(F("\r\nOSC did not start"));
    while(1); //halt
    }
    Serial.print(F("\r\nPS3 Bluetooth Library Started"));  
  
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(enb, OUTPUT);
  pinMode(servoPin, OUTPUT);
  
  servo1.attach(servoPin); 
  //servo1.write(90);
}

void loop()
{
 Usb.Task();

if(PS3.PS3Connected || PS3.PS3NavigationConnected)
  {
    //servo1.write(90);
    servoVal = PS3.getAnalogHat(RightHatX);
    driveVal = PS3.getAnalogHat(RightHatY);
    brakeVal = PS3.getAnalogButton(R2_ANALOG);
    
    Serial.print("\r\nServoVal: ");
    Serial.print(servoVal);
    Serial.print("\r\nDriveVal: ");
    Serial.print(driveVal);
    Serial.print("\r\nBrakeVal: ");
    Serial.print(brakeVal);
    
    if(driveVal < 120)
    {
      forward(driveVal);
    }
    else if(driveVal > 135)
    {
      reverse(driveVal);
    }
    else
    {
     digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);
    analogWrite(enb, 0);
    }
    
    if(servoVal < 127.5)
    {
      turnLeft(servoVal);
    }
    else if(servoVal > 127.5)
    {
      turnRight(servoVal);
    }
    else
    {
     servo1.write(90); 
    }
    
    if(brakeVal > 20)
    {
      brake(brakeVal);
    }
    else
    {
    //servo1.write(90);
    }
//  servo1.write(90);  
}
  
  //End of Check BT
}
//End of Loop()

void forward(int driveVal)
{
 digitalWrite(in3, HIGH);
 digitalWrite(in4, LOW);
      driveVal = map(driveVal, 120, 0, 0, 255);
      Serial.print("\r\nForward: ");
      Serial.print(driveVal);
      analogWrite(enb, driveVal);
      delay(10); 
}

void reverse(int driveVal)
{
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
     driveVal = map(driveVal, 130, 255, 0, 255);
     Serial.print("\r\nReverse: ");
     Serial.print(driveVal);
     analogWrite(enb, driveVal);
     delay(10);
}

void turnLeft(int servoVal)
{
  Serial.print("\r\nTurning Left:");
  int servoWrit = map(servoVal, 110, 0, 90, 120);
  Serial.print(servoWrit);
  servo1.write(servoWrit);
  servoWrit = 0;
  delay(10);
}

void turnRight(int servoVal)
{
  Serial.print("\r\nTurning Right:");
  int servoWrit = map(servoVal, 140, 255, 90, 60);
  Serial.print(servoWrit);
  servo1.write(servoWrit);
  servoWrit = 0;
  delay(10);
}

void brake(int brakeVal)
{
  digitalWrite(in3, HIGH);
  digitalWrite(in4, HIGH);
    Serial.print("\r\nBraking: ");
    Serial.print(brakeVal);
    analogWrite(enb, brakeVal);
    //delay(10); 
}

Problem was alot of interference on the servo Data line to the Arduino… Added a diode to it as well as the 5V+ line and servo no longer freaks out at all.

-Pyro

Working Code:

#include <PS3BT.h>                                                  
#include <Servo.h>

USB Usb;
BTD Btd(&Usb);
PS3BT PS3(&Btd); 

int in3 = 5;
int in4 = 6;
int enb = 9;
int servoPin = 3;

Servo servo1;

double servoVal = 0;
double driveVal = 0;
double brakeVal = 0;

void setup()
{
  Serial.begin(115200);                                              
  if (Usb.Init() == -1) 
    {                                            
    Serial.print(F("\r\nOSC did not start"));
    while(1); //halt
    }
    Serial.print(F("\r\nPS3 Bluetooth Library Started"));  
  
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);
  pinMode(enb, OUTPUT);
  pinMode(servoPin, OUTPUT);
  
  servo1.attach(servoPin); 
  //servo1.write(90);
}

void loop()
{
 Usb.Task();

if(PS3.PS3Connected || PS3.PS3NavigationConnected)
  {
    //servo1.write(90);
    servoVal = PS3.getAnalogHat(RightHatX);
    driveVal = PS3.getAnalogHat(RightHatY);
    brakeVal = PS3.getAnalogButton(R2_ANALOG);
    
    Serial.print("\r\nServoVal: ");
    Serial.print(servoVal);
    Serial.print("\r\nDriveVal: ");
    Serial.print(driveVal);
    Serial.print("\r\nBrakeVal: ");
    Serial.print(brakeVal);
    
    if(driveVal < 110)
    {
      forward(driveVal);
      driveVal = 127;
    }
    else if(driveVal > 145)
    {
      reverse(driveVal);
      driveVal = 127;
    }
    else
    {
     digitalWrite(in3, LOW);
    digitalWrite(in4, LOW);
    analogWrite(enb, 0);
    }
    
    if(servoVal < 110.00)
    {
      turnLeft(servoVal);
      servoVal = 127.00;
    }
    else if(servoVal > 145.00)
    {
      turnRight(servoVal);
      servoVal = 127.00;
    }
    else
    {
     servo1.write(90); 
    }
    
    if(brakeVal > 20)
    {
      brake(brakeVal);
      brakeVal = 0;
    }
    else
    {
    //servo1.write(90);
    }
  servoVal = 127.00;
}
  
  //End of Check BT
}
//End of Loop()

void forward(int driveVal)
{
 digitalWrite(in3, HIGH);
 digitalWrite(in4, LOW);
      driveVal = map(driveVal, 120, 0, 0, 254);
      Serial.print("\r\nForward: ");
      Serial.print(driveVal);
      analogWrite(enb, driveVal);
      delay(10); 
}

void reverse(int driveVal)
{
  digitalWrite(in3, LOW);
  digitalWrite(in4, HIGH);
     driveVal = map(driveVal, 130, 254, 0, 254);
     Serial.print("\r\nReverse: ");
     Serial.print(driveVal);
     analogWrite(enb, driveVal);
     delay(10);
}

void turnLeft(int servoVal)
{
  Serial.print("\r\nTurning Left:");
  double servoWrit = map(servoVal, 110, 0, 90, 120);
  Serial.print(servoWrit);
  servo1.write(servoWrit);
  servoWrit = 0;
  delay(10);
}

void turnRight(int servoVal)
{
  Serial.print("\r\nTurning Right:");
  double servoWrit = map(servoVal, 140, 254, 90, 60);
  Serial.print(servoWrit);
  servo1.write(servoWrit);
  servoWrit = 0;
  delay(10);
}

void brake(int brakeVal)
{
  digitalWrite(in3, HIGH);
  digitalWrite(in4, HIGH);
    Serial.print("\r\nBraking: ");
    Serial.print(brakeVal);
    analogWrite(enb, brakeVal);
    //delay(10); 
}
  • 5V+ out is connected to Servo 5V

If the servo is being powered from the arduino +5v via the H-bridge board, then when the large servo tries to move, it will probably cause the arduino to reset.