Pulse reading circuit (tachometer) for arduino

Hi all!

I'm currently working on creating a datalogging system for a motorcycle, that includes logging the RPM of the bike. An Arduino Mega 2560 is at the base of this system. So far I have a number of sensors (temperature, TPS, lambda) working, but I'm having some problems figuring out how to read the RPM.

I'm trying to read the dedicated tacho out signal from the bike's CDI to log the RPM. The info I have on this signal is limited (since I also don't own an oscilloscope) and is based on the RPM input requirements I have for the bike's instrument panel. These are as follows:
Vhigh: > 4.3V
Vlow: < 0.5V
Tperiod: >0.5ms
Tactive: >0.2ms

I got some advice on the hardware side, a circuit that should help in reading the RPM signal with the Arduino, but honoustly I don't really get what the output of this circuit is... Anyone that can enlighten me?

Also, the Schottky diodes that are in the circuit seem to be SMD types, but I don't think I have the tools (or skill) to solder any SMD components. Would anyone know of through-hole equivalents I could replace them with?

I'm fairly new to the electronics game, so any help would be greatly appreciated either on the hardware or software side!

RPM Circuit.png

Where did you get the circuit (the image is a bit small) as the protection diodes look to be the wrong way round.
Have a read of this to get an idea how the circuit effects the signal pulses.

Thanks Riva! Interesting read :slight_smile: I now understand the functionality of the circuit much better.

I got the circuit from the instrument panel supplier, as a response to me asking if they could give me some pointers on how to read the CDI's RPM output line with an Arduino.
Just the circuit though, no further explanation, so its great to get some info here!

If I understand correctly from your link, the circuit helps in conditioning the pulse signal so the Arduino won't get fried, while also eliminating any noise that might otherwise effect getting a proper reading?

If this is the case, what would in be the best way to determine the frequency (and ultimately the RPMs)?

And it seems you're right about the protection diodes being the wrong way round, don't know why they would make that mistake.
Is the selection for the specific type of Schottky diode important btw? Or can I get any through-hole Schottky diode to get this circuit to work? I looked into the BAT54S specs and also noticed it is a 'double Schottky diode', is there a specific reason this is necessary? For the D5 Schottky diode I couldn't find much :frowning:

rccrdo:
Is the selection for the specific type of Schottky diode important btw? Or can I get any through-hole Schottky diode to get this circuit to work? I looked into the BAT54S specs and also noticed it is a 'double Schottky diode', is there a specific reason this is necessary? For the D5 Schottky diode I couldn't find much :frowning:

No specific reason to get a double diode, the BAT54S is a single convenient package instead of 2 separate components. As for what to use instead, you need to ensure it can cope with the voltages your using and it needs to be Schottky as ordinary diodes wont kick in to protect before the Arduino pin is damaged, maybe the BAT85.
For code I would be tempted to use an interrupt pin to count pulses and if you read this count in the main code at fixed intervals (say 1 second) you can interpolate the RPM.

I ordered the components today, for the Schottky I went with two 1N5817 and I changed the condensator to a 3.3 nF one, according to the 'Fastest edge = 2.2RC' formula' from the website you posted Riva.
Once I have everything in I will try and figure out how to build my code. Seems an interrupt would be the best way, but I'm fairly new to this area so I'll have to do some digging on how to get it to work:)

Thanks again!

I recently made a tachometer for my lathe using a toothed disk and slot sensor for a pulse generator. Since you can't waste time doing other things while you count pulses I set up the counter to suspend other things for 1 sec while pulses were counted then went on with other things, eg, display for a few seconds then back to get another count. Possibly an interrupt could be used just as well and I'll look into it someday but for now what I have works fine.

Just passing this along as my experience (nothing sacred about it) and maybe you can use it.

rccrdo:
Once I have everything in I will try and figure out how to build my code. Seems an interrupt would be the best way, but I'm fairly new to this area so I'll have to do some digging on how to get it to work:)

Attached is some sample code I wrote to use Timer 5 to count pulses that you can maybe use as a building block.

Frequency_Counter_Mega_Timer5.ino (1.93 KB)

Thanks for the info Phoxx, right now I'm trying to use a hardware interrupt to count pulses and divide the total time passed by the total number of pulses counted.

I'm also trying to readout other stuff and sensors, so I guess suspending the code is not really an option for me. In the end, I would like a 1-2 second fixed interval for the logged data.

Right now, for the RPM part, I have written the code below. I haven't been able to test it yet (components should come in tomorrow), but if I'm correct this should work for both a signal that varies in duty cycle or period since I'm interrupting on a falling edge (someone that can confirm this? I'm quite new to the whole interrupts game)

edit: I realised that for duty cycle it doesn't work since the time between falling edges would stay the same, maybe if I could read the time between a falling edge and the first rising edge... For now, I'm assuming that the signal is going to change by it's period/frequency, and not duty cycle.

@Riva, thanks for the code! As you can see below, right now I'm just using millis to do my timing. Is there any benefit to using another (dedicated) timer, like you did in your code? I know that the millis timer is only an 8-bit timer, but in my case it should not be able to overflow or anything since the code is at a maximum running for about 8 hours straight.

Thanks!

// define rpm variables
volatile int revs = 0;
unsigned long rpm = 0;
unsigned long lastmillis = 0;

#define rpmPin 2

// refreshrate of data logging/printing in ms (only full seconds!)
int refreshRate = 2000;

void setup() {
// connect at 115200
Serial.begin(115200);

// define pinmodes
pinMode(rpmPin, INPUT);

// setup interrupt for counting engine revolutions
attachInterrupt(digitalPinToInterrupt(rpmPin), rpm_engine, FALLING);
}

void loop() {

// RPM reading & logging/printing of data
if (millis() - lastmillis == refreshRate) { // update every one second, this will be equal to reading frequency(Hz)
detachInterrupt(digitalPinToInterrupt(rpmPin)); // disable interrupt when calculating

// RPM logging/printing
rpm = revs * 60 / (refreshRate / 1000); // convert frequency to RPM, this works for one interruption per full rotation.
Serial.print("RPM: ");
Serial.println(rpm);

// Finish line
Serial.println(" ");
Serial.flush();

revs = 0; // restart the RPM counter
lastmillis = millis(); // update lastmillis
attachInterrupt(digitalPinToInterrupt(rpmPin), rpm_engine, FALLING); // Enable interrupt
}
}

void rpm_engine() {
revs++; }

@Riva, Is there any benefit to using another (dedicated) timer, like you did in your code? I know that the millis timer is only an 8-bit timer, but in my case it should not be able to overflow or anything since the code is at a maximum running for about 8 hours straight.

Unless you need PWM on pins 44-46 or have some special library code using timer 5 it us unlikely to be used for anything else so seems the ideal candidate to count external pulses. The advantage of using a timer input to count pulses is they can count at higher speeds than hardware/software interrupts pins.

FYI the millis timer might only be an 8 bit timer but it updates a 32 bit number so won't overflow for many days.

rccrdo:
The algorithm I used would work just as well with interrupts so you're welcome.

1 Set up a pulse counter and in the interrupt service routine add one to the count every time an interrupt comes in.

2 Every "x" seconds look at the counter, check the number in it and calculate rpm from the number and "x".

3 Zero the counter.

So now you have rpm to use as you please.

As for the "x" seconds I found the modulo function very useful. It's explained in the Arduino reference.

Copy millis() into a storage, say "millisx" (the Arduino doesn't like you mucking around with millis() other than to read it). Using the modulo function, divide millisx by, for example 1000. Then the modulo function gives a tick every 1000 milliseconds or 1 second. Of course you can use any other divisor "y" to get a tick every y milliseconds

Edit re above:
On the millis(), I see on some stuff in the past I've I used millis directly and got away with it, but I remember having problems other times.

Expansion on the modulo function: This is an integer function which returns the remainder when you divide the dividend by the divisor. The remainder will be zero when the dividend is an exact multiple of the divisor so you just have to check to see if the remainder is zero
eg:

if millisx % 1000==0

{
tick=1

if millisx % 1000==0

{
tick=1;
}