SoftwareSerial and Servo conflict

Since changing over to 1.0 development program, I understand SoftwareSerial now is derived from NewSoftwareSerial, which is interrupt based. Ever since then, I get undesirable jitter to the servos when using both. I would think this is a fairly common need and suspect it is related to interrupts. Plenty of memory left in the sketch so that is not it. Is anyone else experiencing this, found a workaround, or willing to tell me my C programming skills are just rusty and it is something obvious?

Are you willing to post your sketch so that we can help you?

Yes, though it is still very much a draft but when I tried, the forum says it will not accept since it is >9500 characters. Suggestions?

I tried eliminating the built-in servo library (servo.h) and wrote my own simple servo driver that did not use interrupts. The built-in library version caused jitter that interfered with an accurate Ping reading. The routine below is very basic but it did solve the problem. I can tolerate a lax servo in the application I have but a better solution would be nice.

void servoPosition(int servopin, int pulsemicros)
{
for(int i=0; i<16; i++) { //gets about 90 degrees movement, call twice or change i<16 to i<32 if 180 needed
digitalWrite(servopin, HIGH);
delayMicroseconds(pulsemicros);
digitalWrite(servopin, LOW);
delay(25);
}
}

In my case, the problem was due to SoftwareSerial disabling interrupts while transmitting. This prevents the timer used by Servo from behaving correctly. To fix the problem, I modified the code to only transmit when Servo is not mid-pulse. See: http://pharos.ece.utexas.edu/wiki/index.php/Conflict_Between_Servo_and_Software_Serial_Libraries_-_02/15/2012

I have exactly the same problem, even with using the Arduino Uno hardware serial port (pins 0 and 1). Using SoftwareSerial, the problem is much worse, but it also occurs using the hardware serial port. Any suggestions to fix this?

Would it help to go back to Arduino 0023 and using the older NewSoftSerial library?

How much data are you trying to send via the serial port? Perhaps you need to find a way to send less data - perhaps send raw binary data instead of ascii data and let the PC or other recieving device convert it for display?

Any suggestions to fix this?

If you have bad code, you probably should fix it.

I have the same conflict and it’s very bad for my project. I didn’t understand how to solve it:

//variables: int-integer  float-a double that includes fractions
int MinWind=550; // wind of 1.5 m/s
int RudderZeroPos=50; 
int MinDist=200;// can be changed freely
int MotorOnSpeed=60; // can be changed by experiments
int incomingByte;      // a variable to read incoming serial data into

//the command "include" uses a special library of IDE
#include <Servo.h>  // uses a library that allows using Servo motors
Servo Rudderservo; //servo's values are between 0 to 180
Servo motor_sc;
Servo sailservo;
// defining servo "variables" (the value will be sent to the motors)

////////gps commands -taken from a big script
#include <SoftwareSerial.h>
#include <math.h>

SoftwareSerial gpsSerial(10, 11); // RX, TX (TX not used)
// attaching the gps to pins 10,11
const int sentenceSize = 80;
char sentence[sentenceSize];
//the gps get info in this array and then process it
//gps give lat.&long. in the format: ddmm.mmmm/dddmm.mmmm (d-degrees, m-minutes)

// our addons:
//coordinates: user's responsiblity:
float alfa=0; // the cartesian angle
int N=2;  //# of coordinates
int Pi=0; //counter
float plat[]={3206.6692,3206.6613};
float plong[]={03448.3814,03448.3855};
//(random coordinates)

//compass
#include <Wire.h> // for using I2C protocol
int HMC6352SlaveAddress = 0x42;
int HMC6352ReadAddress = 0x41; 
int headingValue;
///

//for the encoder
volatile int pulses=0;// volatile - a value that can be changed by interrupts, represents the angle
int encoder_angle=0;
int dir=0;
int encoderPinA = 7;//attaching encoder to pins 7 and 2 (beneath)
 int encoderPinB = 2;
//

void setup()  //setup is a one time event
{
  //delay is a function that stops the running (delaying) for the value givven in miliseconds
  //delay(1) is sometimes important to avoid debouncing / time for the signal to fall
 Rudderservo.attach(9);// attaching Rudderservo to pin #9
  delay(1);
  Rudderservo.write(RudderZeroPos);  //initial condition (i.c.)

  motor_sc.attach(6); // attaching speed controller to pin #6
  delay(1);
  motor_sc.write(50);  // 50 = 0 speed ; validate power to servo (Lipo battery)
//initial condition

sailservo.attach(5);// attaching sail servo to pin #5
  delay(1);
 sailservo.write(90);  //initial condition

 Serial.begin(9600); //making a serial communication between the computer and the Arduino board

   pinMode (encoderPinA,INPUT);
   //defining the pin presented by the value of encoderPinA as input (and not output)
   pinMode (encoderPinB,INPUT); //important for digital pins

   digitalWrite(2,HIGH);//digital pins get valus of HIGH/LOW
   digitalWrite(7,HIGH);//i.c. for the encoder 
   
  attachInterrupt(0, pulse_rise, HIGH); // "0" - for pin#2, calling the func. "pulse_rise", when pin#2 is HIGH
 
 Wire.begin(); //starting the Wire library

   gpsSerial.begin(9600); //enables serial communication with the GPS
 
 
   // "The Wire library uses 7 bit addresses throughout. 
  //If you have a datasheet or sample code that uses 8 bit address, 
  //you'll want to drop the low bit (i.e. shift the value one bit to the right), 
  //yielding an address between 0 and 127."
 HMC6352SlaveAddress = HMC6352SlaveAddress >> 1; // I know 0x42 is less than 127, but this is still required

}  //setup ends

//// compass
void compassread() 
{
    Wire.beginTransmission(HMC6352SlaveAddress);
  Wire.write(HMC6352ReadAddress);              // The "Get Data" command
  Wire.endTransmission();

  //time delays required by HMC6352 upon receipt of the command
  //Get Data. Compensate and Calculate New Heading : 6ms
  delay(6);

  Wire.requestFrom(HMC6352SlaveAddress, 2); //get the two data bytes, MSB and LSB

  //"The heading output data will be the value in tenths of degrees
  //from zero to 3599 and provided in binary format over the two bytes."
  byte MSB = Wire.read();
  byte LSB = Wire.read();

  float headingSum = (MSB << 8) + LSB; //(MSB / LSB sum)
  float headingInt = headingSum / 10; 

  Serial.print(headingInt);
  Serial.println(" degrees (Azimuth)");

  /////////  moving rudder
headingInt=90.0-headingInt;
// the relation between the unit circle and the direction of the north/compass
float a=headingInt-alfa;
  
  /// to delete (only 4 us)
    Serial.println ("headingInt (90-azimuth) is):");
  Serial.println (headingInt);
  Serial.println ("alfa is):");
  Serial.println (alfa);
  Serial.println ("a (the delta):");
  Serial.println (a);
  Serial.println ("Rudder servo read:");
  Serial.println (Rudderservo.read());
///

if (a>4)
{
  Rudderservo.write(RudderZeroPos-20);
  delay (1000); //time to make an effect
    Rudderservo.write(RudderZeroPos);
}
if (a<-4)
  {
  Rudderservo.write(RudderZeroPos+20);   
    delay (1000);
    Rudderservo.write(RudderZeroPos);
  }  
} //compass.read func. ends

  
//// gps:

void getField(char*buffer, int index)
//it "cuts" the part of the field we use for coordinates from a bigger field
{
  int sentencePos = 0;
  int fieldPos = 0;
  int commaCount = 0;
  while (sentencePos < sentenceSize)
  {
    if (sentence[sentencePos] == ',')
    {
      commaCount ++;
      sentencePos ++;
    }
    if (commaCount == index)
    {
      buffer[fieldPos] = sentence[sentencePos];
      fieldPos ++;
    }
    sentencePos ++;
  }
  buffer[fieldPos] = '\0';
} 


int displayGPS()
{
  char field[20];
  getField(field, 0);
  if (strcmp(field, "$GPRMC") == 0) //the part it cuts is staring with this phrase
  {
    Serial.print("Lat: ");
   getField(field, 3);  // puts the latitude's string in "field"
   // Serial.println(field);        
    //////////
    float n=0;
    n=atof(field);//puts the latitude in "n" as a float - will be used for calculations
    Serial.println(field);        
    getField(field, 4); // N/S
    Serial.println(field);
    
    Serial.print(" Long: ");
    getField(field, 5);  // number
    Serial.println(field);    
   float m=0;
    m=atof(field);
    Serial.println(field);        
    getField(field, 6);  // E/W
    Serial.println(field);
////////////  alfa  calculations   ///////////////
float deltalat=0;
deltalat=plat[Pi]-n;

float deltalong=0;
deltalong=plong[Pi]-m;
deltalong=deltalong*cos(n/100);
//////see next link for explanation
//   http://cgate.co.il/navigation/parallel.htm // the distance between longtitudes depends on the latitudes

//tan (+/-)90 = err so
if ((deltalong==0) && (deltalat <0))
{
  alfa=-90.0;
}
if ((deltalong==0) && (deltalat >0))
{
  alfa=90.0;
}
if (deltalong || 0)
{
alfa= atan2 (deltalat, deltalong) * 180 / PI; 
}
if (deltalong <0) //if destination is on left side of the unit circle
{
  alfa=alfa+180;
}


////////// reaching a WP ////////
if ((deltalat ==0) && (deltalong==0))
{
  Pi++;
      Serial.println("WE HAVE REACHED A WAYPOING");        
if (Pi==N)
      {
      Serial.println("WE HAVE REACHED THE LAST WAYPOING");                
      }
}

    Serial.print(" speed over ground in knots: ");
    getField(field, 7);  // number
    Serial.println(field); // 1 knot = 1.852 km/h
     float s=0;
     s=atof(field)*1.852;
     Serial.print(" speed over ground in km/h: ");
    Serial.println(s); // 1 knot = 1.852 km/h

    Serial.print(" course over ground in degrees(by gps: ");
    getField(field, 8);  // number
    Serial.print(field);

    Serial.print(" sUTC date : ");
    getField(field, 9);  // number
    Serial.println(field); //just for fun

    return 1;
  } else {
return 0;
  }
 
}


int gpsread()
{
  static int i = 0;
  if (gpsSerial.available())
  {
    char ch = gpsSerial.read();
    if (ch != '\n' && i < sentenceSize)
    {
      sentence[i] = ch;
      i++;
    }
    else
    {
     sentence[i] = '\0';
     i = 0;
if (     displayGPS() == true)
     return 1;
    }
  }
  return 0;
}

void pulse_rise(){ //counting steps - changes of the angle
dir=digitalRead(encoderPinA);
 if(dir==1)
 pulses++;//moving clockwise
 if (pulses>=1024) 
 pulses=pulses-1024;
 //the encoder have 1024 steps
 
 if(dir==0)
 pulses--;//moving counterclockwise
  if (pulses < 0)
pulses=pulses+1024;
}

void loop()  //the infinite part of the program
{
 if (gpsread() == true){
   compassread();////rudder rotation included inside
   
   int windspeed=analogRead(A0);
      Serial.print("windspeed:  ");
   Serial.println(windspeed, DEC);
   
if(windspeed<MinWind) motor_sc.write(MotorOnSpeed); //use motor
   else motor_sc.write(50); //stops the motor
   
      int dist1=analogRead(A1);
      Serial.print("distance sensor 1:  ");
   Serial.println(dist1);
   
      int dist2=analogRead(A2);
      Serial.print("distance sensor 2:  ");
   Serial.println(dist2);
   
if ((dist1<MinDist) || (dist2<MinDist))
  motor_sc.write(50);
  
  if ((dist1<MinDist) && (dist2<MinDist))
  {
        Serial.println ("dist1&dist2<MinDist, moving Rudder");
  Rudderservo.write(RudderZeroPos+80);
  delay (2000);
    Rudderservo.write(RudderZeroPos);
  }
  else
    if (dist1<MinDist)
  {
    Serial.println ("dist1<MinDist, moving Rudder");
    Rudderservo.write(RudderZeroPos+40);
  delay (1000);
    Rudderservo.write(RudderZeroPos);
  }
  else
  if (dist2<MinDist)
  {
        Serial.println ("dist2<MinDist, moving Rudder");
    Rudderservo.write(RudderZeroPos+40);
  delay (1000);
    Rudderservo.write(RudderZeroPos);
  }
  
   encoder_angle = map(pulses, 0, 1023, 0, 359);  //maps the value to a smaller range
        Serial.print (encoder_angle);
     Serial.println ("  degrees of the encoder");
 
 if (windspeed>MinWind)
 { 
   ///we start when encoder's flag is directing to the head at pulses=0
   // we start with mifras Orthogonal to body at sailservo=90 (at setup)

     if((encoder_angle<60) && (encoder_angle>=0)) 	///between 0 to 60
     {
     	sailservo.write(60);
     delay (5000);
     }

     if((encoder_angle>300) && (encoder_angle<360)) 	///between 300 to 360
     {
     	sailservo.write(300);
     delay (5000);
     }

     if((encoder_angle<=170) && (encoder_angle>=60)) 	///between 60 to 170
     	sailservo.write(encoder_angle  + 15);

     if((encoder_angle<=300) && (encoder_angle>=190)) 	///between 190 to 300
     	sailservo.write(encoder_angle  -165);

     if((encoder_angle<=190) && (encoder_angle>=170)) 	///between 170 to 190
     	sailservo.write(encoder_angle/2);
 }

         Serial.print (sailservo.read());
     Serial.println ("  degrees of the sail servo");
 }

}  //loop end

Hello!

I've solved this problem with AltSoftSerial and ServoTimer2. https://github.com/vrxfile/track_robot

In ServoTimer2.h it is necessary to comment the line typedef uint8_t boolean;

https://github.com/nabontra/ServoTimer2 https://github.com/PaulStoffregen/AltSoftSerial