frequency correction

Hi,

late summer this idea was born. Now in christmas holidays I have more time to follow up on this project. It sounds simple, but after searching and playing several days I am still stuck in the beginning completely.

I have a bike with a tachometer, which is running ahead approx 8,5%. The reading is faster, than the actual speed. The signal comes from a hall sensor giving e.g. 80.9Hz @10km/h or 1213.4Hz @ 150km/h.

The path:

  • Read the signal from the hall sensor usind a Nano. I was thinking of pulseIn()
  • define a correction factor, in my case 1.085 and
  • give a square wave frequency out, that displays the correct bike speed

I am quite new to micro controller programming, so please let me learn.

I succeded on a simple code, that creates a nice output frequency as I need it.

int PulseModOut = 3;
int SpeedPulse = 7;
int state = 0;

void setup() {
pinMode(SpeedPulse, INPUT);
pinMode(PulseModOut, OUTPUT);
}

void loop() {
while(1) {
if(state == 0) {
digitalWrite(PulseModOut, LOW);
state = 1;
}
else {
digitalWrite(PulseModOut, HIGH);
state = 0;
}
delayMicroseconds(400);
}
}

The problem:
The accuracy is far below of what I expect. The osci reads 1217-1229Hz.
I need to improve on that, this is 1% error. I do not understand why.
@ delayMicroseconds(100) it increases to 2.8% error and
@ delay(0) I get 55.56-62.5kHz program frequeny. That is an amazing 12.5% error!!

What else do I need?
As I am also not satisfied with the reading of input frequency, I split this function code for testing. My osci has a test pin with 2V @ 50Hz, I could not succeed to get a good reading of this yet by using my Nano and pulseIn()

byte PWM_PIN = 3;
int pwm_value;

void setup() {
pinMode(PWM_PIN, INPUT);
Serial.begin(115200);
}

void loop() {
pwm_value = pulseIn(PWM_PIN, HIGH);
Serial.println(pwm_value);
}

Q:
Do I need a transistor, or should I rather use a opto cuppler as a signal converter into my Nano? Dirct connection is not appropriate?

And now the really tricky part in the programming logic:
So, I measure the time between two HIGH or two LOW, which equals to the time of oscillation (T). The frequency (f) correlates as f=1/T. If I shift the frequency by my correction factor, how often should I do it? If I do it every periode, there will not be any change or shift. I must find a logic, that has good update speed and still appropriate functionality. The relatively low input frequency is not really in my favor.

What else shall I consider in this project?

The tachometers used for bicycles have a software calibration setup page for setting the wheel circumference.
Are motorcycle tachometers different?

Because interrupts and other considerations tend to distract the CPU in the Arduino, the best way to produce an accurate and stable frequency is to program a hardware timer (in this case you would probably use the 16 bit Timer1).

However, there is a steep learning curve. You can find an excellent overview here but googling "arduino timer frequency generation" will show you many others.

For test frequency generation, I often use the tone() function.

When I tested your code with some frequency reading tools I have, I was getting about 1225 HZ, which is reasonable given the overhead of the digitalWrite() and the other tests and assignments in your while loop.

When I replaced your code with tone(3,1250) I was reading that frequency.

You are absolutely on the correct path with using frequency generating tools to develop tachometer or frequency reading code. pulseIn() is a blocking function and in not ideal for the basis of a tachometer. You will be better off learning how to use interrupts to read pulse edges. Start with external interrupts on pin 2 or 3 of the nano. It turns out that you can use the tone() function at the same time the pin that is running the interrupt and verify your code.

volatile unsigned long  count = 0;

unsigned long copyCount = 0;

unsigned long lastRead = 0;

unsigned long interval = 1000;

void setup()
{
  Serial.begin(115200);
  Serial.println("start...");

 pinMode(3,INPUT_PULLUP); //external interrupt 1 on pin 3
 attachInterrupt(1, isrCount, RISING);

 tone(3,1250);//1250 hz square wave on pin 3
}

void loop()
{
  if (millis() - lastRead >=interval) //read interrupt count every second
  {
    lastRead  += interval; //millis();
    // disable interrupts,make copy of count,reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
   interrupts(); 

   Serial.println(copyCount);
  }
}

void isrCount()
{
  count++;
}

Hi cattledog,

your lead was very helpful, thank you very much. I not understand it deeply yet. Interrupts are very useful, and I was able to modify it to my needs. I am already quite happy.

I use one nano to provide a changing square wave frequency using tone() to simulate the speedsignal from my bike, and a second nano to have following code running. Both are hooked up to a digital oscilloscope to verify the actual output.

Though, I still have some issues, which I am not sure how to solve. The minimum frequency that can be generated by tone is 30.64Hz. If the supplied frequency (real speed signal) is below that (even zero) my new code will still give out 30.64Hz. This corresponds to 3,8km/h. Not really a big deal, but my bike is having a digital tachometer, and I feel funny, that when standing around, I can constantly read 4km/h. All my trying to implement a second if/else into the code did not succeed.

A second path for finding a solution I found on following website (in german)
http://www.elektronik-labor.de/Arduino/Rechteck.html
It describes a way to generate even lower frequencies (down to 2Hz). That is quit advanced, so I couldn´t implement it into my code yet. The provided code there shows several error messages when I try to test it. Once I understand it, I may update this entry here to share my new knowledge…

frequency_modulator_final_2.ino (713 Bytes)

Hi,

BIG QUESTION, TACHO is engine RPM, SPEEDO is vehicle speed, which is it?

If its speed, then leave it alone, I think you will find that is how vehicle speedo's are calibrated, to read to fast.
In some countries it is illegal to tamper with speedo equipment.

So what is it TACHO or SPEEDO, you are talking kph?

Tom..... :slight_smile:

Hi,

what you write is correct. The corresponding national regulations concerning tachometer readings come from a time, where production relateded accuracy and wheelsizes differed so much, that up to 10% faster readings were permitted for manufacturers. The tachometer just was not allowed to show a slower speed than the actual.

Today there are certain maufacturers which abuse these regulations for another purpose. It is concerning their fuel efficiency. They cannot achieve a more efficient motor technology so cheaply as by cheating on tachometer readings. They just shorten the actual traveled distancy of the vehicle still consuming the same amount of gasoline. So if you are reading 100km(/h) on tachometer but just ride 91km your fuel consumption seem to have improved astonishing 9%. That´s magic! Or is it legal betray on customers?

It is extremely annoying for me, that I must ride 110km/h not to be disturbing the other flowing traffic. For my bike only one type of wheel is allowed, so on that side there is nearly no divergence.

Based on above facts I just don´t care, as I stay within the legal accuracy. If I feel fancy, I could bring my bike to a legal office to have my modification approved against $. Possibly I will do it after implementation. :wink:

The best in this project is a great learning experience. That is the true motor in our life!

I understand that you need a way to handle the low frequencies. You can do it with the hardware timers, but as you have discovered, that route is somewhat complex.

However, I think you should be able to replace the use of tone() for output with a simple library named TimerOne which can generate pulses from hardware Timer1. GitHub - PaulStoffregen/TimerOne: TimerOne Library with optimization and expanded hardware support

I’m not sure how well it will handle the dynamic frequency changes, but give it a try. If it doesn’t work, I can help you with more complex code which works with Timer1 directly. I have also changed the some of the floats in your code to integers.

#include <TimerOne.h>
//https://github.com/PaulStoffregen/TimerOne

//volatile unsigned long count = 0;
volatile unsigned int count = 0;
float cf = 1.085; // correction faktor
//float copyCount = 0;
int copyCount = 0;
//float SpeedPulseOut;
int SpeedPulseOut = 0;
const byte pulseOutPin = 9;

unsigned long lastRead = 0;
unsigned long interval = 500; // updatespeed 0,5sec

void setup()
{
  Serial.begin(115200);
  pinMode(3, INPUT_PULLUP); //external interrupt 1 on pin 3
  attachInterrupt(1, isrCount, RISING);
  //tone(3, 500);//500hz on pin 3 test input
  pinMode(pulseOutPin, OUTPUT);
  
  //Timer1 library interrupt to output sq wave
  Timer1.initialize(500000);//base time interrupt every .5 sec
  Timer1.attachInterrupt(pulseOutput);
}

void loop()
{
  if (millis() - lastRead >= interval) //read interrupt count every interval
  {
    lastRead  += interval; //millis();
    // disable interrupts, make copy of count, reenable interrupts
    noInterrupts();
    copyCount = count;
    count = 0;
    interrupts();

    SpeedPulseOut = copyCount * 1000UL / interval * cf;
    Serial.println(SpeedPulseOut);
    //tone(9, SpeedPulseOut); //replace with Timer1 interrupt
  }
}

void isrCount()
{
  count++;
}

void (pulseOutput)()  //toggle output pin 1Hz minimum
{
  Timer1.initialize(500000 / SpeedPulseOut);//change frequency
  //toggle output pin
  digitalWrite(pulseOutPin, !digitalRead(pulseOutPin));
}

Hi,
SPEEDO ot TACHO. okay.....

Have you measured your ODOMETER reading against a known distance?
I think you will find it is accurate.

Do not assume because your SPEEDO is reading high that the ODOMETER is too, one is a distance system, the other a velocity system.

Although they use the same pickup they use the information differently.

Tom.... :slight_smile: