Go Down

Topic: Servo twitches on my tankbot (solved - analogWrite/servo library clash) (Read 1 time) previous topic - next topic

GH

Having got the bump sensor working (thanks everyone!) I've been working on adding a sensor arm to the tankbot I am building.

The bot is controlled by using the ardunio serial monitor window, basically typing a letter and hitting return ('f' for forward, 'u' to raise the servo arm etc.)

Thing is, unless I stick a stop command at the end of the loop, the bot will continue executing the last command entered until told to stop.  If I put the stop command in, the servo twitches.

As per usual, I am sure I am missing something obvious

Cheers


Code: [Select]
//Code based on the larrybot from www.luckylarry.co.uk - with eternal gratitude to Larry
#include <Servo.h>

Servo myservo;  // create servo object to control a servo
int bumppin=11;                                   // Connect bumper switch here
int motor1Pin1 = 3;                             // pin 2 on L293D
int motor1Pin2 = 4;                             // pin 7 on L293D
int enable1Pin = 9;                             // pin 1 on L293D
int motor2Pin1 = 5;                             // pin 10 on L293D
int motor2Pin2 = 6;                             // pin  15 on L293D
int enable2Pin = 10;                            // pin 9 on L293D
int incomingByte = 0; // for incoming serial data

// variable for steering void statements
int dist;
int bump =0;                                    //bump variable
int gonogo=1;

void setup() {
 myservo.attach(12);  // attaches the servo on pin 12 to the servo object
 // set the motor pins as outputs:
 pinMode(motor1Pin1, OUTPUT);
 pinMode(motor1Pin2, OUTPUT);
 pinMode(enable1Pin, OUTPUT);
 pinMode(motor2Pin1, OUTPUT);
 pinMode(motor2Pin2, OUTPUT);
 pinMode(enable2Pin, OUTPUT);
 pinMode (bumppin, INPUT);


}

void loop()
{
  Serial.begin(9600);

bump=digitalRead(bumppin); // read bump high = no bump, low=bump
Serial.println(bump);
if (bump ==LOW)
 {
 backward (50);
 Serial.flush();
}


if (Serial.available() > 0)
{
// read the incoming byte:
incomingByte = Serial.read();
}
delay (100);
 if (incomingByte =='d')
{
 myservo.write(90);
}
 if (incomingByte =='u')
{

 myservo.write(0);
}
 if (incomingByte =='m')
{
 myservo.write(45);
}

if (incomingByte =='f')
 {
 forward (500);
 incomingByte=0;
}

if (incomingByte =='b')
{
 backward (500);
 
incomingByte=0;
}

if (incomingByte =='l')
{
 turnL (300);

incomingByte=0;
}

if (incomingByte =='r')
{
 turnR (300);

 incomingByte=0;
}

if (incomingByte =='s')
{
stop();
 incomingByte=0;
}


// I need this to stop the bot rampaging off if unattended, but makes my servo twitch
stop();
incomingByte=0;


}


void stop ()
{
// stop : force both motor speeds to 0
analogWrite (enable1Pin, 0);
analogWrite (enable2Pin, 0);
digitalWrite (motor2Pin1,LOW);
digitalWrite (motor1Pin1,LOW);
digitalWrite (motor2Pin2,LOW);
digitalWrite (motor1Pin2,LOW);
}

void forward(int dist)
{
 // forward : set both motors forward
   digitalWrite (enable1Pin, HIGH);
   digitalWrite (enable2Pin, HIGH);
   digitalWrite(motor1Pin1, LOW);
   digitalWrite(motor1Pin2, HIGH);
   digitalWrite(motor2Pin1, LOW);
   digitalWrite(motor2Pin2, HIGH);
delay (dist);

}

void backward(int dist)
{
 // forward : set both motors forward    
    digitalWrite (enable1Pin, HIGH);
   digitalWrite (enable2Pin, HIGH);
   digitalWrite(motor1Pin1, HIGH);
   digitalWrite(motor1Pin2, LOW);
   digitalWrite(motor2Pin1, HIGH);
   digitalWrite(motor2Pin2, LOW);
delay (dist);
}

void turnL (int dist)
{
     // turn left
   digitalWrite (enable1Pin, HIGH);
   digitalWrite (enable2Pin, HIGH);
   digitalWrite(motor1Pin1, HIGH);
   digitalWrite(motor1Pin2, LOW);
   digitalWrite(motor2Pin1, LOW);
   digitalWrite(motor2Pin2, HIGH);
delay (dist);

}

void turnR (int dist)
{
     // turn right
   digitalWrite (enable1Pin, HIGH);
   digitalWrite (enable2Pin, HIGH);
   digitalWrite(motor1Pin1, LOW);
   digitalWrite(motor1Pin2, HIGH);
   digitalWrite(motor2Pin1, HIGH);
   digitalWrite(motor2Pin2, LOW);
delay (dist);

}

PaulS

Code: [Select]
if (Serial.available() > 0)
{
// read the incoming byte:
incomingByte = Serial.read();
}
delay (100);
  if (incomingByte =='d')
{
  myservo.write(90);
}
  if (incomingByte =='u')
{

  myservo.write(0);
}

What is the delay for?

Is there any possibility that two of the if blocks will ever be executed on one pass? I don't see that happening. A switch statement would make more sense than all these if statement.

At the very least, a proper if/else if structure should be used.

The entire switch statement or if/else if structure should be inside the if(Serial.available() > 0) block. There is nothing for your bot to do differently, if no serial data has come in.

Code: [Select]
  Serial.flush();
Please explain why you are throwing away random amounts of data.

Quote
Thing is, unless I stick a stop command at the end of the loop, the bot will continue executing the last command entered until told to stop.

That's what it should do. You don't have to keep telling your computer or light to stay on, do you?

Quote
If I put the stop command in, the servo twitches.

Twitches? There's a nice technical term for you. What does this mean?

What happens if the only commands that the bot receives are to move the servo arm? Does the stop() still cause the servo to "twitch"?

How is the servo being powered?

Korman

Quote
Code: [Select]
analogWrite (enable1Pin, 0);


analogWrite and the Servo Library don't work together if both want to use Timer1. What's the point of analogWrite() her anzway, when you use digitalWrite in other other occasions?

Korman

GH


Code: [Select]

delay (100); 

What is the delay for?

Ooops.  I forgot to take that out.  I was experimenting to see if the twitch (see later) was due to the speed of things running.

Is there any possibility that two of the if blocks will ever be executed on one pass? I don't see that happening. A switch statement would make more sense than all these if statement.


A fair point, my programming is (evidently) at a very primitive stage and I hadn't sat down and worked out the proper switch/case/break thing.  I'll have a stab at that later.


Code: [Select]
  Serial.flush();
Please explain why you are throwing away random amounts of data.

In a previous post of mine, I was having trouble making a bump sensor work.  One thing that seemed to work was to read the bump sensor, reverse the bot and then clear the serial buffer so that nothing else happened until the next run through the loop.
There is probably a better way of doing that but I've not worked it out yet.


Quote
Thing is, unless I stick a stop command at the end of the loop, the bot will continue executing the last command entered until told to stop.

That's what it should do. You don't have to keep telling your computer or light to stay on, do you?

Which is all well and good, but leaving that stop statement in seemed to be what caused the twitch...

Speaking of which...

Quote
If I put the stop command in, the servo twitches.

Twitches? There's a nice technical term for you. What does this mean?

Well, the dictionary defines "twitch" as a jerky movement or spasm.  Which seemed a pretty apt way to describe the way the servo sits there and periodically makes a little jerk.

What happens if the only commands that the bot receives are to move the servo arm? Does the stop() still cause the servo to "twitch"?


The servo twitches even if you do nothing.

How is the servo being powered?
It has a power supply of its own to avoid browning the arduino out (and yes, I remembered to common the grounds for it, the arduino and the motor driver chip, for a change...!)


GH


Quote
Code: [Select]
analogWrite (enable1Pin, 0);


analogWrite and the Servo Library don't work together if both want to use Timer1. What's the point of analogWrite() her anzway, when you use digitalWrite in other other occasions?

Korman



Aha!  Y'see, that's where a fresh pair of eyes is invaluable.  When I first set things up, I was using analogWrite with a speed variable to allow the robot to move at different speeds.  I changed my mind but evidently my baby addled brain missed that last pair of analogWrite's!

THANKS!

Also, I didn't know that about the analog/servo interaction.  Well worth noting that, again... thanks!

GH

A very big thank you to PaulS and Korman...

The delay statement definitely didn't help the smooth running of the commands and taking out that analogWrite made the 'twitch' go away!

Lesson for the day... don't code with a 6 week old baby in the house and you're doing the night feeds! :)

Korman

Quote
Also, I didn't know that about the analog/servo interaction.


This is one of the joys when programming embedded systems: You need to understand how the functions work on the hardware, otherwise you'll miss dependencies between seemingly independent functions because they use the same resources on the hardware. If you want to make a big improvement of your Arduino programming, take the time to properly understand how and when analogWrite() (using hardware PWM) interacts with the Servo library (which uses Timer1 on ATMega328 based Arduino) by checking it in the ATmega328 data sheet. If you do that the first time, it'll be hard work and will confuse you a lot, but this step is necessary to understand the system properly.

You can read the relevant chapters to your baby, it will bore it to sleep, guaranteed. And you probably too.

Korman

mowcius

Quote
Also, I didn't know that about the analog/servo interaction.

Is there any way to either control a servo/PWM some pins without the servo library or analogWrite?
I can't have something running in a really fast loop either.

Korman

#8
Mar 10, 2011, 04:19 pm Last Edit: Mar 10, 2011, 04:23 pm by Korman Reason: 1

Quote
Also, I didn't know that about the analog/servo interaction.

Is there any way to either control a servo/PWM some pins without the servo library or analogWrite?
I can't have something running in a really fast loop either.


You can mix the Servo library and PWM, you need only to avoid doing PWM on the two pins controlled by Timer1, which are pin 9 and pin 10. As long as you just use analogWrite(), you can safely use the pins 5,6,3 and 11. If you want to mess around with the PWM mode (phase correct PWM and such stuff) or change the frequency divider, better stay away from the pins connected to Timer0 (pins 5 and 6) too or you'll mess up your millis() reading and anzthign depending on it. Pins 3 and 11 use Timer2, which are safe to play around in your context.

In the ATmega data sheet this is all described quite well, once you figure out which table which contains the stuff you want. The key is to look in the schematics for pin functions named OC1A or OC1B (replace 1 by 0 or 2 if you care about Timer0 or Timer2) or the more generic version OCnA used in the general description how timers and PWM work.

Korman

mowcius

Ahh, I do remember something about this now.

So on my ATmega1280, I need to go look up which are on Timer1 and avoid them like the plague :D

Also, I thought it was Timer2 they used or is that some other library I'm getting confused with?

Korman


So on my ATmega1280, I need to go look up which are on Timer1 and avoid them like the plague :D


No, not at all, to the contrary Timer1 attached PWM ports are safe unless you attach more than 12 servos. On the ATmega128 the Servo library uses Timer5 for the first 12 servos, then as needed Timer1, Timer3 and Timer4 for the next 12 servos in this order for a total of 48 servos. It's defined in Servo.h. But basically, the same applies, don't use analogWrite on ports connected to the timer used by the Servo library. If I remember correctly, on the ATmega128 the 16-bit timers (which the Servo library uses) have 3 PWM ports attached. Better check the data-sheet for the real truth.

Korman


mowcius


Go Up