speed sensor and problem with interruption

Dears,
I finished collecting my first robot project with these components:

2 gear DC Motor (3-6V)
L298N Driver
6 Battery 1.5V each (after measurment 7.8V)
Arduino UNO (powred directly from my PC with USB connection)

DCMotor.jpg
L298N Model.jpg

and this is my montage

and my full code

//  Moteur Left
int LM_AV = 6; // Mettre (HIGH ici et LOW sur LM_AR) pour faire avancer le moteur vers l'avant
int LM_AR_7 = 7; // Mettre (HIGH ici et LOW sur LM_AV) pour faire réculer le moteur vers l'arrière
int LM_EN_5 = 5; // PWM port pour mettre une vitesse au moteur Left



//  Moteur Right
int RM_AV = 8; // Mettre (HIGH ici et LOW sur RM_AR) pour faire avancer le moteur vers l'avant
int RM_AR = 9; // Mettre (HIGH ici et LOW sur RM_AV) pour faire réculer le moteur vers l'arrière
int RM_EN = 10; // PWM port pour mettre une vitesse au moteur Right

void setup()
{
  pinMode(LM_AV, OUTPUT);
  pinMode(LM_AR, OUTPUT);
  pinMode(LM_EN, OUTPUT);
  pinMode(RM_AV, OUTPUT);
  pinMode(RM_AR, OUTPUT);
  pinMode(RM_EN, OUTPUT);

}


void loop()
{
  moveForward(255, 255);
  delay(10000);
  moveForward(200, 200);
  delay(10000);
  moveForward(150, 150);
  delay(10000);
}

void stopMotorLeft()
{
  digitalWrite(LM_AV,LOW);
  digitalWrite(LM_AR,LOW);
  analogWrite(LM_EN,0);
}

void stopMotorRight()
{
  digitalWrite(RM_AV,LOW);
  digitalWrite(RM_AR,LOW);
  analogWrite(RM_EN,0);
}

void stopAll()
{
  stopMotorLeft();
  stopMotorRight();
}

void moveBackMotorLeft(int speed)
{
  digitalWrite(LM_AV,LOW);
  digitalWrite(LM_AR,HIGH);
  analogWrite(LM_EN,speed);
}

void moveBackMotorRight(int speed)
{
  digitalWrite(RM_AV,LOW);
  digitalWrite(RM_AR,HIGH);
  analogWrite(RM_EN,speed);
}

void moveBackward(int speedL, int speedR)
{
  moveBackMotorLeft(speedL);
  moveBackMotorRight(speedR);
}

void moveForwardMotorLeft(int speed)
{
  digitalWrite(LM_AV,HIGH);
  digitalWrite(LM_AR,LOW);
  analogWrite(LM_EN,speed);
}

void moveForwardMotorRight(int speed)
{
  digitalWrite(RM_AV,HIGH);
  digitalWrite(RM_AR,LOW);
  analogWrite(RM_EN,speed);
}

void moveForward(int speedL, int speedR)
{
  moveForwardMotorLeft(speedL);
  moveForwardMotorRight(speedR);
}

void turnLeft(int speedL, int speedR)
{
  moveBackMotorLeft(speedL);
  moveForwardMotorRight(speedR);
}

void turnRight(int speedL, int speedR)
{
  moveForwardMotorLeft(speedL);
  moveBackMotorRight(speedR);
}

when i run this project with 7.8V power supply, all my motors work fine with the differents PWM given

void loop()
{
  moveForward(255, 255);
  delay(10000);
  moveForward(200, 200);
  delay(10000);
  moveForward(150, 150);
  delay(10000);
}

But i need to measure the speed of my motors, so for that i inserted in my schema 2 LM393 speed sensor with 2 encoder disk (attached directly on my motors)

you can find the new schema here :

so for that i used TimerOne library

#include "TimerOne.h"

const byte MOTORLeft = 2;  // Motor 1 Interrupt Pin - INT 0
const byte MOTORRight = 3;  // Motor 2 Interrupt Pin - INT 1
unsigned int counter1 = 0;
unsigned int counter2 = 0;

// Float for number of slots in encoder disk
float diskslots = 20;  // Change to match value of encoder disk

//  Moteur Left
int LM_AV = 6; // Mettre (HIGH ici et LOW sur LM_AR) pour faire avancer le moteur vers l'avant
int LM_AR = 7; // Mettre (HIGH ici et LOW sur LM_AV) pour faire réculer le moteur vers l'arrière
int LM_EN = 5; // PWM port pour mettre une vitesse au moteur Left

//  Moteur Right
int RM_AV = 8; // Mettre (HIGH ici et LOW sur RM_AR) pour faire avancer le moteur vers l'avant
int RM_AR = 9; // Mettre (HIGH ici et LOW sur RM_AV) pour faire réculer le moteur vers l'arrière
int RM_EN = 10; // PWM port pour mettre une vitesse au moteur Right


void setup()
{
  pinMode(MOTORLeft, INPUT);
  pinMode(MOTORRight, INPUT);
  pinMode(LM_AV, OUTPUT);
  pinMode(LM_AR, OUTPUT);
  pinMode(LM_EN, OUTPUT);
  pinMode(RM_AV, OUTPUT);
  pinMode(RM_AR, OUTPUT);
  pinMode(RM_EN, OUTPUT);

  Serial.begin(9600);
  Timer1.initialize(1000000); // set timer for 1sec
  attachInterrupt(digitalPinToInterrupt (MOTORLeft), ISR_countLeft, RISING);  // Increase counter 1 when speed sensor pin goes High
  attachInterrupt(digitalPinToInterrupt (MOTORRight), ISR_countRight, RISING);  // Increase counter 2 when speed sensor pin goes High
  Timer1.attachInterrupt( ISR_timerone ); // Enable the timer
}

void loop()
{
  moveForward(255, 255);
  delay(10000);
  moveForward(200, 200);
  delay(10000);
  moveForward(150, 150);
  delay(10000);
}


void stopMotorLeft()
{
  digitalWrite(LM_AV, LOW);
  digitalWrite(LM_AR, LOW);
  analogWrite(LM_EN, 0);
}

void stopMotorRight()
{
  digitalWrite(RM_AV, LOW);
  digitalWrite(RM_AR, LOW);
  analogWrite(RM_EN, 0);
}

void stopAll()
{
  stopMotorLeft();
  stopMotorRight();
}

void moveBackMotorLeft(int speed)
{
  digitalWrite(LM_AV, LOW);
  digitalWrite(LM_AR, HIGH);
  analogWrite(LM_EN, speed);
}

void moveBackMotorRight(int speed)
{
  digitalWrite(RM_AV, LOW);
  digitalWrite(RM_AR, HIGH);
  analogWrite(RM_EN, speed);
}

void moveBackward(int speedL, int speedR)
{
  moveBackMotorLeft(speedL);
  moveBackMotorRight(speedR);
}

void moveForwardMotorLeft(int speed)
{
  digitalWrite(LM_AV, HIGH);
  digitalWrite(LM_AR, LOW);
  analogWrite(LM_EN, speed);
}

void moveForwardMotorRight(int speed)
{
  digitalWrite(RM_AV, HIGH);
  digitalWrite(RM_AR, LOW);
  analogWrite(RM_EN, speed);
}

void moveForward(int speedL, int speedR)
{
  moveForwardMotorLeft(speedL);
  moveForwardMotorRight(speedR);
}

void turnLeft(int speedL, int speedR)
{
  moveBackMotorLeft(speedL);
  moveForwardMotorRight(speedR);
}

void turnRight(int speedL, int speedR)
{
  moveForwardMotorLeft(speedL);
  moveBackMotorRight(speedR);
}

// Interrupt Service Routines

// Motor 1 pulse count ISR
void ISR_countLeft()
{
  counter1++;  // increment Motor 1 counter value
}

// Motor 2 pulse count ISR
void ISR_countRight()
{
  counter2++;  // increment Motor 2 counter value
}

// TimerOne ISR
void ISR_timerone()
{
  Timer1.detachInterrupt();  // Stop the timer
  Serial.print("Motor Left Speed 1: ");
  float rotationLEFT = (counter1 / diskslots) * 60.00;  // calculate RPM for Motor Left
  Serial.print(rotationLEFT);
  Serial.print(" RPM - ");
  counter1 = 0;  //  reset counter to zero
  Serial.print("Motor Right Speed 2: ");
  float rotationRIGHT = (counter2 / diskslots) * 60.00;  // calculate RPM for Motor Right
  Serial.print(rotationRIGHT);
  Serial.println(" RPM");
  counter2 = 0;  //  reset counter to zero
  Timer1.attachInterrupt( ISR_timerone );  // Enable the timer
}

this is my loop function :

void loop()
{
  moveForward(255, 255);
  delay(10000);
  moveForward(200, 200);
  delay(10000);
  moveForward(150, 150);
  delay(10000);
}

the problem is for the first Command moveForward(255, 255) it's OK, both motors work correctly with the full speed
But when i decrease the speed with (moveForward(200, 200) and moveForward(150, 150))
the left Motor works, but the right motor didn't work, and when i measure the voltage it indicates a 0 V
and when the loop comes again to max speed (moveForward(255, 255)) both motors work correctly
i tried to swap the wiring, and the problem happen on the left motor now

I made a rollback to my first sketch (without sensor and interruption) both motors work correctly with the different speed
have you seen a similar problem ? i suspect a power problem,

  • my L298N is powerd with 7.8V, the jumper of the 5volt regulator is present
  • my Arduino is powered by an USB cable to my PC

Get rid of Timer1 library as you don't know how to use it. Take a look at the BlinkWithoutDelay example to see how a sketch can be implemented to avoid delay() calls. Once you reorganized your sketch to don't have any delay() calls anymore, you can do the stuff you do in the ISR of Timer1 now in the loop().

Inside ISRs you must not use the Serial object as it depends on interrupts and inside interrupt handlers interrupts are disabled.

Hi Pylon,
Confirmed, My problem comes from delay function

void setup()
{
  pinMode(MOTORLeft, INPUT);
  pinMode(MOTORRight, INPUT);
  pinMode(LM_AV, OUTPUT);
  pinMode(LM_AR, OUTPUT);
  pinMode(LM_EN, OUTPUT);
  pinMode(RM_AV, OUTPUT);
  pinMode(RM_AR, OUTPUT);
  pinMode(RM_EN, OUTPUT);

  Serial.begin(9600);
  //Timer1.initialize(1000000); // set timer for 1sec
  attachInterrupt(digitalPinToInterrupt (MOTORLeft), ISR_countLeft, RISING);  // Increase counter 1 when speed sensor pin goes High
  attachInterrupt(digitalPinToInterrupt (MOTORRight), ISR_countRight, RISING);  // Increase counter 2 when speed sensor pin goes High
  //Timer1.attachInterrupt( ISR_timerone ); // Enable the timer
}
int previous_ms =0;
void loop()
{
  int now = millis();
  //Serial.println(now - previous_ms);
  moveForward(150, 130);
  if ((now-previous_ms)> 1000)
  {
  Serial.print("Motor Left Speed 1: ");
  float rotationLEFT = (counter1 / diskslots) * 60.00;  // calculate RPM for Motor Left
  Serial.print(rotationLEFT);
  Serial.print(" RPM - ");
  counter1 = 0;  //  reset counter to zero
  Serial.print("Motor Right Speed 2: ");
  float rotationRIGHT = (counter2 / diskslots) * 60.00;  // calculate RPM for Motor Right
  Serial.print(rotationRIGHT);
  Serial.println(" RPM");
  counter2 = 0;  //  reset counter to zero
  previous_ms = now;
}
  //delay(10000);
  //moveForward(200, 200);
  //delay(10000);
  //moveForward(150, 150);
  //delay(10000);
}

this code works
but i realized that i have another problem...
my LM393 sensor don't tell me the truth, the real rotation in RPM (calculated manually) is less than the RPM calculated with my counters

attachInterrupt(digitalPinToInterrupt (MOTORLeft), ISR_countLeft, RISING);  // Increase counter 1 when speed sensor pin goes High
  attachInterrupt(digitalPinToInterrupt (MOTORRight), ISR_countRight, RISING);  // Increase counter 2 when speed sensor pin goes High

//......
// Motor 1 pulse count ISR
void ISR_countLeft()
{
  counter1++;  // increment Motor 1 counter value
}

// Motor 2 pulse count ISR
void ISR_countRight()
{
  counter2++;  // increment Motor 2 counter value
}

i calculated the RPM manually with my eyes is about 130 RPM but this is the output of my sensor :

Motor Left Speed 1: 432.00 RPM - Motor Right Speed 2: 492.00 RPM
Motor Left Speed 1: 504.00 RPM - Motor Right Speed 2: 546.00 RPM
Motor Left Speed 1: 489.00 RPM - Motor Right Speed 2: 534.00 RPM
Motor Left Speed 1: 504.00 RPM - Motor Right Speed 2: 534.00 RPM
Motor Left Speed 1: 516.00 RPM - Motor Right Speed 2: 528.00 RPM
Motor Left Speed 1: 510.00 RPM - Motor Right Speed 2: 543.00 RPM
Motor Left Speed 1: 516.00 RPM - Motor Right Speed 2: 546.00 RPM

it seems that there is a coeficient 4 between ther real and the calculated RPM
do you have any idea about the cause ?

You should mark the counters as volatile, and read and reset the counters with interrupts disabled.

Did you adjust diskslots to your encoders? Output the counter values instead of the calculated rpm to find out more.

The difference in your values is because you are not actually using the encoders to calculate RPM. I think rite now, in your code you are using timer interrupts and in its function you are just incrementing a simple variable. And finally displaying that variable on serial terminal. Can you point out the code where you are getting values from LM393?

@jackthom41: Please stop confusing people by unfounded guesses. Can you point out which timer interrupt you found in the code?

The only problem is the discrepancy between expected and calculated rpm, most probably caused by a wrong encoder slot count.

elec_robokids:
this code works

I seriously doubt that. It's not complete, and won't even compile in the form posted. Both ISR functions are missing (you do give them in a separate snippet), variable declarations are missing.

Hi
the problem is more bigger than the code, and it's well explained in this post :

in fact, i realized that the RPM calculated with this FC03 optical sensor = 4 * Real RPM
and it's due to the fact of signal bouncing, i'll try the solution described here and i'll come back to you

Many thanks

Bouncing never results in a constant factor of 4.

hi DrDiettrich,
it's not really a static coeficient 4, but sometimes 3 or event 2, sometimes 6, 7. but in general, and only a human constation i think 4 iis verry close.
this is the FC03 Encoder :

Then your sensor or wiring is inadequate. Why don't you use ordinary fork light barriers as available for rotary encoders? Comparators are sensible to all sort of pulses on cables or (ambient) power supplies.

Review this thread for several simple hardware fixes for multiple interrupt problems with the FC03.

Thanks for the link :slight_smile:

I'd suggest to use the A0 output, that works without that error prone LM393.

I will use Analog Output Pin, but i shall insert it in PIN2 in arduino board ?
and it's the same code ?

I will use Analog Output Pin, but i shall insert it in PIN2 in arduino board ?
and it's the same code ?

Try it. Putting a varying 0-5v signal to a digital pin certainly won't damage the Aduino.

Analog output from the FC03 to an interrupt pins has worked for some people. Success will likely depend on the intensity of the IR, the rise and fall time of the signals, and the smoothness of the signal.

The digital inputs have some hysteresis, so the rise and fall times should not matter and bouncing should not occur.

Dears,
this is the output of all my findings about the proposed solution of this FC-03 IR speed sensor
there is 4 solutions proposed :

use Analog pin instead of digital pin
use 3.3 Volt output instead of 5V
use a software correction
use a hardware correction (a 100microF capacitor between PIN sensor (digitalO and GND)

this is the software debouncing correction on ISR function

void ISR_countRight()
{
if (digitalRead (MOTORRight) && (micros() - debouncel > 500) && digitalRead (MOTORRight) ) {

debouncer = micros();
counter2++;
}
else ;

}

#include "TimerOne.h"

const byte MOTORLeft = 2;  // Motor 1 Interrupt Pin - INT 0
const byte MOTORRight = 3;  // Motor 2 Interrupt Pin - INT 1
volatile byte counter1 = 0;
volatile byte counter2 = 0;
static volatile unsigned long debouncel = 0;
static volatile unsigned long debouncer = 0;

// Float for number of slots in encoder disk
float diskslots = 20;  // Change to match value of encoder disk

// ------------Others declaration ------------------

void setup()
{
  pinMode(MOTORLeft, INPUT);
  pinMode(MOTORRight, INPUT);
  pinMode(LM_AV, OUTPUT);
  pinMode(LM_AR, OUTPUT);
  pinMode(LM_EN, OUTPUT);
  pinMode(RM_AV, OUTPUT);
  pinMode(RM_AR, OUTPUT);
  pinMode(RM_EN, OUTPUT);

  Serial.begin(9600);
  Timer1.initialize(1000000); // set timer for 1sec
  attachInterrupt(digitalPinToInterrupt (MOTORLeft), ISR_countLeft, RISING);  // Increase counter 1 when speed sensor pin goes High
  attachInterrupt(digitalPinToInterrupt (MOTORRight), ISR_countRight, RISING);  // Increase counter 2 when speed sensor pin goes High
  Timer1.attachInterrupt( ISR_timerone ); // Enable the timer
}


void loop()
{
  moveForward(255, 255);
 }


void moveForwardMotorLeft(int speed)
{
  digitalWrite(LM_AV, HIGH);
  digitalWrite(LM_AR, LOW);
  analogWrite(LM_EN, speed);
}

void moveForwardMotorRight(int speed)
{
  digitalWrite(RM_AV, HIGH);
  digitalWrite(RM_AR, LOW);
  analogWrite(RM_EN, speed);
}

void moveForward(int speedL, int speedR)
{
  moveForwardMotorLeft(speedL);
  moveForwardMotorRight(speedR);
}

// Interrupt Service Routines

// Motor 1 pulse count ISR
void ISR_countLeft()
{
    if (digitalRead (MOTORLeft) && (micros() - debouncel > 500) && digitalRead (MOTORLeft) ) {
    // Check again that the encoder sends a good signal and then check that the time is greater than 500 microseconds and check again that the signal is correct.
    debouncel = micros(); // Store the time to check that we do not count the rebound in the signal.
    counter1++;
  }  // Add the good pulse that enters.
  else ;


}

// Motor 2 pulse count ISR
void ISR_countRight()
{   
  if (digitalRead (MOTORRight) && (micros() - debouncel > 500) && digitalRead (MOTORRight) ) {
    
    debouncer = micros(); 
    counter2++;
  }  
  else ;
  
 
}

// TimerOne ISR
void ISR_timerone()
{
  Timer1.detachInterrupt();  // Stop the timer
  Serial.print("Motor Left Speed 1: ");
  float rotationLEFT = (counter1 / diskslots) * 60.00;  // calculate RPM for Motor Left
  Serial.print(rotationLEFT);
  Serial.print(" RPM - ");
  counter1 = 0;  //  reset counter to zero
  Serial.print("Motor Right Speed 2: ");
  float rotationRIGHT = (counter2 / diskslots) * 60.00;  // calculate RPM for Motor Right
  Serial.print(rotationRIGHT);
  Serial.println(" RPM");
  counter2 = 0;  //  reset counter to zero
  Timer1.attachInterrupt( ISR_timerone );  // Enable the timer
}

i calculated manually the RPM of my motors, and it's about 216 RPM for the left and 198 for the right Motor

i started to try the first three solution

and this is the combination of all my result :

Analog pin and softwarecorrection and VCC=5V
Motor Left Speed 1: 228.00 RPM - Motor Right Speed 2: 186.00 RPM
Motor Left Speed 1: 228.00 RPM - Motor Right Speed 2: 192.00 RPM
Motor Left Speed 1: 225.00 RPM - Motor Right Speed 2: 198.00 RPM

Analog pin and softwarecorrection and VCC=3.3V
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 195.00 RPM
Motor Left Speed 1: 225.00 RPM - Motor Right Speed 2: 198.00 RPM
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 198.00 RPM

Analog Pin without SoftWare Correction and VCC=3.3V
Motor Left Speed 1: 249.00 RPM - Motor Right Speed 2: 204.00 RPM
Motor Left Speed 1: 258.00 RPM - Motor Right Speed 2: 204.00 RPM
Motor Left Speed 1: 270.00 RPM - Motor Right Speed 2: 201.00 RPM

Analog Pin without SoftWare Correction and VCC=5V
Motor Left Speed 1: 222.00 RPM - Motor Right Speed 2: 192.00 RPM
Motor Left Speed 1: 213.00 RPM - Motor Right Speed 2: 192.00 RPM
Motor Left Speed 1: 210.00 RPM - Motor Right Speed 2: 192.00 RPM


Digital Pin without SoftWare Correction with VCC = 5V
Motor Left Speed 1: 633.00 RPM - Motor Right Speed 2: 588.00 RPM
Motor Left Speed 1: 642.00 RPM - Motor Right Speed 2: 594.00 RPM
Motor Left Speed 1: 648.00 RPM - Motor Right Speed 2: 600.00 RPM

Digital Pin without Software correction with VCC=3.3V
Motor Left Speed 1: 273.00 RPM - Motor Right Speed 2: 156.00 RPM
Motor Left Speed 1: 300.00 RPM - Motor Right Speed 2: 171.00 RPM
Motor Left Speed 1: 261.00 RPM - Motor Right Speed 2: 177.00 RPM

Digital Pin with Software  and VCC =5V
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 378.00 RPM
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 384.00 RPM
Motor Left Speed 1: 213.00 RPM - Motor Right Speed 2: 387.00 RPM


Digital PIN with Software and VCC = 3.3V
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 531.00 RPM
Motor Left Speed 1: 213.00 RPM - Motor Right Speed 2: 555.00 RPM
Motor Left Speed 1: 219.00 RPM - Motor Right Speed 2: 552.00 RPM

it's clear that the closest combination to the real is

Analog pin and softwarecorrection and VCC=3.3V
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 195.00 RPM
Motor Left Speed 1: 225.00 RPM - Motor Right Speed 2: 198.00 RPM
Motor Left Speed 1: 216.00 RPM - Motor Right Speed 2: 198.00 RPM
Or
Analog Pin without SoftWare Correction and VCC=5V
Motor Left Speed 1: 222.00 RPM - Motor Right Speed 2: 192.00 RPM
Motor Left Speed 1: 213.00 RPM - Motor Right Speed 2: 192.00 RPM
Motor Left Speed 1: 210.00 RPM - Motor Right Speed 2: 192.00 RPM

So with this configuration (Arduino Uno, FC-03 IR Speed Sensor) we should use the Analog Pin instead of digital one.
and to be more effecient, we can choose also the softWare correction

The Hardware correction also should work

many thanks for you all.

An optical encoder does not need any debouncing, as there's nothing that can bounce to begin with.

And I don't understand your digital/analog pin thing. Where and how does that come in play?

The analog output AO comes directly from the optocoupler. This signal also is conditioned by a comparator, which seems to oscillate on every level change, output to the digital output DO.