Question

So I have this old motorized wheelchair that I am trying to convert into a robot. I replaced the original motor driver with a sabertooth 2x12 and I’m using an Arduino micro to talk to it. The motors shaft goes all the way threw so I attached a magnet and a Hall Effect sensor to the back side to act as a rotary encoder. My current goal is to be able to tell the robot to move forward a certain amount of feet then stop. I wrote some code to do this linearly however this didn't work so well. Then I learned about interrupts and that sounded like exactly what I needed. So I tried my hand at that and things went wrong on several different levels.

LEVEL ONE: I have never seemed to be able to properly drive the motors it seems like any time I put the command to turn them on inside of a loop or if statement they decide to do what they want and move sporadically and unpredictably

LEVEL TWO: I feel like the interrupts are interrupting themselves and the thing I set in place to stop the wheels from moving forward because I can tell it to move 14 rotary encoder clicks forward and one wheel will continue moving way past 1000 clicks while the other stops

LEVEL THREE: couple times I guess I placed my interrupts wrong because when I uploaded the code windows would stop recognizing the Arduino and my driver would break until I uploaded the blink sketch after pressing the reset button which also reloaded and fixed my drivers. Then if I deleted one of my interrupts it would upload normally.

LEVEL FOUR: my Hall Effect sensors seem to not work right when the motors are on. They tend to jump from 1 to 200 clicks in a matter of seconds. This in turn floods my serial port and crashes the Arduino ide.

So as you can see there are several flaws somewhere in the system whether it’s hardware or software I don't know. Am I approaching this the right way or is there some Arduino secret I don’t know about that would make my life easier? If I am approaching this right could you take a look at my code below and see what I’m doing wrong.

 #include <Servo.h>//the motor driver uses this library

Servo LEFT, RIGHT;//left wheel right wheel

int RclickNum=0;//used for the rotory encoder
int LclickNum=0;//these are the number of "clicks" each wheel has moved

int D =115;//Drive
int R =70;//Reverse
int B =90;//Break

int Linterrupt = 1;//these are the interrupt numbers. 0 = pin 3 and 1 = pin 2
int Rinterrupt = 0;

int clickConvert = 7;// how many rotery encoder clicks equal a foot

void setup()
{
  Serial.begin(9600); //starting serial communication 
  LEFT.attach( 9, 1000, 2000);//attaching the motor controller that is acting like a servo
  RIGHT.attach(10, 1000, 2000);
  attachInterrupt(Linterrupt, LclickCounter, FALLING);//attaching the rotory encoders as interrupts that will 
  attachInterrupt(Rinterrupt, RclickCounter, FALLING);//trip when the encoder pins go from high to low


}
void loop()
{//This is for controling the robot using the standard wasd format
  int input= Serial.read();
  if(input == 'a')
    left(2);
  if(input == 'd')
    right(2);
  if(input == 'w')
    forward(2);
  if(input == 's')
    backward(2);
  if(input == 'e')
    STOP();
}

void forward(int feet)//this is called when w is sent threw the serial port and is where i am testing all of my code. 
{
  interrupts(); //turn on the interrupts
  while(RclickNum < feet * clickConvert || LclickNum < feet * clickConvert)// while either the left or right wheel hasnt made it to the desired distance
  {
    if(RclickNum < feet * clickConvert)//check if the right wheel has gone the distance
      RIGHT.write(D); //make the right wheel move
    else
      RIGHT.write(B);//stop the right wheel

    if(LclickNum < feet * clickConvert)
      LEFT.write(D);
    else
      LEFT.write(B);
  }
  noInterrupts();//stop the interrupts 
  resetCount();//set the click counters back to zero
}

//once i have the forward function working i will implament it through out the other functions
//----------------------------------------------------------------------

void backward(int feet)
{
  RIGHT.write(R);
  LEFT.write(R);
}

void left(int feet)
{
  RIGHT.write(D);
  LEFT.write(R);
}

void right(int feet)
{
  RIGHT.write(R);
  LEFT.write(D);
}

void STOP()
{
  resetCount();
  RIGHT.write(B);
  LEFT.write(B);
}

void LclickCounter()//this is called by the left encoder interrupt
{
  LclickNum++; 
  Serial.print("L");
  Serial.println(LclickNum); 
}

void RclickCounter()//this is called by the right encoder interrupt
{
  RclickNum++;
  M Serial.print("R");
  Serial.println(RclickNum);
}


void resetCount()
{
  RclickNum=0;
  LclickNum=0;
}
Was it helpful?

Solution

  1. don't use interrupt() and nointerrupt() (or cli() and sei()) as they will stop timer and serial interrupt, breaking a lot of things. Just set to 0 the counting variable OR use detachInterrupt and attachInterrupt.

  2. variable used inside interrupt AND normal execution flow should be declared as volatile, or their value my be unsyncornized. So declare them like volatile int RclickNum=0;

  3. interrupt should be fast to execute, as by default other interrupt will NOT execute while inside an interrupt.

  4. NEVER use Serial inside interrupt; if Serial buffer is full, it will call Serial.flush(), that will wait for Serial interrupt of byte written, but because you are alreadi inside an interrupt will never happen...dead lock aka you code hangs forever!

  5. because your "moving" function use quite a long time to execute, if multiple command arrive to the serial, thay will remain isnode the buffer until readed. So if in the terminal you write "asd" and then "e", you will see robot go left, backward, right, stop (yes, actually the stop function is not usefull as it does nothing because your "moving" function are "blocking", that mean they won't return until they ended, so the loop() code (and the read of "e") will not execute until the buffer of serial has been processed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top