ServoTimer2 works for a few seconds, then stops working.

Hi everyone,
So far, any problem I have had I have found answers by searching the forums or with Google. But not so this time.

Arduino Uno (R3), with TowerPro MG996R

My problem is that I am driving a servo using ServoTimer2. It works for a few seconds, then stops. I use the Serial Monitor to see the value I am passing to ServoTimer2 continues to change (and it does), but the pulse width out of the servo pin stops changing. Like I said, it works for a few seconds, the pulse width varies with the value passed to the ServoTimer2 library function, but then stops varying even though the value passed to it continues to vary. I can see on my scope the pulse width is around the 1.0 - 1.5ms, and the pulses come one for each 12-13ms. All that sounds ok from what I have read

It’s a remote control car and the servo does the steering.
I am using ServoTimer2 because I am using VirtualWire (which had timer conflicts with the ‘Servo’ library).
I have had this arrangement work before and I didn’t see this behaviour. I had other problems then. What I have changed since then is I got rid of a stepper motor I was using to drive the back wheels and replaced it with a DC motor (still to be tested), which means I removed the call to the ‘stepper’ library functions and now will just use analogWrite. Also, I have added some simple code to turn headlights on and off, and tidied up the code a bit.
But I don’t think I have broken anything because IT WORKS! (for a while)… so maybe something is a little bit broken.

I will attach my code. It looks more complex than it is (I will remove some superfluous commentary).
The transmitter sends the A/D values from the joysticks, and sticks an A or B at the front to differentiate which data is for steering and for speed (it also sends a C or D to turn lights on and off).
This code receives the transmitted data and decodes it. This has worked in my earlier version when other things weren’t working very well.
The received data is turned back into a number from a string, then mapped to a useful scale.

If anyone else has seen this behaviour from ServoTimer2, I would love to hear about it.
Thanks,
Eric

#include <ServoTimer2.h>  // Doesnt' seem to let me use pin 11 for analogWrite!
#include <VirtualWire.h>  // Takes over control of pins 9, 10, so can't use them
                          // also takes over timer1, that's why use ServoTimer2.
#define MIN_PULSE  750    // Min and max times permissible for ServoTimer2.
#define MAX_PULSE  2250   // These values are pulse durations in microseconds.

// need to undef the following for Virtual Wire?
#undef int
#undef abs
#undef double
#undef float
#undef round

int MotorSpeed;      // Speed setting 
int SpeedReading;    // Signal received from transmitter after conversion from string
char SpeedString[4]; // String recieved from transmitter, after removal of leading character

int SteerPosn;
int SteerReading;    // ditto for steering
char SteerString[4];

char LightString[1]; // record light input light command so can be displayed 

int LIGHTS = 3; // drive headlights from pin 1
int IN1 = 7;    // IN1 and IN2 control motor direction
int IN2 = 8;
int ENA = 5;    // must be PWM, duty cycle determines DC motor speed
int LED = 13;   // There is a LED on the Arduino board connected at pin 13.
    
ServoTimer2 servoSteer;  //  create servo object to control the steering servo motor

//------------
void setup() {
  
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(ENA, OUTPUT);
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  analogWrite(ENA, 0);      // should be able to use both digitalWrite and analogWrite on one pin.

  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);   // debug, to see what MotorDriver is doing
  pinMode(LIGHTS, OUTPUT);
  digitalWrite(LIGHTS, LOW);
  
  servoSteer.attach(6);     // attaches the servo on pin 6 (a PWM pin) to the servo object 

  Serial.begin(9600);       // start serial comms to the monitor for debugging
  
  // setup VirtualWire for RF comms
  vw_set_ptt_inverted(true); // Required for my transmitter/receiver (I think)
  vw_set_rx_pin(2);          // receive data on this pin
  vw_setup(2000);	     // Bits per sec
  vw_rx_start();             // Start the receiver PLL running
  
  LightString[0] = 'C';      // Initialise lights to be off
  
}

//-----------
void loop() {

// First, decode transmitted data

  uint8_t buf[VW_MAX_MESSAGE_LEN];      // these lines copied from VirtualWire
  uint8_t buflen = VW_MAX_MESSAGE_LEN;  // example code.
  if (vw_get_message(buf, &buflen))     // Non-blocking... whatever that means
  {
    int i;
    if(buf[0] == 'A')   // data is Speed data
    {
      for (i=0; i<(buflen-1); i++)              
        SpeedString[i] = char(buf[i+1]); // copy data, leaving out leading 'A' or 'B' char
      SpeedString[(buflen-1)] = '\0';   
    }
    else if(buf[0] == 'B') // data is Steering data
    {
      for (i=0; i<(buflen-1); i++)
        SteerString[i] = char(buf[i+1]);
      SteerString[(buflen-1)] = '\0';
    }
    else if(buf[0] == 'C') // turn lights on
    {
      digitalWrite(LIGHTS, HIGH);
      LightString[0] = 'C';
    }
    else if(buf[0] == 'D') // turn lights off
    {
      digitalWrite(LIGHTS, LOW);
      LightString[0] = 'D';
    }
    else
      Serial.println(" uh oh ");  // debug
  }   

// Second, manipulate speed and steering data to useable formats

  SpeedReading = atoi(SpeedString);
  MotorSpeed = map(SpeedReading, 0, 1023, -255, 255); // convert 10-bit A/D to 8-bit PWM (+ and - range)
           
  SteerReading = atoi(SteerString);
  SteerPosn = map(SteerReading, 0, 1023, MAX_PULSE, MIN_PULSE);
        
 // Now set speed
  if (MotorSpeed > 30) 
  {
    // set motor direction
    digitalWrite(IN2, LOW);
    digitalWrite(IN1, HIGH);
    // set motor speed
    analogWrite(ENA, MotorSpeed);
  }  
  else if (MotorSpeed < -30) 
  {
    // reverse motor direction
    digitalWrite(IN1, LOW);
    digitalWrite(IN2, HIGH);
    // set motor speed
    analogWrite(ENA, -MotorSpeed); // Value must be positive, 0-255
  }
  else
  digitalWrite(ENA, LOW);  // Disable the driver, turn the motor off, should be able to 
                           // use analogWrite and digitalWrite on the same pin for UNO

// Now set steering  
  servoSteer.write(SteerPosn);

// Send data to serial monitor for debugging: 
  
  Serial.print(", MotorSpeed = ");
  Serial.print(MotorSpeed);
  Serial.print(", SteerPosn = ");
  Serial.println(SteerPosn);

}

The VirtualWire library looks to do some heavy lifting in timer1 overflow ISR, this is probably throwing the ServoTimer2 timing completely out, or causing it to miss interrupts. timer1 ISR is running every 62.5us.

Also looking at the code for ServoTimer2 I don't trust it to be rock solid. There seems to be no disabling of interrupts multiple volatiles are updated for instance.

Thanks for the reply MarkT,

Based on your post, I tried a few things to change how the two (VirtualWire and ServoTimer2) interact (changing order, turning them on and off, etc). Nothing worked, but I did see some interesting things.

I had a look at the ServoTimer2.h and .cpp files, and though a lot of it was gobbledigoop, it was clear that the servo pulses were supposed to repeat every 20ms. I remember seeing them repeat every 12-13ms. So I put my oscilloscope on the servo pin again and surprise surprise, it was repeating every 20ms. If I move my joystick in the direction to increase the pulse width, it behaved properly (the pulse width would increase, then decrease to the mid width of 1.5ms). However, if I move my joystick in the other direction to decrease the pulse width, the pulse width would decrease and stay decreased, and simultaneously, the pulse spacing would drop from around 20ms to 12ms. And there it would stay. I plugged the servo back in, and sure enough, I had no problems turning left, but as soon as I tried to turn right, it would freeze. So my car, like Derek Zoolander, is not an ambiturner.

I looked at 'right-turning' again. It seemed to lock up when I pushed the joystick full-scale, but seemed to work when pushing the joystick only part way. I did earlier change the pulse width range with no success, but maybe there is something it doesn't like when the pulse width approaches the MIN_PULSE width.

Bed time now. I will have to investigate tomorrow.

Wel another quick look at ServoTimer2.cpp doesn't suggest anything, where did you get your copy from BTW?

Well, I have found the problem! By the way, the .h and .cpp files for ServoTimer2 have the line in them:

define FRAME_SYNC_PERIOD 20000 // total frame duration in microseconds

I think this is how the 20ms pulse repetition pulse rate is set.

But back to my problem. Believe it or not, the problem wasn’t with the library files, but with my code. I know! I couldn’t believe it either! (That’s not to say there aren’t issues with the interrupt handling you mentioned in your posts.) But I digress… I found the servo started misbehaving when the steering joystick ADC output sent from the transmitter went from 999 to 1000 (10-bit ADC, 1023 being the maximum value). So it had to do with the way I managed the string of digits transmitted by RF. When there were four digits to send, there was no space left for a null terminator. And something somewhere wasn’t happy about that. I added another character to all my arrays used to hold the transmitted and received strings and now everything works. So thanks for spending the time to look at my problem, hopefully I can reciprocate one day.

Well you fooled me with this statement:

Like I said, it works for a few seconds, the pulse width varies with the value passed to the ServoTimer2 library function, but then stops varying even though the value passed to it continues to vary.

Perhaps you should have been clearer that the values passed to the library function were not actually verified, only the values passed from the other end of the RF link...

The golden rule of troubleshooting is take nothing for granted until you've proved it to be the case :)

Huh.. I thought I would be dealing with same problem again.. virtual wire and servo already give me headache...

As conclusion, the servotimer2 and virtualwire work fine...

because I did compiled the code to my arduino and everything is fine but I didn't have the opportunity to try the servos as they were with another fellas. I will try the servo once everything iis gathered.