Interrupt woes - double pulses, weird timings and more

Hi Everyone,

Bit of project background first. I have built most of a digital dashboard for my Ural Motorcycle. It's old tech and has noisy electrics so I have electrically isolated my Arduino and sensors from the bike's electrical system and ground. I power the Arduino on the bike via a powerbank, ergo smooth DC supply voltage.

Initially for bike speed I set about using two magnets on opposite sides of the drive shaft, but I got garbage results no matter how much I tried to debounce. I understood that as a magnet approaches, the Hall Effect module will see a rising signal, but I imagined that using RISING to trigger the interrupt would sort that. No dice, so I gave up on that and moved to GPS for the speed.

The ignition module on my bike lights an LED at top dead centre so I figured that would be ideal for using a light sensor (digital output) to opto-isolate the Arduino from the bike - and also because LEDs do not have significant ramp up time vs incandescant bulbs.

Placed the module inside the ignition housing and again got garbage results. My next step would be to use a fibre optic (from HiFi) 'cable' to transmit the LED to the Arduino location, far away from spurious interference etc. But first I decided to do some bench tests.

To prove to myself that interrupts work, I first set up one Arduino with the following script and I connected output pin 7 it to the input pin 2 of my main Arduino. This was to use one Arduino to generate a nice clean square wave to feed the other.

//Simple pulse generator for 20Hz
void setup() {
  // put your setup code here, to run once:
pinMode(7, OUTPUT);
pinMode(13, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(7, HIGH);
  digitalWrite(13, HIGH);
  delay(25);
  digitalWrite(7,LOW);
  digitalWrite(13, LOW);
  delay(25);
}

On the main Arduino I run this script.

volatile byte counter = 0;
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = 0;
long CurrentMillis = 0;

void Interrupt_Function()
 {
 // If interrupts come faster than X ms, assume it's a bounce and ignore 
 interrupt_time = millis();
 if (interrupt_time - last_interrupt_time > 10) 
 {
    counter++;
 }
 last_interrupt_time = interrupt_time;     
 }

/*
 void Interrupt_Function()
 {
 counter++;
 }*/
 
void setup()
  {
  Serial.begin(9600);
  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), Interrupt_Function, HIGH);  
  }
 
void loop()
  {
    if ( millis() > (CurrentMillis + 999))
      {
      noInterrupts();
      CurrentMillis = millis();
      Serial.print("Pulses = ");
      Serial.println(counter);
      interrupts();
      counter = 0;
      }  
  }

As you can see I have two available interrupt functions, one with debounce.

Results:

Setting the non debounced interrupt function to RISING or FALLING works perfectly and I get a constant 20 pulses per second on the Print output.

If I set LOW or HIGH I get a constant 40 pulses per second unless I debounce, but then I'm almost restricting the counted pulses by whatever figure I choose for debouncing.

Since I had a very nice constant 20 pulses on this I then attached a light sensor. When held directly over the LED (pin 13) on the signal generating Arduino, I get a constant 40 pulses per second with RISING, FALLING and HIGH (OK this is double the expected pulses, but it's exactly double so I could use it potentially).
If I select LOW, something kooky happens and the reporting interval gets screwy and instead of every second becomes every three seconds or so.

This is all confusingly in contradiction to Arduino's notes on which interrupt triggers are allowed on each board.

Apologies for the lengthy post but I hope all the info is there. The question being really, what the heck is going on.

not sure that it's a debounce problem or a noisy signal

i faced something similar with long wires to an optical encoder near a motor. I fixed things in SW by ignoring interrupts that appeared too soon.

but i believe the real fix is to use a hysterisis circuit. I believe using trigger/threshold inputs to an NE555 would be well suited. they switch the output at 1/3 and 2/3 Vcc

How fast are the changes you are trying to read? I would not think an interrupt was the correct way to read the output from a sensor monitoring a motorcycle drive shaft. Interrupts are for things that happen fast and need to be processed in a very short time frame. By 'fast' I mean in micro-controller terms, not in human or even motor cycle terms. I also don't think you need 2 magnets, unless one was for balance. 2 just doubles the number of pulses you need to cope with for little or no gain.

If this were my project I'd be polling the output, not using interrupts. You also need to get rid of the delays so the code has chance to do some work instead of twiddling its thumbs.

I would expect the output of a Hall effect switch to be clean, with no bounce, but an oscilloscope will tell you that.

PerryBebbington:
I would not think an interrupt was the correct way to read the output from a sensor monitoring a motorcycle drive shaft. Interrupts are for things that happen fast and need to be processed in a very short time frame.

A lot will depend on how long the sensor remains in the triggered state - in other words if a first attempt at polling is just a tiny bit too early will the signal still be available for the next round of polling.

I think I would use an interrupt.

I agree that two magnets may over complicate things for little or no gain.

...R

Jetmoo,

I have a lot of respect for Robin's opinion and we are disagreeing, which doesn't help you. I stand by my comment but you need to experiment.

Try feeding the output of your Hall effect sensor on your drive shaft into this and see if the count increments smoothly. Code compiles, whether it works is something I cannot test as I don't have a spare motorcycle.

uint8_t speedPin = 3;
uint32_t count;

void setup() {
  Serial.begin(9600);
  pinMode(speedPin, INPUT_PULLUP);
}

void loop() {
  if (measureSpeed()) {
    Serial.print (count);
  }
}

bool measureSpeed() {
  static bool lastState;
  bool countUpdated = 0;
  bool pinState = digitalRead(speedPin);
  if (pinState != lastState) {
    lastState = pinState;
    ++count;
    countUpdated = 1;
  }
  return countUpdated;
}

Referring to the code int the Original Post, I think this will cause a problem.

     noInterrupts();
      CurrentMillis = millis();
      Serial.print("Pulses = ");
      Serial.println(counter);
      interrupts();
      counter = 0;

Interrupts need to be enabled for printing. The correct way to handle this is to take a copy of the counter variable like this

     noInterrupts();
         copyOfCounter = counter;
         counter = 0;
      interrupts();
      CurrentMillis = millis();
      Serial.print("Pulses = ");
      Serial.println(copyOfCounter);

Separately, my preference is to use the interrupt to record when it happens and use the time difference between interrupts to determine the speed. Something like

void interruptFunction() {
    pulseTime = micros();
    newPulse = true;
}

void loop() {
   if (newPulse == true) {
      newPulse = false;
      previousPulseTime = latestPulseTime;
      noInterrupts();
         latestPulseTime = pulseTime;
      interrupts();
      timeForOnePulse = latestPulseTime - previousPulseTime;
   }
}

Note also that variables for millis() and micros() must be unsigned long

...R

What Arduino are you using?

I would stay away from LOW or HIGH as a mode choice, On the AT328 Arduinos HIGH is the same as CHANGE, and LOW will produce multiple interrupts.

Use either RISING, FALLING, or CHANGE.

Since I had a very nice constant 20 pulses on this I then attached a light sensor. When held directly over the LED (pin 13) on the signal generating Arduino, I get a constant 40 pulses per second with RISING, FALLING and HIGH (OK this is double the expected pulses, but it's exactly double so I could use it potentially).

This is not as expected you should see 20 for the RISING and FALLING and 40 for the CHANGE or HIGH.

Can you provide some detail on the light sensor? Is it a digital or analog output? Is there a pullup or pull down?

My goodness, folk, what a lot of information and wealth of knowledge to go on. Thank you so much.

@gcjr, I will look into the hysteresis circuit. Unfortunately due to lockdown here in Romania getting components is a no-no at the moment.

@PerryBebbington - for the drive shaft measurement I was looking at around 90 pulses per second at top speed of 60mph/100kmh. For the engine RPM I guess anything up to 7000 would cover the realistic capabilities of the engine - it never actually had a tacho so I've no idea.
I used two magnets to improve the low speed accuracy a bit, mainly as an experiment, but I can use software to round down below say, 5mph, as I'm not exactly liable to be in any speed trap trouble. Of course, counting the pulses though is essential for updating the Odometer on the bike.

@Robin2 the magnets I'm using are self adhesive 10mm thin neodymium types so they do trigger a very quick pulse. I agree that a single curved flat magnet around half the drive shaft would be better - the optical equivalent of painting one half white I guess. Sidenote, I could possibly move to an optical system - aluminium tape to provide a reflective surface - it's not so much the drive shaft I'm using but a 3 inch diameter coupling between gearbox and drive shaft. The only issue would be dirt hampering the signal.

@Robin2 post 2 - thanks for the code. Very interesting about the interrupts and print - I had no idea.
As for the alternative method of recording time between pulses vs pulses, I did consider this but couldn't really come up with a solution for when I'm sitting at traffic lights for a couple of minutes and the micros got really big.
I guess in the loop I could have if(lastinterval>10000000){ lastinterval = 0; }
to keep the lastinterval low until a genuine reading comes in. Then in the code just if lastinterval is 0 then speed is zero.

@cattledog I have choices. The testbed at the moment is a genuine Uno R3. My failed attempts with the speed and rpm on the bike were with a Mega2560. I favour the mega but obviously its size makes mobile electronics more challenging to keep things neat.

As for your light sensor question, I have two kinds. One three pins digital only and the other four pins with choice of D or A. Both have pots on to adjust sensitivity. The three pin type is, I think, Elegoo.

Thanks again,

jetmoo:
I guess in the loop I could have if(lastinterval>10000000){ lastinterval = 0; }
to keep the lastinterval low until a genuine reading comes in. Then in the code just if lastinterval is 0 then speed is zero

That's what I did when controlling a small motor. Pick the big number as a bit more than the largest value that can arise in normal motion.

...R

For the drive shaft measurement I was looking at around 90 pulses per second at top speed of 60mph/100kmh.

Are you sure? I did some rough calculations. I don't know the wheel size of a Ural motorcycle so I took a guess at 20".
A 20" diameter wheel has a circumference of about 63"
1 mile is 5280', or 63360"
1 mile therefore requires approximately 1000 revolutions of the wheel.
At 60 miles per hour the wheel will rotate 60000 revolutions in 1 hour.
As there are 3600 seconds in an hour the wheel will rotate 60000/3600 = ~17RPM
So at the wheel you would get about 17 pulses per second at 60MPH.

90 pulses per second is 5 times as many, is the gearing between the drive shaft you are measuring and the wheel about 5:1?

If it is, then 90 pulses per second is doable with polling, that's one pulse every 11ms. You would need to get rid of the delays and write code carefully, avoiding all blocking.

Hi,

It's old tech and has noisy electrics so I have electrically isolated my Arduino and sensors from the bike's electrical system and ground. I power the Arduino on the bike via a powerbank, ergo smooth DC supply voltage.

HOW?

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Then we will know how you are inputting to the controller.

Thanks.. Tom.. :slight_smile:

PerryBebbington:
Are you sure? I did some rough calculations. I don't know the wheel size of a Ural motorcycle so I took a guess at 20".
A 20" diameter wheel has a circumference of about 63"
1 mile is 5280', or 63360"
1 mile therefore requires approximately 1000 revolutions of the wheel.
At 60 miles per hour the wheel will rotate 60000 revolutions in 1 hour.

Your maths is correct, however there is a 4.96 (I think) final drive gearbox between wheel and the drive shaft so I'm multiplying the wheel revolutions by this gear ratio - suppose I could have mentioned it :slight_smile:

Your maths is correct, however there is a 4.96 (I think) final drive gearbox between wheel and the drive shaft

:slight_smile:
I didn't expect to be THAT close to the actual gear ratio based on a guess at the wheel diameter :slight_smile:

As for your light sensor question, I have two kinds. One three pins digital only and the other four pins with choice of D or A. Both have pots on to adjust sensitivity. The three pin type is, I think, Elegoo.

Many of the low cost hobby sensors have comparators based on the LM393 without any hysteresis feedback. Performance is often improved with a cap between D0 and ground. That solution and other possible circuit mods for hysteresis are discussed here
https://forum.arduino.cc/index.php?topic=342650.0

You have to consider pulse duration as well as period. If it is very short, you could miss it with polling even if the period between pulses is very long. An interrupt will always catch a pulse as long as it meets the very short minimum hold time for an input interrupt.

@cattedog - that's great info, thank you so much!

@aarg - also fab to know. I might consider the optical IR route - to be honest I'll not be going anywhere muddy with the bike. There is also the original speedo take off which is a slotted shaft just before the drive shaft. With a bit of jiggery-pokery I could possibly make an enclosed disc with on of the beam break type optical sensors. I have the Elegoo 36 sensor starter kit which was a great investment (xmas gift)as it gives me a ton of sensors to consider.

So the plot thickens slightly.

I wrapped a piece of white tape around the chuck of my cordless drill, 50% white, 50% black. The optical sensor read 32 pulses per second at full speed, or 1920rpm (the drill is rated for 2000 rpm no load so that's near enough).

Then I attached a 5mm diameter magnet and tried with a hall sensor. Unbelieveably, the exact same reading of 32 pulse per second at top speed.

So I would conclude that my interrupts, sensors and software are working just fine. I am going to hypothesise that the larger 12mm magnet I put on the bike, in conjunction with its often very slow speed passing the sensor caused some false readings. Next step is to try the smaller magnet on the bike and/or the optical method.

It could also be interference from the old and dirty ignition, albeit some distance away and I did use ferrites to help. I mean I guess it could also be the Arduino on the bike has sprung a fault. That's something else to test.

Thanks for all your help thus far and I will continue and report back when I have findings to make the topic more helpful for others who might chance across it.

Jetmoo,

Very much appreciate the report. It's frustrating to offer advice and then not get updates, it's rewarding to get progress reports.

Thanks.

Hi,
What sort of wiring and how long is the wiring between the Arduino and the sensor?
Is the wiring shielded?
Have you tried connecting the Arduino system gnd to the bike gnd.

Can you post a circuit diagram of your project please?

Thanks.. Tom.. :slight_smile:

@TomGeorge not far at all between Arduino and the Hall sensor - but I literally can't do a wiring diagram as I kind of cobbled it together as I went along.

Had a set back earlier as I think I have busted my LCD screen, the installation of which is a nightmare. It's not the cost, it's the time. I was foolish and connected my hall effect cross polarized and I think it has done in the screen - the backlight works but I'm getting no image - I'll oscilloscope it tomorrow to make sure the pins are sending a signal as expected.