Need help with erratic servo

Hey guys! First time poster here. I am getting back into the coding game that I haven't been in since the Commodore 128 days.

A recent project I embarked on is building a trolling "cruise control" for my boat. The idea is to be able to take GPS speed and move a servo motor that will control the throttle of my trolling motor (even wide open it only pushes the boat 3-4mph). I came up with the code and assembled a rig including:

Arduino Uno
Adafruit Ultimate GPS
Buck converter to step down the 12v boat battery to 6v for the servo
Adafruit micro high torque servo

It took a lot of trial and error but I finally had the rig working good. Tonight I tried to connect the servo and it doesn't operate correctly. Regardless of the mode I use, it always "hunts" back and forth about 45*. The input (pot or otherwise) does seem to move the servo, but it always is moving around.

Initially I suspected a power supply problem, but when using the Servo Library example code for both sweep and knob, the servo works perfectly fine! This is probably me showing my "green-ness", as I am sure there is something with my code. Let me know what you guys think.

//libraries and definitions
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <Wire.h>
#include <TinyGPSPlus.h>
#include <SoftwareSerial.h>

//objects
LiquidCrystal_I2C lcd_1(0x27, 16, 2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
Servo servo;                           // create servo object to control a servo
TinyGPSPlus gps;                       // sets up gps
SoftwareSerial ss(8, 7);               //sets up software serial for GPS

//variables
int potpin = 0;         // analog pin used to connect the potentiometer
int pot = 0;            // variable to read the value from the analog pin
int tper = 0;           //variable for throttle percentage
int srbuttonstate = 0;  //stop run switch state
int mabuttonstate = 0;  // manual auto switch state
float gpsspeed = 0.0;   //speed over ground from GPS
float setspeed = 0.0;   //set speed for auto mode
int servopos = 0;       //servo position


void setup() {
  //servo
  servopos = constrain(servopos, 0, 90);  //sets limit on servo
  servo.write(servopos);                  //sets servo to idle position
  servo.attach(9);                        // attaches the servo on pin 9 to the servo object

  //pins
  pinMode(3, INPUT);   //set stop/run input pin
  pinMode(4, INPUT);   //set man/auto input pin
  pinMode(9, OUTPUT);  //sets servo output

  //lcd
  lcd_1.init();  // initialize the lcd
  lcd_1.backlight();

  //GPS
  //uncomment for testing Serial.begin(115200);
  ss.begin(9600);
}

void loop() {
  //gps
  if (gps.speed.isUpdated()) {
    gpsspeed = (gps.speed.mph());
    //uncomment for testing Serial.println(F("Speed:"));
    //uncomment for testing Serial.println(gps.speed.mph());
  }

  while (ss.available() > 0) {
    gps.encode(ss.read());
  }

  //buttons
  srbuttonstate = digitalRead(3);  //read stop/run switch
  mabuttonstate = digitalRead(4);  //read man/auto switch

  if (srbuttonstate == LOW)  //standby loop
  {
    servopos = 0;                        //set servo to idle
    servo.write(servopos);               // move servo to idle
    lcd_1.setCursor(0, 0);               //go to start of screen
    lcd_1.print(" System Standby  ");    //print status
    lcd_1.setCursor(0, 1);               //go to second line
    lcd_1.print("                   ");  //clear second line
  }

  else if (mabuttonstate == LOW)  //manual mode loop
  {
    lcd_1.setCursor(0, 0);                //go to start of screen
    lcd_1.print("     Manual     ");      //print status
    lcd_1.setCursor(0, 1);                //go to second line
    lcd_1.print(" Throttle %:     ");     //print throttle
    lcd_1.setCursor(12, 1);               //go to end of writing
    lcd_1.print(tper);                    // print throttle position
    pot = analogRead(potpin);             // reads the value of the potentiometer (value between 0 and 1023)
    tper = map(pot, 1023, 0, 0, 100);     //scale for throttle percentage
    servopos = map(pot, 0, 1023, 0, 90);  // scale it to use it with the servo (value between 0 and 90)
    servo.write(servopos);                // sets the servo position according to the scaled value
  }

  else {                                   //auto mode loop
    pot = analogRead(potpin);             //reads potentiometer position
    pot = (pot - 1023);                   //converts pot singal to negative- backwards pot
    setspeed = (pot * -.003);             //converts pot signal to speed
    tper = map(servopos, 0, 90, 0, 100);  //converts servo position to throttle position
    lcd_1.setCursor(0, 0);                //goto beginning of screen
    lcd_1.print("SS:      AS:");          //prints top line headings
    lcd_1.setCursor(12, 0);               // goto gpsspeed part of screen
    lcd_1.print(gpsspeed);                //prints gps speed
    lcd_1.setCursor(3, 0);                //goto setspeed part of screen
    lcd_1.print(setspeed);                //prints setspeed
    lcd_1.setCursor(0, 1);                //go to second line
    lcd_1.print(" Throttle %:     ");     //print throttle
    lcd_1.setCursor(12, 1);               //go to end of writing
    lcd_1.print(tper);                    // print throttle position


    if (setspeed > gpsspeed) {    //speed too slow loop
      servopos = (servopos + 2);  //increases throttle position
      servo.write(servopos);      //moves servo to increased throttle position
      delay(1000);                //wait one second
    }

    else if (setspeed < gpsspeed) {  //speed too fast loop
      servopos = (servopos - 2);     //decreases throttle position
      servo.write(servopos);         //moves servo to decreased throttle position
      delay(1000);                   //wait one second
    }

    else {                    //speed accurate loop
      servo.write(servopos);  //keeps servo in same position
      delay(1000);            //wait one second
    }
  }
}

Pin 0 is a digital pin, not an analog one. It won’t work to read a pot.

To the boat?

This looks odd. In setup() servopos is 0, so no need to limit its value here. And, servo.attach() should be called before acting on the servo.

Later you are comparing setspeed and gpsspeed, two floating point variables. They will almost never be equal. You should allow for some hysteresis or tolerance when comparing them.

Your project is one of speed control: set up a reference value and operate an actuator to control the actual speed. You could do better using a PID algorithm. I haven’t used PID with an MPU and cannot be of much help…

Make sure that the buck converter can easily handle the full stall current of the servo (2 - 3 Amperes or more for big ones).

Many people aren't aware that motors and servos briefly draw the stall current every time they start moving.

Thanks guys!

The pot is connected to A0 and I’m not sure how that got missed in code. Whoops.

I think some of that code got shifted around while I was troubleshooting. Normally the constrain is in the auto mode loop to keep the servo from going too far either direction. I also like the idea about adding a “buffer” for the speed comparison, I’ll have to look into that.

The buck converter is good for up to 5a and I have confirmed that it’s a perfect 6.0v at the servo even when it’s running erratically

Through some googling I think I found the problem- it appears softwareserial conflicts with the servo controller. I’d like to stick with my uno board if possible- is using the hardware servo as easy as connecting to pins 0/1? I’m aware I’ll have to disconnect to upload code and can’t use the serial monitor with the ide, but neither of those are big deals for this project.

Plan B would be to upgrade to a mega or another board with a separate hardware serial (maybe a mini?)

A Leonardo or a Micro will do.

Wanted to just update that I got the code working well. As I suspected, the software serial was interfering with the servo control. Once switching to the hardware serial everything worked perfectly. I do have do disconnect the GPS to upload code but that shouldn't be a big concern since hopefully I won't have to work on the code much more :wink:

I did end up having to put the GPS update in an interrupt as it was not updating properly. The only downside to this is in manual mode there is a 2 sec delay with adjustments, but that shouldn't be a concern as I mostly intend to use that mode for short bursts of speed and/or a backup if the GPS goes haywire.

Thought I would post my final code for anybody curious:

//libraries and definitions
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <Wire.h>
#include <TinyGPSPlus.h>

//objects
LiquidCrystal_I2C lcd_1(0x27, 16, 2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
Servo servo;                           // create servo object to control a servo
TinyGPSPlus gps;                       // sets up gps

//variables
int potpin = 0;         // analog pin used to connect the potentiometer
int pot = 0;            // variable to read the value from the analog pin
int tper = 0;           //variable for throttle percentage
int srbuttonstate = 0;  //stop run switch state
int mabuttonstate = 0;  // manual auto switch state
float gpsspeed = 0.0;   //speed over ground from GPS
float setspeed = 0.0;   //set speed for auto mode
int servopos = 0;       //servo position
uint32_t timer = millis();  //timer 

void setup() {
  //servo
  servo.attach(9);        // attaches the servo on pin 9 to the servo object
  servo.write(servopos);  //sets servo to idle position

  //pins
  pinMode(3, INPUT);   //set stop/run input pin
  pinMode(4, INPUT);   //set man/auto input pin
  pinMode(9, OUTPUT);  //sets servo output

  //lcd
  lcd_1.init();       // initialize the lcd
  lcd_1.backlight();  // turn on LED backlight

  //GPS
  Serial.begin(9600);
}

void loop() {
  //gps
  if (gps.speed.isUpdated()) { //check if GPS has new speed data available
    gpsspeed = (gps.speed.mph()); //store GPS speed
  }

  while (Serial.available() > 0) { //check if serial available
    gps.encode(Serial.read()); //read serial data from gps
  }

  if (millis() - timer > 2000) { //every two seconds, run rest of program
    timer = millis();  // reset the timer

    //buttons
    srbuttonstate = digitalRead(3);  //read stop/run switch
    mabuttonstate = digitalRead(4);  //read man/auto switch

    if (srbuttonstate == LOW)  //standby loop
    {
      servopos = 0;                        //set servo to idle
      servo.write(servopos);               // move servo to idle
      lcd_1.setCursor(0, 0);               //go to start of screen
      lcd_1.print(" System Standby  ");    //print status
      lcd_1.setCursor(0, 1);               //go to second line
      lcd_1.print("                   ");  //clear second line
    }

    else if (srbuttonstate == HIGH && mabuttonstate == LOW)  //manual mode loop
    {
      pot = analogRead(potpin);             // reads the value of the potentiometer (value between 0 and 1023)
      servopos = map(pot, 1023, 0, 0, 90);  // scale it to use it with the servo (0-90)
      servo.write(servopos);                // sets the servo position according to the scaled value
      tper = map(servopos, 0, 90, 0, 100);  //scale for throttle % (0-100)
      lcd_1.setCursor(0, 0);                //go to start of screen
      lcd_1.print("     Manual     ");      //print status
      lcd_1.setCursor(0, 1);                //go to second line
      lcd_1.print(" Throttle %:     ");     //print throttle
      lcd_1.setCursor(12, 1);               //go to end of writing
      lcd_1.print(tper);                    // print throttle position
    }

    else {                                    //auto mode loop
      servopos = constrain(servopos, 0, 90);  //sets limit on servo
      pot = analogRead(potpin);               //reads potentiometer position
      pot = (pot - 1023);                     //converts pot singal to negative- backwards pot
      setspeed = (pot * -.003);               //converts pot signal to speed
      tper = map(servopos, 0, 90, 0, 100);    //converts servo position to throttle position
      lcd_1.setCursor(0, 0);                  //goto beginning of screen
      lcd_1.print("SS:      AS:");            //prints top line headings
      lcd_1.setCursor(12, 0);                 // goto gpsspeed part of screen
      lcd_1.print(gpsspeed);                  //prints gps speed
      lcd_1.setCursor(3, 0);                  //goto setspeed part of screen
      lcd_1.print(setspeed);                  //prints setspeed
      lcd_1.setCursor(0, 1);                  //go to second line
      lcd_1.print(" Throttle %:     ");       //print throttle
      lcd_1.setCursor(12, 1);                 //go to end of writing
      lcd_1.print(tper);                      // print throttle position


      if (setspeed > (gpsspeed + .05)) {  //speed too slow loop
        servopos = (servopos + 2);        //increases throttle position
        servo.write(servopos);            //moves servo to increased throttle position
      }

      else if (setspeed < (gpsspeed - .05)) {  //speed too fast loop
        servopos = (servopos - 2);             //decreases throttle position
        servo.write(servopos);                 //moves servo to decreased throttle position
      }

      else {                    //speed accurate loop
        servo.write(servopos);  //keeps servo in same position
                                // delay(3000);            //wait 3 seconds
      }
    }
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.