Frequency meter

Hi community,

I'm looking to measure the frequency of a sine wave signal in the range of 20Hz to 70Hz using an ESP32 S2 board.

I've tried using the ArduinoFFT library, but it didn’t work for my setup. Additionally, the freqcount and ESPFreqCount libraries are not functioning on this board.

While I'm unsure if FFT is necessary for this signal, I do notice some noise in the measurements.

Here are the raw data points I've collected (microseconds, analog input), which indicate a frequency of approximately 45Hz:


graph7.txt (3.6 KB)

Does anyone have suggestions for measuring the frequency, either with or without using FFT? Any help would be greatly appreciated!


#include <arduinoFFT.h>
#include <Wire.h>


#define CHANNEL 9
#define LED 5

const uint16_t samples = 512;          //This value MUST ALWAYS be a power of 2
const float samplingFrequency = 1000;  //Hz, must be less than 10000 due to ADC
unsigned int sampling_period_us;
unsigned long microseconds;

float a;

float vReal[samples];
float vImag[samples];
int data[] = {
    2907, 2833, 2727, 2643, 2593, 2553, 2527, 2487, 2417, 2337, 2237, 2113, 2017, 1945, 1943, 2033, 2159, 2357, 2555, 2753, 2901, 2939, 2915, 2849, 2743, 2665, 2585, 2541, 2513, 2483, 2443, 2359, 2261, 2155, 2053, 1955, 1945, 1989, 2119, 2279, 2473, 2703, 2859, 2945, 2913, 2877, 2797, 2697, 2621, 2575, 2533, 2507, 2445, 2383, 2293, 2189, 2091, 2017, 1947, 1989, 2101, 2263, 2435, 2657, 2823, 2913, 2931, 2897, 2799, 2701, 2627, 2563, 2555, 2513, 2473, 2411, 2311, 2193, 2099, 2017, 1925, 1949, 2051, 2201, 2391, 2603, 2787, 2911, 2961, 2905, 2831, 2727, 2627, 2575, 2531, 2521, 2471, 2419, 2353, 2237, 2121, 2017, 1947, 1963, 2017, 2161, 2317, 2541, 2731, 2891, 2931, 2939, 2867, 2749, 2663, 2571, 2555, 2515, 2489, 2425, 2355, 2283, 2145, 2033, 1971, 1927, 2017, 2125, 2287, 2487, 2697, 2861, 2917, 2921, 2881, 2789, 2665, 2605, 2575, 2537, 2517, 2445, 2377, 2277, 2181, 2057, 1965, 1903, 1951, 2079, 2227, 2425, 2645, 2825, 2917, 2933, 2889, 2799, 2705, 2635, 2567, 2521, 2493, 2465, 2413, 2305, 2205, 2101, 2017, 1959, 1949, 2063, 2195, 2357, 2595, 2785, 2907, 2933, 2915, 2827, 2719, 2639, 2583, 2549, 2509, 2481, 2399, 2333, 2247, 2121, 2017, 1951, 1953, 2017, 2157, 2329, 2529, 2749, 2871, 2931, 2929, 2861, 2743, 2673, 2597, 2549, 2525, 2485, 2435, 2373, 2269, 2157, 2025, 1959, 1939, 1973, 2115, 2265, 2477, 2699, 2865, 2921, 2925, 2877, 2769, 2685, 2621, 2567, 2533, 2497, 2465, 2385, 2305, 2175, 2053, 1963, 1923, 1983, 2075, 2233, 2425, 2631, 2817, 2899, 2951, 2939, 2825, 2733, 2697, 2585, 2523, 2493, 2451, 2415, 2307, 2225, 2091, 1985, 1947, 2017, 2063, 2183, 2379, 2531, 2783, 2905, 2931, 2907, 2845, 2695, 2669, 2583, 2547, 2531, 2471, 2427, 2349, 2229, 2125, 2023, 1927, 1949, 2017, 2135, 2327, 2539, 2737, 2883, 2949, 2941, 2849, 2767, 2657, 2595, 2559, 2511, 2497, 2447, 2365, 2261, 2159, 2041, 1979, 1941, 1979, 2103, 2273, 2461, 2653, 2837, 2935, 2929, 2877, 2803, 2705, 2607, 2569, 2513, 2487, 2451, 2363, 2281, 2181, 2069, 1961, 1941, 1949, 2073, 2221, 2415, 2631, 2805, 2905, 2923, 2885, 2811, 2721, 2641, 2561, 2535, 2471, 2457, 2395, 2305, 2193, 2083, 1993, 1945, 1941, 2017, 2175, 2353, 2615, 2765, 2891, 2943, 2915, 2835, 2721, 2635, 2585, 2553, 2521, 2485, 2423, 2359, 2267, 2133, 2037, 1955, 1965, 2017, 2129, 2331, 2533, 2737, 2893, 2957, 2947, 2889, 2793, 2689, 2629, 2577, 2545, 2505, 2441, 2381, 2269, 2165, 2065, 1971, 1953, 1983, 2109, 2259, 2477, 2689, 2867, 2929, 2925, 2897, 2783, 2705, 2623, 2565, 2547, 2497, 2473, 2403, 2301, 2185, 2059, 1957, 1925, 1959, 2063, 2225, 2407, 2635, 2809, 2917, 2929, 2861, 2789, 2701, 2623, 2579, 2521, 2511, 2459, 2397, 2311, 2207, 2101, 2017, 1929, 1945, 2019, 2169, 2323, 2573, 2763, 2865, 2919, 2899, 2819, 2737, 2653, 2593, 2521, 2499, 2469, 2391, 2339, 2249, 2139, 2017, 1953, 1947, 2017, 2141, 2349, 2525, 2739, 2859, 2925, 2919, 2853, 2771, 2655, 2609, 2555, 2523, 2501, 2465, 2387, 2281, 2173, 2025, 1955, 1933, 1993, 2133, 2303, 2497, 2703, 2863, 2937, 2943, 2891, 2791, 2701, 2635, 2587, 2533, 2519, 2443, 2379, 2293, 2197, 2065, 1989, 1947, 1973, 2083, 2267, 2455, 2673, 2829, 2931, 2933
};

/* Create FFT object with weighing factor storage */
ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, samples, samplingFrequency, true);

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03

void setup() {

  pinMode(LED, OUTPUT);

  sampling_period_us = round(1000000 * (1.0 / samplingFrequency));
  Serial.begin(115200);
  Serial.println("Ready");

  Serial.println(F("Initialized!"));

}


void loop() {

  microseconds = micros();
  for (int i = 0; i < samples; i++) {
  vReal[i] = data[i];
  vImag[i] = 0;
  Serial.println(vReal[i]);
  
  microseconds += sampling_period_us;
  }

  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward); /* Weigh data */
  FFT.compute(FFTDirection::Forward);                       /* Compute FFT */
  FFT.complexToMagnitude();                                 /* Compute magnitudes */
  float x = FFT.majorPeak();

  Serial.println();
  Serial.print("Hz: ");
  Serial.println(x);
  delay(6000);

}
 

Thank you!

You did not tell us anything about the voltage of your sine wave. That is kind of important. Arduinos do not like negative voltages. So please show a schematic of your project.

For such a low frequency signal it will work best to count the milliseconds between zero crossings. OR even microseconds.

1 Like

Hi Paul, it is an IR sensor TCRT5000 connected to a gpio with a 10kohm

I see absolutely nothing in the documentation to indicate there is a sine wave output from that device. Please continue to explain fully.

Sorry, did you look the graph? I'm generating a sine wave and measuring with IR sensor.

The posted plot is not of a sine wave, and an FFT spectrum calculation will reveal multiple harmonics as well as the fundamental.

The ArduinoFFT library works fine on all Arduinos, but old example code won't compile properly because of recent library updates.

yes, it is true. It has harmonics it is a string measure with an IR sensor. I'll share the code...

Here is what I get when I plot the first 128 points of the posted "Serial Debug" data versus the (origin shifted) time in seconds. Can you explain why it doesn't look anything like the plot you posted?

untitled

First seven data points:

12976542 1153
12986529 1123
12996529 1025
13006529 1119
13016529 935
13026529 1217
13036529 1025

Sorry my bad. It was another sample. I've updated the txt

Spectrum of the reposted data (magnitude of the FFT result, X axis in Hz):
untitled

Keep in mind that for the FFT calculation, the sample buffer length must be a power of 2, so length 256 for the 192 point data set. Zero padding will introduce some artifacts.

An alternative to frequency measurement is to use autocorrelation, which is much simpler than calculating the FFT.

1 Like

Yes, I test it with 64, 128, 256… and for the frequency when I set 100Hz and I print it, is a shit. So I have to put 1000hz to have a nice sampling.
So I don’t understand how it would work with a low frequency as the library say (measured frequency x2).

To avoid sampling artifacts (aliasing), a low pass filter must used to ensure that no signal component in the input data is present at frequency higher than (sample frequency)/2.

Alternatively, sample at > 2x(highest frequency in input data).

4 Likes

If you are generating the original frequency yourself, then you should know the frequency.

Are you kidding me? I’m doing this to test it. In the practical it will be unknown

Then maybe you should have mentioned that.

I see this is your first post so perhaps you should read this

and this

and post your code

how?

why not connect the sine wave directly to the arduino?

it seems to me you have designed a cruise ship without checking if it will float.

1 Like

Hi John, my question was actually about the code. The electrical part is resolved, and you can see the signal. I thought that would be sufficient. Since you have the raw data, I was hoping you guys could give me a hand with how to compute the signal.

Thanks man, I appreciate it. You’ve taken the time to analyze the signal.

SOLUTION:
I change i = 5 in the findMaxY function of the library ArduinoFFT.

void ArduinoFFT<T>::findMaxY(T *vData, uint_fast16_t length, T *maxY,
                             uint_fast16_t *index) const {
  *maxY = 0;
  *index = 0;
  // If sampling_frequency = 2 * max_frequency in signal,
  // value would be stored at position samples/2
  for (uint_fast16_t i = 5; i < length; i++) {
    if ((vData[i - 1] < vData[i]) && (vData[i] > vData[i + 1])) {
      if (vData[i] > vData[*index]) {
        *index = i;
      }
    }
  }
  *maxY = vData[*index];
}