ArduinoFFT - high-pass filtering via coding and a separate multiplication problem

Sorry for the lengthy write-up, want to give as much info as possible so you have the info needed to advise. Not sure, if this should go under "projects" because it involves programming and hardware and decision making on further development. But would like to ask with the most acute problem I have now: 60hz (mains) interference with FFT reading on laptop USB Serial port.

I get good FFT values, but the first few FFT bins readings (0Hz-100Hz) are killing the rest of the FFT string due to 60 Hz interference leaking in to the input signal. If I exclude the first few bins between 0 HZ to around 100 Hz and then going down the FFT string, I can identify/match the correct osope-measured frequency as the next highest magnitude FFT reading value.

The project will use batteries, but for now while developing I am using my laptop's USB port for power and USB serial input, an an oscope. The measured circuit and the laptop and the oscope inputs are all on a common ground and I have the wires as short as possible at this stage.

Based on my "research", the best solution is to use hardware (i.e. RC) to condition the ADC input signal for the desired (high and low) bandpass. The next best option is to pad the "offending" bins with mean FFT readings, and the last resort is padding them with zeros.

So first, a hardware high-pass filter question: what R and C values should I use on the Seed XIAO 52840:

  • Nordic nRF52840, ARM® Cortex®-M4 32-bit processor with FPU, 64 MHz
  • ADC sampling with input impedance between 6M Ohm-10M Ohm, max. sampling rate 250 kHhz capability, 12 bits max, set at 2.4V Internal Reference

The input signal is:

  • active op am amplified filtered AC sine wave (DC removed) between 0-2.4V, resting at 0-60mV, FFT reading triggered above 1V and then the measured input signal length period is between 40us and 1ms at a relatively narrow frequency band that is located anywhere between 500hz to 25Khz.
  • do not know how many mA's on the input line, but could measure if necessary

In my case, I would like to measure FFT above 500Hz and below 25kHz (in the most desirable case 500Hz-100Khz).

For the high-pass RC filter, want to read only above 500 Hz, so I calculated: 1K for R and 300nF for C. I am wondering if the 1K resistance would weaken the input signal too much?

As far as a possible programming solution: I am using a slightly modified ArduinoFFT library on this XIAO nrf52840 in mbed mode. I set an FFT-measurement start trigger threshold at 1V input and the ADC is set at 12-bit and 2.4V Internal REF.
Sampling rate: 50kHz Sample Size: 4096

With lower sample sizes like 64. 128. 256 the actual signal freq is detected by the FFT, but with higher ones like 1024 and 2048 and 4096 progressively I get the 60 Hz interference creeping in. I tried to change the 1.0 variable in the "sampling_period_us" values set at (1000000*(1.0/samplingFrequency)). But with much higher values of the 1.0 (longer sampling period) the FFT stops reading. I suspect that something here also leads to m second concern, that the FFT values read (when it works) are about 2.2 times consistently the actual freq measure with the oscope. For example:

actual input freq: 2160 Hz measured FFT: 4812 Hz
actual input freq: 2890 Hz measured FFT: 6360 Hz

(50kHz sampling rate, 2048 sample size, 12-bit)

Here is an example of the input signal burst captured by the oscope

//https://github.com/kosme/arduinoFFT/blob/master/Examples/FFT_speedup/FFT_speedup.ino

#include "arduinoFFT.h"
#include <Arduino.h>

/*
These values can be changed in order to evaluate the functions
*/
#define CHANNEL A0                      //default A0
const uint16_t samples = 4096;          //4096???
const float samplingFrequency = 50000;  //Hz, must be less than 10000 due to ADC (AVR only???)  Nrf52 ADC sampling can go as high as 35 khz or more
unsigned int sampling_period_us;
unsigned long microseconds;

/*
These are the input and output vectors
Input vectors receive computed results from FFT
*/
float vReal[samples];
float vImag[samples];

/* 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() {

  // Set the resolution to 12-bit (0..4095)
  analogReadResolution(12);  // Can be 8, 10, 12 or 14

  // Set the analog reference to 2.4V (default = 3.6V)
  analogReference(AR_INTERNAL2V4);                                  //signal threshold on oscope is 1.2V
                                                                    /*
AR_VDD: the default 3.3 V reference
AR_INTERNAL: built-in 0.6 V reference
AR_INTERNAL1V2: 1.2 V reference (internal 0.6 V reference with 2x gain)
AR_INTERNAL2V4: 2.4 V reference (internal 0.6 V reference with 4x gain)
*/
  sampling_period_us = round(1000000 * (1.0 / samplingFrequency));  //1 million -> 1 sec

  Serial.begin(115200);
}

void loop() {

  float y = 1;                             // analog in reading value multiplier
  if ((analogRead(CHANNEL) * y) > 1000) {  // starts a sampling period if exceed inout signal threshold

    /*SAMPLING*/
    microseconds = micros();  //start timer

    for (int i = 5; i < samples; i++) {  //sampling into real and img arrays

      vReal[i] = (analogRead(CHANNEL) * y);  //
      vImag[i] = 0;

      while (micros() - microseconds < sampling_period_us) {  //  sampling for ex: 1M/(1.0*256) = 28.5us period of time ,
        //empty loop
      }
      microseconds += sampling_period_us;
    }


    /* Print the results of the sampling according to time */
    // Serial.println("Data:");
    //PrintVector(vReal, samples, SCL_TIME);
    FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward); /* Weigh data */
                                                              // Serial.println("Weighed data:");
                                                              // PrintVector(vReal, samples, SCL_TIME);
    FFT.compute(FFTDirection::Forward);                       /* Compute FFT */
    // Serial.println("Computed Real values:");
    // PrintVector(vReal, samples, SCL_INDEX);
    // Serial.println("Computed Imaginary values:");
    //PrintVector(vImag, samples, SCL_INDEX);

    FFT.complexToMagnitude(); /* Compute magnitudes */
    Serial.println("Computed magnitudes:");
    PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
    float x = FFT.majorPeak();
    Serial.print(" Dominant Hz    ");
    Serial.println(x, 2);  //Print out what frequency is the most dominant. //high cutoff  1khz

    //while(1); /* Run Once */
    //delay(500); /* Repeat after delay */
  }  //  if abalogRead() loop
}

void PrintVector(float *vData, uint16_t bufferSize, uint8_t scaleType) {
  for (uint16_t i = 0; i < bufferSize; i++) {
    float abscissa;
    // Print abscissa value
    switch (scaleType) {
      case SCL_INDEX:
        abscissa = (i * 1.0);
        break;
      case SCL_TIME:
        abscissa = ((i * 1.0) / samplingFrequency);
        break;
      case SCL_FREQUENCY:
        abscissa = ((i * 1.0 * samplingFrequency) / samples);
        break;
    }

    Serial.print(abscissa, 6);  //num of digits to print /default 6
    if (scaleType == SCL_FREQUENCY)
      Serial.print(" Hz");
    Serial.print("    ");
    Serial.println(vData[i], 4);
  }
  Serial.println();
}

Interference from the AC line is best and most easily removed by proper grounding and shielding of the input signal circuitry.

If you want help with that, post a complete schematic diagram and a close up, focused photo of the setup.

If that is not sufficiently effective, an extremely sharp, active notch filter is easy to make and can be added after the input circuit.

2 Likes

I have no experiience with this library, I prefer to save the data to an SD and evaluate with python.

Is this the only test you have done with your hardware?

const float samplingFrequency = 50000;  
//Hz, must be less than 10000 due to ADC (AVR only???)  Nrf52 ADC sampling can go as high as 35 khz or more

Is sounds odd.

Why are you sampling at 50 khz?
Nyquist is only valid for ideal systems.
https://www.google.com.ar/search?q=sampling+rate+for+oscilloscopes+technical+note+application

I suggest read the first 4-5 links.

The RC filter you suggest has a -3dB attenuation at 500 Hz. If the problem is 50 Hz calculate the filter for 100~200 Hz and test it (you can change resistors easily, dont know why you ask instead of trying it). You should take into account the output impedance of your source.

I suggest reading about FFT and how it works.
Its obvious that more samples provide more information on lower frequencies, that's how it works. No one could expect the opposite.

About getting double the frequency of the sirgnal is odd,
Why is that you want the major peak? Specially since it wont work. Get all the data bins, and exclude the one at 60 Hz, then print the highest. There are enought examples for this in the github link you provided.

Another option could be a notch filter, but it might be more complicated than just removing the 50/60hz bin from the fft results.

pd. you said that the 60 hz is detected properly, but your burst signal is doubled. That means your logic to detect peaks might be wrong.

Thank you both for the replies and the advice. I am a hobbyist with some (huge) gaps in my knowledge and I end up going down rabbit holes often (at times for days), but eventually always able to take some small steps ahead after reading up and researching the issue. However, not having a formal background in electronics at times eventually brings me to a standstill.

Let me explain the setup details. I am trying to understand the hardware and coding requirements for a speed chronograph that measures nerf/baseball/archery/pellets/etc. To help with that, I checked the signal circuit of such a setup that I found online (low speed) link to project HERE ...and of a small 24GHz radar device that I took apart which has a near identical circuit. I managed to get a good idea of the signal-circuit minus the capacitors (which I was not able to measure because they are physically on the PCB).

Here is the radar device's signal circuitry that feeds into the nrf52840 MCU's SAADC. I am taking my input signal from C36 right before pin AN0. It connects to my nrf52840's ADC A0 pin without any conditioning. I just tried to make a high-pass filter by using a 9M Ohm R and a 33pf C. But not even budging to take any readings anymore. But with these values I can play around, but I am concerned that my resistor selection may attenuate (kill) the signal's input current to my MCU.

I trigger the device with small aluminum foil balls that are being read at about 1-3 feet ahead of the radar within about a 12mm -24mm window gap (this gap is based on the freq vs speed measured).

I think understand the major FFT principles, but I would not dare to elaborate on the actual math behind it. But this posted code prints out all the measured freq bins and corresponding magnitude next to it. And at the end it prints the major magnitude's corresponding freq. The dominant freq to actual signal freq ratio of 2.2 x varied with setting the setting of the sampling rate Hz. For example at 50khz sampling it is 2.22 ratio and at 35khz it was 1.44 ratio. Which makes sense since 50khz/35khz is a ratio of 1.4, but obviously not correct for the FFT.

I have done 40 + different tests on this setup with 10Khz to 60Khz sampling rate and 64 to 4096 sampling sizes. But only the last 10 or so make sense. That is because I placed my 1V software threshold preciously in the wrong location in the code. It worked with smaller values like 64-256 , but not with 2048-4096. Then I realized that I must not have allowed the FFT to run a full cycle, so placed the trigger where it is now.

The current FFT cycle as I see it:

  • Radar sensor input signal at rest is 0-300mV and the dual op am puts out 2.5V at no reading

  • When radar sensor reads object, signal goes "negative" and positive from 2.5VDC at the range of 0V to 5V DC. Then after the dual op amp the DC component is removed and becomes AC before going into device MCU ADC pin at about 0-2.5VAC. I assume the device ADC runs at 200-250Khz. I think the other voltage divider ADC input is for providing an actual reference voltage based on actual device environmental values for accurate ADC calculation.

  • I take my signal from C36 of the device into A0 pin of my nrf52840. The resting signal value is about 0mV to 60mV/120mV. I set the trigger at 1V (1000 on the scale of 2400mV) in the code as not to always run the FFT when no object is sensed. When the object is sensed, the signal goes up to 2.4V in the wave-from shape I posted previously. Is set the ADC at 2.4V INTERNAL REF.

  • Upon the 1V trigger, the FFT starts a reading for the set "sampling_period_us", which in my case at 50Khz rate is 20us => 1sec*(1.0/50Khz). Is 20us enough for reading 4096 samples? I was checking and depending on the FFT type, the ADC cycle could be anywhere between 28us and 300us?! This part is not clear for me....

  • Next an array loop puts Real and Image readings into two separate arrays. The Imaginary values are zeroed since I have only real input data.

  • Then these arrays are worked upon by the FFT code into a window type math "FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward)", then calcukates magnitude on each with FFT.complexToMagnitude(); and then goes to loop "PrintVector" with "SCL-FREQUENCY", which then places them into frequency bins that are 12.2 Hz wide (in my case at 50Khz/4096) . Then the FFT "majorpeak" goes through the array readings and picks the major value of the magnitude readings, and prints the matching frequency value.

EDIT: when I used small sampling sizes like 64, 128 (at 50Khz rate), the 60hz main interference did not dominate the results. And I got 3-4 "final" major-peak reading results (like 6060hz, 6280hz, , 6790hz, 6350hz) in one reading cycle vs single result for the 2048 and 4096 size samples. So there is something with the sampling time period.

EDIT: for testing only, I am sampling at 50Khz rate even tough I know it should be about 2.5 x freq sampled, because I am only able to put in up to about a 10 Khz signal at the current setup. But eventually I want to sample 25 Khz frequencies at minimum ...and hopefully around 75 khz max at the 200 ksps max ADC speed of the Nrf52 MCU. For more accuracy at 10 Khz max input freq, I could sample at lower rate s like 25 Khz,

Here is the op amp circuit (I linked prior) that is near identical to the radar device's op amp circuit. But the radar device is set for 40us period / 25Khz max freq , while this one is for low-speed only:

Here are some reasons why zeroing or substituting mean values for FFT bins is not a good idea, as it will skew the results. :
matlab - Why is it a bad idea to filter by zeroing out FFT bins? - Signal Processing Stack Exchange

Ok.
So, you already have a filtered signal.
Those 2 op amps are band pass filters.

I would never choose this configuration, 1 2nd order low pass and 1 2nd order high pass filter worked better for me.
You can easily simulate that circuit and choose R and C for each op amp (they should be the same values). You dont need to add any extra circuitry.

The change of the ratio and the sampling frequency is due to aliasing.
Fiind a sampling frequency that works. It should be below those 35khz.
Sampling 25 khz with these libraries seems imposible.
10k sampling rate works ok at detecting this signal? Then go up until it is not showing the same result.

  • Then these arrays are worked upon by the FFT code into a window type math "FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward)", then calcukates magnitude on each with FFT.complexToMagnitude(); and then goes to loop "PrintVector" with "SCL-FREQUENCY", which then places them into frequency bins that are 12.2 Hz wide (in my case at 50Khz/4096) . Then the FFT "majorpeak" goes through the array readings and picks the major value of the magnitude readings, and prints the matching frequency value.

Now you understand why increasing samples provides low frequency information. You can just remove the bins corresponding to 60 hz (bin 5) and do your thing.

1 Like

Thank you for the information and the advice. I think aliasing is correct, makes the most sense, thank you.

What libraries would you recommend that I can use up to about 25 khz freq sampling (preferably 75-100 khz freq sampling) ? At the 200 ksps max ADC speed.

How do I remove the first few bins? Python is not not yet an option, so I need to do it in C++/Arduino. I do not need any measurements done below about 500hz.

I tried this but did not work:


for (int i = 0; i < samples; i++) {  //sampling into real and img arrays

      vReal[i] = (analogRead(CHANNEL) * y);  //
vReal [0] = 0;
vReal [1] = 0;
vReal [2] = 0;
vReal [3] = 0;
vReal [4] = 0;
vReal [5] = 0;
      vImag[i] = 0;

      while (micros() - microseconds < sampling_period_us) {  //  sampling for ex: 1M/(1.0*256) = 28.5us period of time ,
        //empty loop
      }
      microseconds += sampling_period_us;
    }

Then tried to start sampling from bin "5" vs bin "0":


for (int i = 5; i < samples; i++) {  //sampling into real and img arrays

      vReal[i] = (analogRead(CHANNEL) * y);  //
      vImag[i] = 0;

      while (micros() - microseconds < sampling_period_us) {  //  sampling for ex: 1M/(1.0*256) = 28.5us period of time ,
        //empty loop
      }
      microseconds += sampling_period_us;
    }

Reduced to 35 Khz sampling rate and 1024 samples and several input signals in a row were near 3 Khz from oscope reading. With the code below, managed to zero out the first few vReal time (data) , but the VReal real value bins up to 500 hHz had data and the 60 Hz range still dominated in the final FFT magnitude bins (see below).

I think I need to do a separate array that takes the final FFT array values and removes the first bins up to 500 hz and recalculates with FFT again. But having zeros in the first bins up to 500 Hz may itself skew the FFT calculations?

Data: ///////////////////////////////////////////////////////////////////////////
0.000000 0.0000
0.000029 0.0000
0.000057 0.0000
0.000086 0.0000
0.000114 0.0000
0.000143 0.0000
0.000171 0.0000
0.000200 0.0000
0.000229 321.0000
0.000257 310.0000
0.000286 517.0000
more....

Computed Real values: ////////////////////////////////////////////////////////////
0.000000 0.0000
1.000000 -464.2968
2.000000 121.9234
3.000000 174.9818
4.000000 35.3484
5.000000 178.2709
6.000000 135.9259
7.000000 63.7519
8.000000 98.1158
9.000000 49.3528
10.000000 147.8689
11.000000 51.2993
12.000000 49.6438
more....

Computed magnitudes: ////////////////////////////////////////////////////////////////
0.000000 Hz 0.0000
34.179687 Hz 478.3456
68.359375 Hz 189.8398
102.539062 Hz 239.6245
136.718750 Hz 66.0268
170.898437 Hz 182.9503
205.078125 Hz 150.6798
239.257812 Hz 75.5146
273.437500 Hz 114.0072
307.617187 Hz 55.3450
341.796875 Hz 168.4355
375.976562 Hz 117.2507
410.156250 Hz 53.2529
444.335937 Hz 211.1889
478.515625 Hz 64.2841
more....

Dominant Hz 38.45

=======================================================================

For example, with a 2440 Hz signal input I got 51.81 Hz as the dominant freq,. But in the FFT calculated magnitude values 1093 hz and 2084 Hz were the only other values over 500. And the value nearest the actual signal was 2426 Hz, which was only 409, with several others in the array elsewhere higher.

Analog Read values to array vReal after zeroing first 5 bins:

0 0.00
1 0.00
2 0.00
3 0.00
4 0.00
5 0.00
6 869.00
7 656.00
8 0.00
9 0.00
10 670.00
11 175.00
12 209.00
more.....

Data: ///////////////////////////////////////////////////////////////////////////
0.000000 0.0000
0.000029 0.0000
0.000057 0.0000
0.000086 0.0000
0.000114 0.0000
0.000143 0.0000
0.000171 869.0000
0.000200 656.0000
0.000229 0.0000
0.000257 0.0000
0.000286 670.0000
0.000314 175.0000
more....

Computed Real values: ////////////////////////////////////////////////////////////
0.000000 0.0000
1.000000 -521.1390
2.000000 551.9224
3.000000 138.9371
4.000000 326.8387
5.000000 242.1161
6.000000 315.4440
7.000000 335.7718
8.000000 107.0707
9.000000 254.2885
10.000000 290.8521
11.000000 100.2404
12.000000 264.1083
more....

Computed magnitudes: ////////////////////////////////////////////////////////////////
0.000000 Hz 0.0000
34.179687 Hz 549.6006
68.359375 Hz 554.4081
102.539062 Hz 226.2080
136.718750 Hz 333.8550
170.898437 Hz 357.7789
205.078125 Hz 322.0607
239.257812 Hz 351.2221
273.437500 Hz 328.9492

more before....
2324.218750 Hz 150.4459
2358.398438 Hz 170.6266
2392.578125 Hz 116.1921
2426.757813 Hz 409.4838
2460.937500 Hz 167.7197
2495.117188 Hz 305.5883
2529.296875 Hz 283.6756
more after..


    /*SAMPLING*/
    microseconds = micros();  //start timer

    for (int i = 0; i < samples; i++) {  //sampling into real and img arrays

   if (i < 6)      {                     
  vReal[i] = 0;
  } else
{
      vReal[i] = (analogRead(CHANNEL) * y);  //
}

      vImag[i] = 0;

Set the "first few" bins of the transformed data to zero (both real and imaginary). If you perform the inverse transform on that, the original data will be reproduced, minus the contributions of those frequency components.

Thank you. I am reading up on the concept of Inverse FFT as of now. Looks like ArduinoFFT may not have iFFT built in. So I am looking at these possible solutions for now:

I also downloaded the FFT code from OpenMusicLabs you cited in your old post from 2018. Took care of some installing issues. Hopefully it will compile after I switch over from "LIN_OUT" to "analogRead" signal source. Is this library a better path ahead for my project vs. ArduinoFFT when using the NRF52 Cortex Arm MCU?:

Looks like ArduinoFFT may not have iFFT built in.

It does, but no examples are given in the library. For the inverse FFT, call .compute() with the argument "dir" (direction) = FFTDirection::Reverse

In arduinoFFT.h, three overloads of .compute() are defined. The first two are:

  void compute(FFTDirection dir) const;
  void compute(T *vReal, T *vImag, uint_fast16_t samples,
               FFTDirection dir) const;

The forward and reverse Fourier transforms use identical algorithms, with a change in one single sign (+ or -).

I'll write an example to test the inverse feature sometime tomorrow.

I also downloaded the FFT code from OpenMusicLabs

That is written in machine code for AVR-based Arduinos, and won't work on any other type of MCU.

Thank you very much for the information and your kind offer for doing the test. Hopefully, one such inverse FFT example may be included in the ArduinoFFT library in the future. Until tomorrow, I will digest the above information you posted in hopes of not having to have my hand held this much :slight_smile:

Will also revisit the physical high-pass filter issue and reading up on input/output impedance..

EDIT: I did an test with only two 4 inch wires connecting my MCU board to the radar device's PCB (of course still had the USB data/power cable to the laptop)...but removed all oscope connections, alligator clips, etc. Same 60 hz interference.

Then used a USB power battery bank to power the radar device (common grounds still connected between radar device and my MCU board), No change.

Then used a USB noise isolator plug-in for my MCU board USB power/data...no change.

I even turned off the lights (all LED) in the room, the TV, and still the 60 HZ. So the culprit must be the laptop's power supply. (I have not looked at the radar device's power filtering circuit yet)

One more convoluted option would be to power my MCU board via battery and use one GPIO pin for a serial-OUT to the laptop's USB port for the Arduino IDE....or to use the MCU's BLE to send the readings. I already have such working BLE code, but only for short data strings seconds apart.

I went back to the basics to check when the readings skew. I removed the trigger in the code that started the FFT if the signal level input into the ADC (INTRF 2.4V) went above 1V from the 60mV idle (2.4V max scale). Then moved that trigger down to the frequency section to print the frequency value if FFT result FREQ is above 200 Hz.

Then I fed the 1 Khz continuous 50% duty cycle signal from my oscope's AUX port, set the FFT sampling size to 4096 and tried these FFT sampling freq rates. Do not forget that the 60 Hz interference is still present, but looks like the continuous 1 Khz oscope signal suppresses it (albeit skews the results surely). So the FFT runs non-stop in a loop and processes FFTs static/idle input until I connect the 1 Khz oscope signal.

**Sampling Freq      Result in Hz**    
10,000                     997
20,000                     997
23,000                     986
24,000                    1005
25,000                     997
26,000                     990   
27,000                    1027   <= looks like alias for sure by this point
30,000                    1141
35,000                    1340
40,000                    1522
50,000                    1904  
100,000                   3804

So this hardware/software combo seems to be accurate upto 25 Khz ADC sampling rate. I know that the nRF52840 ADC is capable upto 200 Khz sampling rate, but that probably requires some register settings/Nordic specific coding...will deal with this later)

I noticed that the FFT result "idle frequency" (during no signal input at all, only "static" sensor output after op amps) is not always 60 Hz. It depends on the sampling rate and sampling freq settings.

**Sampling freq      Samples     FFT result at idle/no user input: i.e interference**
25,000               4096                    6.11 Hz
25,000               2048                   12.23 Hz 
25,000               1024                   24.50 Hz
25,000                512                   49.20 Hz
25,000                256                    98.20 Hz
25,000                128                  197-198 Hz
25,000                64                   398-404 Hz
20,000                128                      158 Hz

I also tried some high-pass hardware filters, but no avail.
Tried:
100 Ohm and 2.3uF (686 Hz cutoff)
470 Ohm and 2.3 uF (145 Hz cutoff)

They attenuated the signal by about 50% and 30%, but the FFT still picked up the 60 Hz interference' harmonics. These filters also blocked all higher frequency signals, at least at the 3 Khz level and below. Which does not make sense. (This filter taps into C36 capacitor on the two op amp circuit and then goes to pin A0 of my MCU)

Memo for self: I need to measure input and output impedance for the radar device and my MCU board after I read up on this topic.

You are applying a bipolar signal to the ADC input. That can not only damage the nRF52 but will create all kinds of unwanted harmonics.

I would concentrate on fixing your hardware problems and worry about the FFT later.

1 Like

Thank you for the input. The circuit you mentioned is part of an existing radar device that works. I connected another MCU board to C36 capacitor right before it enters AIN0 and that's where I am getting the signal from for my MCU board ADC input. (And made a common ground)

I just tried two other boards (Arm also) and loaded up the FFT code. Both boards were only on the USB cable from my laptop with the ADC pin floating without any connection to anything else like the radar or oscope, also no ground connected (only USB power, GND, data lines). And the FFT output result via USB serial from both boards was 12.20 Hz at 25,000 sampling rate/2048 sample size. It must be me laptop power supply 60 Hz harmonics. I need to check again, but at lower freq sampling rate and sample size I get exactly 60Hz. I will try another laptop later today.

At the end, the project will be on battery, but still surprising how the laptop power supply "bleeds" down via the USB port on my MCU board into the GPIO circuitry.

EDIT: I just tried it with only on my laptop battery with mains disconnected form the laptop, and I am getting the 12.20 Hz FFT output indicating interference. I changed sampling rate to 10,000 and 2048 and getting 4.9 Hz. Then changed to 10000 sampling at 64 size and now it's 161-163 Hz. At 256 it's 39-40 Hz. At 512 it's 19,20 Hz.

As I already pointed out that is a problem as that voltage goes negative.

I believe that the signal going to AIN0 is AC at capacitor C36, as I could not measure it with a multimeter set on DC. I was able to measure the DC signal at 2.5V right after the second op amp, but when reaching C36 had to switch to AC. On the oscope it shows as a 0-60mV slightly noisy signal, and when the sensor reads an object, it goes up to 2.4V in a sine wave (see posted oscope picture).

EDIT: I see what you mean. It goes down to negative 640mV. But I get FFT readings indicating interference even with my MCU board only connected via the USB cable to my laptop. Nothing is connected to the the radar device

EDIT: so my question is: I do not have the firmware code for the radar device, but I am using a near identical MCU setup. So the radar device must be dealing with the negative input via software, because there are no more passive components between C36 and the ADC pin. But the issue is not the FFT readings on the radar device, but on my MCU board. The FFT readings showing 60 Hz harmonics show up also when the two are NOT connected.

I see that is has a differential input that can probably handle negative voltages, your ADC cannot.