Want to modify incoming frequency and resend it (tachometer and speedometer)

hi there,

i want to change the scales on a speedometer and tachometer.

the old RPM scale goes up from 0rpm (0hz) to 9000rpm (293hz) and have a 5v squarewave signal.
the new RPM scale goes from 0rpm to 10.000rpm.

so the frequency should get measured, calculated by /10*9.

the old speedo scale goes up from 0km/h (around 100hz) to 180kmh (around 1300hz) and have a 10v squarewave signal.
the new speedo scale goes from 0km/h to 280km/h

so the frequency should get measured, calculated by /280*180.

i started two days ago and after more than 10hours i have no solution. i´m currently just working on the tachometer.

thats the code which was working the best till now, with the problem it did not work below 31hz (i know and understand why).
other attempts with different methodes causes a inaccurate output on higher frequences.

int pinIN = 7; unsigned long duration;

void setup() { Serial.begin(9600); pinMode(pinIN, INPUT); }

void loop() { duration = pulseIn(pinIN, HIGH); unsigned long frequencyIN = 1000000L / duration /2; unsigned long frequencyOUT = frequencyIN /10*9; tone(10,frequencyOUT); Serial.print(F(" Frequency IN: ")); Serial.print(frequencyIN); Serial.print(F(" Frequency OUT: ")); Serial.println(frequencyOUT); }

of all samples i found the code below was the best working (accuracy), but with the problem that i don´t know how to modify the code to have my calculation instead of dividing by 2)
code found here: Arduino Neuling, Rechtecksignal halbieren und ausgeben - #20 by Whandall

const byte pulsePin = 2;
const byte outPin = 3;

volatile unsigned long duration;
volatile bool newDurationAvailable;
volatile bool gleicheFrequenz;
unsigned long edgeUp;
unsigned long edgeDown;
unsigned long downDuration;
unsigned long upDuration;

void takeEdges() {
static byte currCycle;
byte currState = digitalRead(pulsePin);
if (gleicheFrequenz || currCycle & 1) {
digitalWrite(outPin, currState);
}
if (currState) {
edgeUp = micros();
downDuration = edgeUp - edgeDown;
} else {
edgeDown = micros();
upDuration = edgeDown - edgeUp;
if (!newDurationAvailable) {
duration = upDuration + downDuration;
currCycle++;
newDurationAvailable = true;
}
}
}

void setup() {
Serial.begin(115200);
//pinMode(pulsePin, INPUT_PULLUP);
pinMode(outPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(pulsePin), takeEdges, CHANGE);
}

unsigned long lastPrint;

void loop() {
unsigned long topLoop = millis();

if (newDurationAvailable) {
unsigned long frequency = 1000000L / duration;
gleicheFrequenz = frequency < 3;
if (topLoop - lastPrint >= 100) {
Serial.print(F("duration: "));
Serial.print(duration);
Serial.print(F(" frequency: "));
Serial.print(frequency);
Serial.print(gleicheFrequenz ? F(" gleiche") : F(" halbe"));
Serial.println(F(" Ausgabefrequenz"));
lastPrint = topLoop;
}
newDurationAvailable = false;
}
}

the problem is that i don´t understand how to modify the line
if (gleicheFrequenz || currCycle & 1) {

my knowledge is tiny and i really want to learn more about c++ and arduino but at this point i have to make a cut here and hope someone helps me.

according to google translate, gleicheFrequenz == same frequency so this statement

if (gleicheFrequenz || currCycle & 1) {...

tests the variable 'gleicheFrequenz ' for true (any non-zero value) or tests if currCycle least significant bit is set (e.g. if currCycle = 4 = 0b0100 so the LSB is not set which so that test would be false)
It is basically saying if the samefrequency is true or the currCycle count is odd (e.g. every other cycle) then do the body of the if() statement

I do not think you have to mess with that part of the code. In loop(), if a new cycle has been measured, it calculates the frequency and prints it out. Just apply your scale factor to the frequency

but how? where can i implement this?

Modify the code inside loop()...

void loop() {
  unsigned long topLoop = millis();

  if (newDurationAvailable) {
    unsigned long frequency = 1000000L / duration;
    unsigned long freq2 = freqency * 10.0 / 9.0;
    gleicheFrequenz = frequency < 3;
    if (topLoop - lastPrint >= 100) {
      Serial.print(F("duration: "));
      Serial.print(duration);
      Serial.print(F(" frequency: "));
      Serial.print(frequency);
      Serial.print(F(" freq2: "));
      Serial.print(freq2);
      Serial.print(gleicheFrequenz ? F(" gleiche") : F(" halbe"));
      Serial.println(F(" Ausgabefrequenz"));
      lastPrint = topLoop;
    }
    newDurationAvailable = false;
  }
}

Now you have a new frequency 'freq2' that is 10/9 of the original frequency.

but then i would have still the same problem like now that i don´t know/understand, how to output freq2 .

should also be swapped to get the correct needle position.

unsigned long freq2 = freqency / 10.0 * 9.0;

what i understood is, that this part...

if (gleicheFrequenz || currCycle & 1)

... causing a splitting and by this a half output frequency.

i´m also open to other solutions if you have an idea how i could output the frequency (without tone) by calculation of incoming frequency. :slight_smile:

tried another version.... now with timer1 library.... same problem. at one point the incomming frequency is higher then the real send frequency. by the end (top of scale) its about 30hz.

problem pop up once both (input and output) will work in one sketch.

Would blinker() do the job of generating the output square wave?

allready tried.

did cause to me the same problem like written in the previous post.

the only code which does not cause the problem is the 2nd i posted. but there i have to problem that i don´t know/understand how to implement the formular.

You know what? I would scrap all that code that you copied from the internet and start again. You are struggling because you are trying to use someone else's code, which you don't understand (neither do I).

It is so much better to write this yourself as a learning project, and then you will understand it perfectly and be well placed to add new features, or take on other Arduino projects.

the first code i posted is mine. problem with it was the known 31hz problem.

i was looking for other codes to understand how i could relize that. i can not go study to just solve that one code....

alos... there are different learning types of humans... some they do it and learn, some they just hear it and learn and some they see/read it and learn. i have to do ... on a working example.

what would you do after spending 15-20hours on such a simple thing?

1 Like

OK, so have you tried @blh64's solution, along with blinker()?

Also, I see lots of print() statements, so where, exactly, is the problem occurring? Is the problem with measuring the incoming frequency accurately, doing the calculation, or generating the output frequency?

Input:
I would begin by writing some code to read an incoming signal, measure its frequency and then print() that frequency. I would test this over the operating range that you require using a signal generator, and make sure it is working accurately.

Output:
Then, I would write some code to generate an output square wave, probably using blinker(), and I would confirm that it produces accurate frequencies over the required range, probably using an oscilloscope or frequency meter.

Shim:
Finally, I would write the conversion function that takes in the measured incoming frequency, converts it to the required output frequency, and instructs the output function to generate the modified frequency square wave.

For the tachometer, I'd measure the amount of time (microseconds) it takes to observe 9 cycles of the input clock. Then, divide that number by 10 to set the period of the output signal.

Likewise, for the speedometer, I'd time 9 cycles of the input clock. Then divide by 14 to set the period of the output signal.

The above would take place continuously so that that the output signals track the input signals in the proper ratios.

1 Like

I'd, of course, sigh.., would go at it differently. Put the input pulses on interrupts. Then maybe, once a second? I'd read and save how many pulses came came in of each. Then clear the accumulators. Now, you have all the time in the world. Toss the results into mappers. And, if you want square wave outputs, set the periods of the blinkers. You could even do Serial outputs without messing anything up.

But, NO delay() calls! Messes up the output.

-jim lee

yes.... blinker have been tried.

i will look if i deleted the code and post it.

of course. i read about the delay or better say blink-without-delay stuff.

i will look if i deleted the code with trying it with interrupts and post it. otherwise i will write a new one and post it.

There is another guy trying to do pretty much the same thing you are right now. I posted a "serving suggestion" example on it. Its basically the same thing I was thinking for you. Although yours is a little hard for me to figure out exactly what you are looking for.

-jim lee

Of course. In fact, move as much of the work to hardware as possible. With, say an Arduino Mega, you have enough 16-bit timers to handle the job without software "blinkers".

I'd use two of the timers to measure the period of the input signals (using input capture) and two to run the output signals. Using the ratios I suggested in Post 13 will help smooth things out. With proper mode selection on the timers used to generate outputs, changing period can be done without causing runt pulses.

The only thing you need to do in software is service the interrupts and adjust output pulse period. The latter can be done in non-ISR code and synched with the timer interrupts.

1 Like

Makes perfect sense. We each choose the tools we are used to.

-jim lee

Yea, given that the frequencies involved are pretty low, there are options.