Arduino Nano - reading an input frequency, and outputting a different frequency?

Hi,
I'd like to make a device to drive a car speedometer.
The existing speedometer requires a set number of pulses/mile, but I have wheel speed sensors that output a different value, so I'd like to read an input frequency, apply a correction factor, and output an amended frequency using a Nano.
I need to read an input frequency between zero and around 500Hz, and output zero to around 300Hz.
I've written a sketch that can create a suitable output frequency range if I manually input an input frequency, but when I try and add the <FreqMeasure.h> library, and input a frequency from an external device (An Uno running a signal generator sketch), my conversion maths has no effect :frowning:
Is a Nano capable of the function that I'm trying to use, or does the nano not have enough timers?
'ant tips would be greatfuly received :slight_smile:


#include <FreqMeasure.h>   //NOTE:- Frequency measurment of pin 8 is defined within this library. 
float frequency = 0;

const int outputPin = 12; // Use digital pin 12


void setup() {
  pinMode(outputPin, OUTPUT);
  FreqMeasure.begin();

}
double sum=0;
int count=0;


void loop() {

if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 5) {
      float frequency_Input = FreqMeasure.countToFrequency(sum / count);
      
   sum = 0;         //don't forget these
   count = 0;         //don't forget these


float InputToOutputFactor = 0.61;

float frequency = frequency_Input * InputToOutputFactor;
; // Frequency in Hz
    }
if (frequency>41)
{
const long interval = 1000000 / frequency; // Calculate the interval in microseconds
const long halfInterval = interval / 2;
  digitalWrite(outputPin, HIGH);
  delayMicroseconds(halfInterval);
  digitalWrite(outputPin, LOW);
  delayMicroseconds(halfInterval);
}
if (frequency<41)
{
const float interval_2 = 1000 / frequency; // Calculate the interval in milliseconds
const float halfInterval_2 = interval_2/ 2;
  digitalWrite(outputPin, HIGH);
  delay(halfInterval_2);
  digitalWrite(outputPin, LOW);
  delay(halfInterval_2);

}

}
}`Use code tags to format code for the forum`
type or paste code here

Kindly edit your sketch so that the code tags take effect

500Hz slow enough that you could probably do it by testing the input pin in code with digitalRead(). Otherwise, PulseIn should work. Although I think PulseIn() is blocking, so no other code will execute while it's running. Make sure that FreqMeasure doesn't have that same problem!

What is the exact ratio of the old frequency to the new frequency? If a whole number, there will be cases where the signals will overlap and will be difficult to program for. Even then, there are very likely to be some points that will line up and cause glitches.

I don't know the exact factor that I need to modify the frequency by, and was hoping to fine tune the multiplication factor by road testing and comparison with recorded GPS speeds once I have something close working.
I thought I was close, but then, when I added these lines:-

float InputToOutputFactor = 0.61;

float frequency = frequency_Input * InputToOutputFactor;`

The scaling factor "InputToOutputFactor" has no effect on my output frequency :frowning:

Hi, @Redseven1
Welcome to the forum.

Can you put some Serial.println statements in your code to see what your variables are doing?
Use the IDE serial monitor.

Tom.... :smiley: :+1: :coffee: :australia:

Hello Redseven1,

I've tested your code using an Arduino Uno R3, as I don't have a Nano available.
I fed a square wave from a function generator in to pin8, and monitored the output on pin 12 using an oscilloscope.

The code seems to give a narrow pulse around 20µs wide at the same frequency as the input.

As part of my investigation, I removed the part of the code that generates the output frequency and replaced it with a section that took the output high, printed the values of 'frequency_Input' and 'frequency' then took the output low again.
Here are the results:


The input frequency is measured correctly and 'frequency' correctly calculated, but look how often it happens.

It is taking six cycles of the input frequency to complete.
This is due to the code measuring the frequency 6 times and taking the average.

This shows that the frequency measuring is 'blocking'.

At best the code would measure 6 pulses, generate 1 pulse, measure 6 pulses, generate 1 pulse etc.

Incidentally there was a topic recently:
https://forum.arduino.cc/t/not-sure-where-to-go-hz-question/1394732
that was asking a similar question to yours.

I proposed a solution in post #7, using interrupts to measure the period of the input signal and using timer 1 to generate the output frequency.

However there was a lower limit to the frequency it could generate.
This was around 250Hz (period = 4096µs).

Here is it with 500 Hz input frequency, using your factor of 0.61 to give 305Hz output:

This lower limit could be changed using the timer1 prescaler.

I'm doing some tests to see if I can use interrupts to measure the period, but generate the output using your method of taking the output high, delay for half the new period, take the output low,delay for half the new period.

I'll let you know what I find.

I rejected that method on the other topic as there are errors that get worse as the frequency goes up - they wanted to work up to 15kHz.

maybe worth having a look at not-sure-where-to-go-hz-question

Thank you for your replies so far.
After a little research via one of the listed links, I found a link to another project that included some less elaborate code than my original attempt, not actually measuring frequency, but, as suggested, period, then outputting suitably modified period.
This works well up to an input frequency of around 400Hz, but not above that frequency.
Ideally, I'd like it to work upto around 500Hz.
Is there something that I could do to the following code, to allow an input of 500Hz?

//1986 Porsche 944 Turbo Speedometer CalibratorcurrentSpeed
//Public Domain
//raw 944 transmission sensor signal must be processed by external schmitt trigger
//Schmitt trigger should bypass filer and pull-up resistor
//Use LM2940-10 with decoupling caps per datasheet to power Arduino in automotive setting
//Arduino Pin 7 supplies modified signal for speedometer

 const int debounce = 2500;
 const int speedometerPin = 7;                    
 const int sensorPin = 3;                                     
 int pulseState = LOW;                                        

volatile unsigned long currentMicros = 0; 
volatile unsigned long previousMicros= 0;  
volatile unsigned long currentSpeed = 0;  
volatile unsigned long previousSpeed = 0;
volatile unsigned long interval = 0;                 
unsigned long modInterval = 0;                

float calFactor = .61;   // decrease to slow down speedometer                           
                                     // calFactor of 1 makes no change to speedometer

void setup()
{
pinMode (13, OUTPUT);                        
pinMode(speedometerPin, OUTPUT);    
pinMode(sensorPin, INPUT);                 
digitalWrite (sensorPin, HIGH);             
attachInterrupt (1, iSr, FALLING);        

}

void loop()

{

  noInterrupts();                                                 

  modInterval=interval;                                    

  interrupts();                                                      

  currentMicros = micros();                             

  if (currentMicros-previousSpeed<1000000) 

  {

   if (currentMicros - previousMicros>((modInterval/2)/calFactor))          

       { previousMicros = currentMicros;                                                    

        if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;

        digitalWrite(13, pulseState);         //to blink onboard LED                                                         

        digitalWrite(speedometerPin, pulseState);

        }                                  

   }

}

 

void iSr()

{

  currentSpeed=micros();                   

  if (digitalRead(sensorPin)==LOW) 

     {

        if ((currentSpeed - previousSpeed) > debounce)  

           {

            interval = currentSpeed - previousSpeed;        

            previousSpeed=currentSpeed;                         

           }

      }

}

experimented with an ESP32

// ESP32 - measure pulse width and rising edge-to-edge time using timer1 pins 16 and 17
//  output lower frequency pulses, e.g. read 500Hz output 300Hz

// updated for Arduino ESP32 core 3.0 see
// https://docs.espressif.com/projects/arduino-esp32/en/latest/api/timer.html

#define LED 18  // pulse output

// note using hw_timer_t or micros() give similar values
hw_timer_t *Timer1_Cfg = NULL;  // timer object

// pulse timing data
volatile uint64_t riseTime, period, width;
// rising edge interrupt calculate period uSec
void IRAM_ATTR rising() {
  uint64_t rise = timerReadMicros(Timer1_Cfg);
  period = rise - riseTime;
  riseTime = rise;
}

// falling edge interrupt calculate pulse width uSec
void IRAM_ATTR falling() {
  width = timerReadMicros(Timer1_Cfg) - riseTime;
}

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.printf("\n\nESP32 measure pins 16 and 17 pulse information \n");
  //Timer1_Cfg = timerBegin(0, 80, true);           // API 2.x setup timer for 1uSec
  if ((Timer1_Cfg = timerBegin(10000000)) == NULL)  // API 3.0 setup timer for 1uSec
    Serial.println("timerBegin Failed!!");
  Serial.print("timerBegin() OK frequenmcy ");
  Serial.println(timerGetFrequency(Timer1_Cfg));
  //  setup interrput routines - connect signal to pins 16 and 17
  attachInterrupt(digitalPinToGPIONumber(16), rising, RISING);    // detect rising edge on pin 16
  attachInterrupt(digitalPinToGPIONumber(17), falling, FALLING);  // detect falling edge on pin 17
  Serial.println("displaying results");
  pinMode(LED, OUTPUT);
}

#define MULTIPLIER 500.0/300.0    // period multiplier
void loop() {
  static uint64_t output = timerReadMicros(Timer1_Cfg);
  while (1)
    if (timerReadMicros(Timer1_Cfg) - output > (uint64_t)(period * MULTIPLIER / 2)) {
      digitalWrite(LED, !digitalRead(LED));  // invert pulse HIGH or LOW
      output = timerReadMicros(Timer1_Cfg);
    }
 }

input pulse frequency 500Hz (yellow trace) output 298Hz (blue trace)

how fast do you want it to respond to changes in input frequency?
next cycle? sample over tenth of a second then update?

possibly you could use the same technique with a Nano using micros()

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.