FC-03 and attachInterrupt() problems!?!?

Hi everybody,
after playng around with some stepper motors and a DIY car chassis, I decided to buy a 2WD Car Kit (yep, chinese ones..) to let me "play" with "moving things". I previously used a pair of stepper motors, they were easy to control but too slow, so I switched to such Car Kit that includes two DC motors and two wheel discs. I also bought an L298N motor driver, and two FC-03 IR speed sensors like this:

I planned to use them with attachInterrupt() to count the wheel "holes" (20 per turn) so to determine the distance covered and relative speed. The problem is neither the Car Kit nor the driver, it's the FC-03: it seems to have an unexpected (for me) behavior.
To understand its usage, I found this schematics of the sensor:

The four pins are labeled VCC, GND, DO, AO, so everything was pretty clear to me. I connected DO (Digital Output) to pin 2 (interrupt 0), and obviously VCC to +5V and GND to Arduino GND, and wrote this test sketch:

#define LED1 13
#define M1_SENS 2
#define M1_INT 0

volatile int count1 = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  pinMode(LED1, OUTPUT);
  pinMode(M1_SENS, INPUT);
  attachInterrupt(M1_INT, motorStep1, RISING);
}  

void loop() {  
  if ( count1 >= 20 ) {
    digitalWrite(LED1, !digitalRead(LED1));
    count1 = 0;
  }
}

void motorStep1()
{
  count1++;
  Serial.println(count1);
}

So, if I use a piece of black plastic to block IR light from the LED and the phototransistor (as an "on" event), the serial output shows me TWO events of the ISR , and TWO when I remove it (as an "off" event), e.g. 1 and 2 when I put the plastic, then 3 and 4 whan I remove it). The same if I install it over the encoding disc and move the wheel by hand, a full 360° returns a count of 80, instead of 20!!

First of all, if I set attachInterrupt() with RISING option, why on earth I have interrupts on both "on" AND "off"? I expected to have it on "off" only!
Secondly, that "double interrupt" makes me crazy. Why TWO consecutive events?
Thirdly, if I change RISING into FALLING, the output is exactly the same!!

So, why this behavior? It's not the speed of events, I used both the wheel and making single events by hand, same result. It's not the "Serial.print()" contained in the ISR, I moved to loop() and nothing changed (except it "missed" some counts, like 1 2 3 5 6 7 9..., probably due to lower response of loop() compared to interrupts.

So I replaced the FC-03 with a simple circuit, two resistors, one IR LED (TSUS4300) and an IR phototransistor (TEFT4300), and it works like a charm even if output should be analog (but it falls into digital signals range)!!! Same connections, same sketch, and one event per each "off" event, no double interrupts, no events on "on"!

You'd ask me "what's the problem? Use your components instead of FC-03!". Well, placing my sensors is critical as they should be perfectly aligned, so I'd like to use the FC-03s.

After such test, I tried to use AO (Analog Output) pin instead of DO: same Arduino connection, same sketch, but it WORKS!

So, I think I can now use AO and start working on the car, but I wonder why that DO behavior, if it depends on something wrong in my usage of the sensor, in attachInterrupt(), the schematics is wrong, or the sensors are defective (thus I'll send them back)!!!

What do you think?

docdoc:

void motorStep1()

{
 count1++;
 Serial.println(count1);
}



What do you think?

Remove the Serial.println() from your ISR.

Change your code to something like this

volatile bool updated=false;

void motorStep1(){
count1++;
updated = true;
}

void loop(){
if(updated){ /*
* keep the interrupt off blocks as short as possible to minimize the chance
* of missing an interrupt.
*/

  noInterrupts(); // since count1 is a multiple byte value, must access atomically
  uint16_t tempCount = count1;
  interrupts();

/* Serial print takes a while to convert the value to a displayable character stream
* also, if the Serial tx Buffer is full, the call will hang until there is room in the buffer
* for more characters.
*/

  Serial.print(tempCount,DEC);
  Serial.print(' ');

  noInterrupts();  /* if count1 has not been changed, clear the updated flag
* else leave updated set to cause a new count to be printed next time around
* the loop()
*/

  updated = tempCount != count1;
  interrupts();
  }
}

try this code. Unless you have an Oscilloscope to monitor the Optical interrupter signal, we don't know how dirty the trigger signal is. The act of turning on and driving the LED on D0 could cause the VCC on the module to sag, this would change the divider voltage at the LM393 comparator (-) input. This could cause a multiple trigger event.

Your smooth manual insertion of the flag is probably not smooth on the millisecond, let alone the microsecond time scale the processor reacts on.

Real life is a dirty, smelly, imprecise World. depending on how dirty the actual signal is, you might need to de-bounce this interrupt.

static volatile unsigned long debounce=0;

void motorStep1(){
if(micros()-debounce>500){ //500 microseconds since last interrupt
  debounce = micros();
  count1++;
  updated=true;
  }
else ;/* Way too fast since last interrupt, ignore it
* with a 20 slit disk, and the wheel rotating fast enough to generate interrupts
* at a 500 microsecond intervals, the wheel would have to rotate at 6000 rpm
* = 1/(0.000500sec * 20(slits/rotation)) * 60(seconds/minute)
*/
}

Chuck.

Thanks Chuck, I'll give your interesting remarks a try tomorrow (now it's 1:30 AM here, I'm going to sleep...), I hope it'll solve my doubts, btw I'm asking myself if anyone else experienced the same problems with that encoder...

See ya soon, and thank you!

chucktodd:
Remove the Serial.println() from your ISR.
Change your code to something like this
...omissis...
Chuck.

Unfortunately, no way. With first ISR (no debounce) I read even numbers over serial display (e.g. 2 4 6 8 10 12 and so on...), two for each event. The debounce change works, meaning I have single counts for each event, but I still have both events counted (1 for "on", 2 for "off", and so on), thus still no difference between RISING or FALLING interrupt trigger.
If I use AO instead of DO, everything is ok.

I can't explain why I have such DO behavior.

Did anyone with the same sensor/encoder have the same problems?

docdoc:
Unfortunately, no way. With first ISR (no debounce) I read even numbers over serial display (e.g. 2 4 6 8 10 12 and so on...), two for each event. The debounce change works, meaning I have single counts for each event, but I still have both events counted (1 for "on", 2 for "off", and so on), thus still no difference between RISING or FALLING interrupt trigger.
If I use AO instead of DO, everything is ok.

I can't explain why I have such DO behavior.

Did anyone with the same sensor/encoder have the same problems?

I still think the problem is related to the output of the LM393 bouncing. The hardware of the AVR won't react as you describe, it is seeing 2 falling events for each interrupt cycle.
As a hardware fix, add a 0.1uf cap between GND and the negative input of the comparator. This will stabilize it's trigger point.

The reason using A0 works correctly is that the AVR processor has a large hysteresis between LOW and HIGH.

A High level is any voltage above 60% of VCC, a LOW level is anything below 30% of VCC. So, there is a 1.5v range through which the processor will ignore any bounce.
But the LM393 has a much lower hysteresis level Specified as a maximum of 0.003V.

This sensitivity difference explains your results.

Chuck.

chucktodd:
I still think the problem is related to the output of the LM393 bouncing.

Thank you again for your interest in my problem, Chuck!

I agree with your evaluation about the signal bouncing due to hysteresis (the first problem), and your debouncing code work like a charm as a workaround! But I still can't understand why I have ISR triggered on both RISING and FALLING.

I think I can confirm you the opAmp is a Texas LM393 12N, it looks like opAmp output (sorry, I don't have an oscilloscope/signal analyzer) is something like this:

It should be the reason for both problems, double events (voided if I use debounce code) and RISING/FALLING (still alive), Just to collect more infos (I suppose you don't have an FC-03 to test with...) I connected DO to Arduino pin A0 and wrote a small test sketch, just to see what happens within 5 seconds of continuous analogRead():

#define M1_SENS A0

unsigned long endTime;

void setup() {
  Serial.begin(9600);
  Serial.println("Start");
  endTime = millis() + 5000;  // 5 seconds record
}  

void loop() {
  if (millis() <= endTime)
    Serial.println(analogRead(M1_SENS));
}

The result surprised me, I read small values (around 28-29) when the sensor is open (light passing thru) and then 999-1000 when it is closed, as expected, with no signs of either any "bounce" or some "double up/down" values!!

If I connect FC-03 analog AO pin instead of DO, results are almost identical, around 40-41 on open, 998-1003 on close! The only remarkable difference is a couple of "intermediate" reading before higher level is reached (e.g. 40 40 41 40 172 986 999 1001 1000 ...) obviously due to relatively low speed of my "manual" light beam open/close.
So, apparently no "bounces" on signal (I hope "loop()" cycle is fast enough to capture them), but the main question is still open: why "DO" triggers both on RISING and FALLING, despite the attachInterrupt() setting?

Hello,
Did you reach any solution? I am currently having exactly the same problem with this component. Thank you in advance!

http://www.ebay.es/itm/221863772990?_trksid=p2057872.m2749.l2649&ssPageName=STRK%3AMEBIDX%3AIT

Cheers,
Alberto

Try a hardware RC filter where R=1K, C=0.01µF (100nF).

1 Like

Thank you for your answer! I will try this solution next Monday!

In the scheme you posted, "RPM" would be the Digital Output of the FC-03 module and "Digital Input" the Arduino-Pin... is this correct?

Would you recommend to use the Digital or the Analog Output from the FC-03?

In case anyone already found a reliable and tested solution for this concrete problem... I am still glad to hear any other answer :slight_smile:

albertoing:
Did you reach any solution? I am currently having exactly the same problem with this component.

Alberto, the only practical solution for me was to use AO instead of DO. It works, but I still wonder why on earth I can't use D0...
And I'm not sure a low-pass filter could solve this issue on DO, at least it'd require a triggering/hysteresis circuit to re-convert resulting signal back to digital levels.

@doc doc or @albertoing

There have been several recent threads with folks having multiple interrupts/noise troubles with these basic lm393 comparator modules, so figuring this out may help others.

You now have three good suggestions to make the module behave:

  1. debounce the interrupt routine
  2. stabilize the trigger point with 0.1uf cap between GND and the negative input of the comparator.
  3. add a low pass filter to the output.

It you try all three, do you only see one interrupt using the digital output?

If this does not get you success, you may want to consider adding a feedback resistor from DO to the positive input of the lm393 to add hysteresis to the comparator. I'm not certain what the value should be, but one of the hardware experts might review the circuit diagram and make a suggestion.

Hi.
I had the same problem. I add a low pass filter (only a Capacitor =0.01µF (100nF)) to the output DO and the problem is solved.

The problem with the sensor's comparator circuit is that there's no feedback resistor (as suggested by cattledog) for hysteresis (poor design). I would think something like this would work, but don't have one of these sensors to test or see if its practical to install the 100K resistor:

1 Like

With Arduino UNO (ORIGINAL), I tested the FC-03 encoder with VCC pin to +3,3V. This solution works very well.

dlloyd:
The problem with the sensor's comparator circuit is that there's no feedback resistor (as suggested by cattledog) for hysteresis (poor design). I would think something like this would work, but don't have one of these sensors to test or see if its practical to install the 100K resistor:

I tested this option with a 100k resistor and it does not work well.

The best solution is with a capacitor in between the D0 pin to the GND pin:

I'm not sure if I'm facing the same problem as you did, the problem i faced is the FC 03 has slow response with blocking time < 1ms , the output D0 will be much shorter respect to the real blocking time proportion to the period , and shorter blocking time will even no D0 response , after looking at the wiring diagram of FC 03 , i shredded the capacitor linked to the op-amp + input , (put the module with pins on the top , and the capacitor at right hand side one is the target to crack) , then the response become fast , and with the oscilloscope the sweet square wave shows, no bad side effect till now.

but this is irreversible , if you're not sure, desolder the capacitance instead of cracking it.

SOLVED:

I was having the exact same issue as docdoc (original author).
I am using the lm393 as seen in this video: How to use a LM393 IR Speed sensor with an Arduino - Tutorial - YouTube

Thanks to this thread I was able to solve the issue with dlloyd's fix -

"... a hardware RC filter where R=1K, C=0.01µF (100nF). "

I placed a 1k Resistor from the Digital output of the lm393 to the Arduino. From the resistor I attached the 0.01µF ceramic capacitor (104) and the wire to the Arduino interrupt pin. The other lead of the Capacitor goes to ground.

All this is seen in dlloyd's image of the RC Filter.

Thanks.

a 0.1uf cap between GND and the negative input of the comparator. This method is work. Thank You chucktodd!!!

androminarobot:
I tested this option with a 100k resistor and it does not work well.

There is a missing resistor at the op-amp positive input terminal in your design.

My simulation shows that as long this input resistor is half the resistance of the feedback resistor, the circuit should work perfectly.

I used input resistor of 4.7kOhm, and feedback resistor of 10kOhm.
I have counter checked this using hardware and it is proven working fine.

I will provide scope capture later for references.

Rs.jpg