Motorcycle tacho using neon

Hi

I'm attempting to fit a rpm gauge to a motorbike that has none. No real reason apart from the fun/challenge of it!

Tried various things, the only one to yield results so far, after reading an old thread here, is taping a small neon bulb to my motorbike plug lead along with a photodiode/LM393 module from ebay wrapped in black tape as some crude optocoupler. The neon appears to flash with every plug firing, I've no way to verify this. This is captured by the photodiode/LM393 module, I'm feeding whatever signal comes out of that into a Schmitt trigger to clean up the pulses. This is then captured on interrupt on an Uno, this outputs to a 16*2 lcd on my bike.

Works well, idle appears to be 700rpm, I have no way to measure this but from the sound and 'feel', seems right.

Now the rub. Riding along, all good up to approx 2500/3000 rpm then the reading halves. I can't explain it, that's what appears to happen. So 3000 rpm shows for a second then drops to 1500 with no change in rpm.

Any thoughts? I am wondering if the neon is somehow saturating the photodiode? I have no other electronics in the circuit, I am wondering if a zener somewhere might keep things in order, any ideas welcomed!

Thanks

Hi,
Have you tried to adjust the threshold control on the LM393 opto module?
The other thing to try would be to put something between the neon and the opto to just attenuate the light transmission, that is if its a saturation problem.

Also try this.

In a past life I used to repair automotive service equipment, including timing lights, they used an inductive pickup on the No1 lead.
You could use a toroid that will let you thread the lead through it, and wind about 5 turns on the ring to obtain your signal.

Tom.... :slight_smile:

Google inductive timing light pickup

Sure have, adjusted it at idle until a reading appeared.

I figure that this is the minimum rpm, minimum brightness of neon. From then more rpm, more bright and more signal. Perhaps too much tho. Can't think how to attenuate just at higher rpm.

Ive tried some circuits using capacitance and inductance, this is the only success so far. Good link, haven't seen that, will read and digest.

Thanks for the reply

p359:
This is then captured on interrupt on an Uno, this outputs to a 16*2 lcd on my bike.

Wrong programming decision.

Writing to LCD is a "slow action" which can take up to several milliseconds.

So the "interrupt duration" is too long and every second interrupt is missing while writing to LCD, while things are getting faster and faster.

You never need to update your LCD 50 times or more per second.

Do it like that:

  • within the interrupt routine: count a variable up
  • within the loop function: calculate and show RPM, perhaps once per second

You need accurate synchronization of variables used both, within interrupt and within loop code.

If you don't know about interrupt-safe programming: Show your code!

And in general, there is no value to using an interrupt for this task.

As explained above, updating your LCD, or attempting to do so 50 times a second is ridiculous. At the very most ten would be useful (if you include a bar graph), five probably optimum (but even then it may well blur).

Polling millis() in a loop to determine when it has advanced by 200, you poll your input in the same loop to count flashes, then update the display accordingly and repeat the process starting by taking your baseline reading of millis().

Code:

/*
lcd  via i2c/SPI LCD backpack using MCP23008 I2C expander

For LCD
5V to Arduino 5V pin
GND to Arduino GND pin
CLK to Analog #5
DAT to Analog #4

*/

#include <Wire.h>
#include <SPI.h>
#include <LiquidTWI.h>

LiquidTWI lcd(0);

const int DispSampleTime = 1000;
// variables:
volatile unsigned long lastTime;
volatile unsigned long thisTime;
volatile unsigned long duration;
unsigned long disp_timeold = 0UL;
unsigned long timetemp = 0L;   
//SETUP RUN ONLY ONCE @ start

void setup()
{

  Wire.begin();
  TWBR = 12; //12 for 400mhz, 152 for 50khz, nothing for 100khz
  //Serial.begin(57600);
  lcd.begin(16, 2); // set up the LCD's number of columns/rows
  lcd.clear();
  
  //set interrupt on pin 2 and call function rpm_interrupt
  attachInterrupt(0, rpm_interrupt, RISING);
  
}

void loop()
{
  // DISPLAY RPM
  timetemp = millis() - disp_timeold;
  if( timetemp >= DispSampleTime)
  {   
    disp_timeold += timetemp;   
    unsigned int RPM = (unsigned int)(60000000UL/duration);  // 60.000.000 micros / duration in micros
    //disp stuff
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Rpm:  ");
    lcd.print(RPM);
    //Serial.print("Rpm:  ");
    //Serial.print(RPM);
    //Serial.println();
  }
}
void rpm_interrupt()
{
  lastTime = thisTime;
  thisTime = micros();
  duration = thisTime-lastTime;
}

Cheers for thoughts so far

p359:
Code:

The interrupt handling looks better than I thought.

Though I'd like to recommend some changes to your code.

Changes in variable declaration:

const int DispSampleTime = 1000;
// variables for ISR:
volatile unsigned long lastTime_ISR;
volatile byte count_ISR;
//  variables for loop()
unsigned long lastTime, duration;
byte count;
unsigned long disp_timeold = 0UL;

Changed interrupt handling code (still without debouncing):

void rpm_interrupt()
{
  lastTime_ISR=micros();
  count_ISR++;
}

Changes in calculating RPM within the loop function:

void loop()
{
  // DISPLAY RPM
  if (millis()- disp_timeold >= DispSampleTime)
  {   
    disp_timeold += DispSampleTime;   
    noInterrupts();  // disable interrupts while reading and resetting volatile variables
    duration=lastTime_ISR-lastTime;
    lastTime=lastTime_ISR;
    count=count_ISR;
    count_ISR=0;
    interrupts();   
    unsigned int RPM;
    if (count==0) 
      RPM=0;
    else
      RPM = 60000UL*count*1000.0/duration;
    //disp stuff
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Rpm:  ");
    if (RPM<1000) lcd.print(' ');
    if (RPM<100) lcd.print(' ');
    if (RPM<10) lcd.print(' ');
    lcd.print(RPM);
    
//    Serial.print("Rpm:  ");
//    Serial.print(RPM);
//    Serial.println();
  }
}

I have also added some code to make the RPM output always 4 digits wide. Otherwise you'd see wrong numbers on the LCD when the RPM falls back from a 4-digit number down to a 3-digit number (or down to 0).

If the calculated RPM values are too high, then the ISR needs some debouncing logic, so that pulses are not counted when they come in much quicker than the expected maximum RPM.

BTW: If 4 digits is all you want to display, maybe you'd like to display "big digits" on your display. By using "user defined characters" you could use your text display, to display some extra big digits like shown in this Youtube video: https://www.youtube.com/watch?v=0x3mUlYxfJA

jurs, appreciate the detailed reply, thank you for your time. Have added to my code, very quickly tested and worked great up to the red line of near 8k. Looks like the lcd write was the slow spanner in the works. Will be this weekend before I get back to the testing. Thank you for your help!

Paul__B, thank you also, I may have been blinded by all the other tacho info online, all I have seen use interrupts. Thinking through your description, does sound very logical method. I will try this also, I'm planning other features using gps and rtc so avoiding interrupts may be useful.

Cheers again :slight_smile:

p359:
Have added to my code, very quickly tested and worked great up to the red line of near 8k.

Congratulations!

BTW: As you are always printing the same number of digits, you could remove this line from your code:

lcd.clear();

Without that you can avoid a short "blank blink" each time the LCD is updated.

This would be especially usefull if you reduce the DispSampleTime to shorter values.