Help to optimize code used for a tachometer

Hi can someone help me to optimize my code used for a tachometer?

I need to make the resolution better ie. right now it jumps 60rmp up or down. I would like it to be under 10 - can anyone
help with that?

Im using a UNO and when the tachometer is running it jumps to 0 and them up again.. is this a buffer problem?

Thanks for any help..:slight_smile:

This the code:

// using a hall sensor as a tachometer with a LCD display using U8glib / 12864 graphic LCD ST7920

#include "U8glib.h"
#define PMS_PIN 2 // Pin for signal from hall sensor
//#define LED_PIN 13 //Using Arduino's Internal LED as an hall sensor indicator for testing the signal.

boolean counted=false;
int t1=0,t2=0;
int hits=0;
int rps=0;

U8GLIB_ST7920_128X64_4X u8g(11, 10, 12); // this is the 12864 graphic LCD ST7920 using pin 11,10,12

void u8g_prepare(void) {

void setup(){
  pinMode(PMS_PIN, INPUT);
 // pinMode(LED_PIN, OUTPUT);
   // flip screen, if required
  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 )
    u8g.setColorIndex(255);     // white
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT )
    u8g.setColorIndex(3);         // max intensity
  else if ( u8g.getMode() == U8G_MODE_BW )
    u8g.setColorIndex(1);         // pixel on 
//  pinMode(13, OUTPUT);          
 // digitalWrite(13, HIGH);  

void loop(){
  t2 = millis();
  if(t2 >= (t1 + 1000)){
    rps = hits;
    hits = 0;

  do {
    u8g.print("RPM: ");

  } while( u8g.nextPage() );
  if(digitalRead(PMS_PIN) == HIGH){
      counted = true;
  } else {
    counted = false;
 // digitalWrite(LED_PIN, digitalRead(PMS_PIN));

now your code counts the #hits per second and multiply that by 60, that explains the jumps in steps of 60.

rewrite your code to measure the time between two adjacent pulses (p1 and p2) in micros();

then RPM = 60 000 000 / diffTime;

the problem will be that this method gives some jitter, so you might need to average 5 pulses or so .


What range of frequencies does the tacho need to support?

What are your minimum and maximum acceptable update intervals for the display?

What range of frequencies does the tacho need to support?

10 to 2800rpm

What are your minimum and maximum acceptable update intervals for the display?

10 to 2800rpm and 1 to 2 sec update can i perhaps run buffer interrupt?

to get the idea - not most efficient , give it a try

volatile uint32_t diff = 0;
volatile uint32_t p1 = 0;

void setup() 
  attachInterrupt(0, counter, RISING);

void loop() 
  uint16_t RPM = 60000000UL/ (diff + (diff==0));  // prevent divide by zero

void counter()
  if (p1 == 0) p1 = micros();
    diff = micros() - p1;
    p1 = 0;

10 to 2800rpm and 1 to 2 sec update can i perhaps run buffer interrupt?

In that case using an interrupt to increment the counter, and a timed function once per second to compare the new counter with the previous counter, calculate the frequency and update the display would seem like a sensible approach. If you can tolerate updating the display slightly more frequently or accept a slightly increased latency in reflecting speed changes then it would be a good idea to put a little smoothing (averaging) in the output to reduce the effects of jitter at low speed when consecutive sample periods will contain inconsistent numbers of samples.

Hi again thanks for the help and sample code..:slight_smile:

I will test the code and get back to you and the forum..

Hi again Iam still working on the code and i got it to work ok now, I made a small video about it - I thought I would share it with you here is the link:

I will be back with an update..:slight_smile:

Very good video, good timing, not too long or too short, shows enough detail (for now :wink:

I noticed the screen makes steps of 10 even 20 RPM . some math:
500 => 2000 micros/pulse
499 => 2004
498 => 2008
220 => 4545
221 => 4524

so at 500RPM the timing is at its limit (micros() makes steps of 4us, next step would be HW timer),
that means that stepsize of ~2% of RPM is possible. Typical rounding steps are 1,2,5, 10

int raw = getRawRPM();
if (raw > 500) RPM = ((raw+5)/10)*10;
else if (raw > 300) RPM= (((raw+3)/5)*5;   // in general  ((raw+rounding/2)/rounding*rounding);
else if (raw > 150) RPM =  (((raw+1)/2)*2;
else RPM = raw;