Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 07:45 29 Nov 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Raspbery Pi Pico FFT Routines

Author Message
asknik2022
Regular Member

Joined: 26/03/2022
Location: United Kingdom
Posts: 92
Posted: 08:17pm 05 Apr 2023
Copy link to clipboard 
Print this post

Has anybody created a FFT Code/prgram for creating audio spectrum using Raspberry pi pico with MMBASIC to display on a LCD TFT Display.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4255
Posted: 09:03pm 05 Apr 2023
Copy link to clipboard 
Print this post

Not specific for audio, but my mains monitor has 1 window showing the spectrum.
PicomiteVGA PETSCII ROBOTS
 
asknik2022
Regular Member

Joined: 26/03/2022
Location: United Kingdom
Posts: 92
Posted: 09:10pm 05 Apr 2023
Copy link to clipboard 
Print this post

Hi Volhout

Where can I find it
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4255
Posted: 09:19pm 05 Apr 2023
Copy link to clipboard 
Print this post

Not specific for audio, but my mains monitor has 1 window showing the spectrum.
PicomiteVGA PETSCII ROBOTS
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4255
Posted: 06:12am 06 Apr 2023
Copy link to clipboard 
Print this post

hi asknik,

This is the thread, but I realize, reading it, that I never published the code.
When I get the the installation where the monitor works(this weekend), I will get the code and add it.

The code reads ADC in bursts in alternating buffers (one fills, while the other is processed), and calculates RMS and FFT from the data buffers. Using MATH commands.

mains monitor

Volhout
Edited 2023-04-06 16:15 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4255
Posted: 04:43pm 06 Apr 2023
Copy link to clipboard 
Print this post

This is the code of the last version that used FFT (it is removed now).

'+-------------------------------------------------------------------+
'|                                                                   |
'|                        mains voltage monitor                      |
'|                                                                   |
'|                                                                   |
'| Version 0.11            05-09-2022        Volhout@thebackshed.com |
'+---------------------------------'+-------------------------------------------------------------------+

' using background logging from ADC pingpong-ing in arrays a() and b()
' pre-processing in interrupt routine to offload main from time critical
' functions.
' main handles display functions and file handling

' Version control
' 0.01    Initial version with RMS running in main
' 0.02    Version with RMS and PEAK in interrupt
' 0.03    Trend buffers, PEAK removed, math(sd a()) for RMS, ignore offset
' 0.04    Trend graph drawn and peak indicator shown
' 0.05    Show actual RMS value in top left screen
' 0.06    Show date$/time$, plot waveform
' 0.07    Add FFT screen
' 0.08    remove many variables, hard coded values
' 0.09    make trend graph a rolling graph
' 0.10    adapted cal value 230V, remove duplicates from list,2 wave periods
' 0.11    trend graph 5 hours

' to do
' calculate distortion
' write logs to SD
' detect events
' write events to SD

'---------------- definitions -------------------

'init time
get_time


'hardware parameters------------------------------------------
'
scale = 265.2'322.8           'transformer gain (tune)
'
'tune this value with your hardware---------------------------


'sampling core values
fm=50                   'intende mains frequency
n=512                   'samples multiple of 2 for fft
freq = 2000             'sampling frequency target = 40 samples per period
calcfreq=fm             'start value for frequency calculation

'adjust the sampling frequency so the sampling more or less
'lines up with the mains frequency to avoid resampling math
'this is not super accurate, but gets the accuracy within 0.5%
freq = freq*n/(n\fm*fm) 'corrected sampling freq to sync 50Hz
Print "sampling frequency set to :";freq;" Hz"

'trend buffer
'there are 2 buffers, 1 with actual data, one with display coordinates
m=Int(60*(freq/n))      'size collect samples for 60 seconds
'Print "trend buffer :";m
'm=4                     'dummy, for sampling every second
t=MM.HRes-2             'size trend plot data buffers


'display definitions

'trend graph
rmsColor=RGB(green)     'line color
rmsmin=210              'graph window in voltage
rmsmax=250
trendpeak=RGB(red)      'colors for peak
trendvaley=RGB(cyan)    'and valey
trp=1                   'trend display pointer pointer

'current value
curColor=RGB(white)     'text color

'event list
evColor=RGB(cyan)       'text color normal
evColorAlarm=RGB(red)   'text color alarm
AlarmLevel=230*1.04     'when to change color (230+4%)

'wave graph
wavColor=RGB(green)      'line color
wp=1                     'wave pointer

'fft graph
fftColor=RGB(green)      'line color
avg%=0                   'running average for mains frequency
measfreq=0               'frequency measured on GP8 from zero crossing detector


'memory
Option base 1
Dim a(n),b(n)              'sample ping pong buffers
Dim c(n),d(n)              'sample processing buffer
'Dim pk(m),rm(m)           'buffers for peak and rms values for 1 minute
Dim rmt(t),rmd(t)          'buffers for trend capture and trend display
tp=1                       'trend index pointer
ap=0                       'trend averaging pointer
Dim rmp(5)=(0,0,0,0,0)     'array with 5 peak values
Dim rmh$(5)                'string array with associated time/date
Dim rmdummy(5),rmindex%(5) 'help arrays for sorting

'open the ADC and start first conversion, from here on the ADC is
'restarted in the interrupt routine.
adcRange=3.3
ADC open freq,1,INT_RDY
ready=0:pingpong=1
ADC start a()

'connect zero crossing detector to GP6
SetPin gp8,fin              'use build in frequency measurment
Pause 1000                  'skip 1 measurement period
dummy=Pin(gp8)              'make sure there is a value

'main routine
CLS
Do
drawCurrent
drawTrend
drawList
drawWave
drawFFT
Pause 1000
Loop
End

Sub drawCurrent
'draw the outline
Box 1,1,158,78,1,RGB(white)
'write current value
Text 6,6,Left$(Str$(rms),5),"L",4,3,curColor
Text 79,48,"Vrms","C",1,2,curColor
End Sub

Sub drawList
'draw the outline
Box 1,80,158,78,1,RGB(white)
Text 6,84," VRMS      WHEN","L",1,1,RGB(yellow)

Math add rmp(),0,rmdummy()    'make a local copy
Sort rmdummy(),rmindex%()      'mak a index list

For i=1 To 5
j=rmindex%(i)
If rmp(j)>AlarmLevel Then
colr=evColorAlarm
Else
colr=evColor
EndIf
Text 6,156-(12*i),Left$(Str$(rmp(j)),6),"L",1,1,colr
Text 66,156-(12*i),rmh$(j),"L",1,1,colr
Next i
End Sub

Sub drawWave
'convert the analog data to display data
gain=-76/adcRange   'ADC values between 0 and 3.3V plot over 76 pixels
Math scale c(),gain,d() 'use d() to double buffer c()
Math add d(),76,d()

'find first zero crossing index wp (vWSize/2) typically wp < 40
'with f=50Hz and 2kHz sampling. The graphs will display phase aligned.
wp=0:th=76/2
Do
wp=wp+1
If wp > 50 Then wp=1:Exit
Loop Until ((d(wp) > th) And (d(wp+1) < th))

'and plot the graph roughly 2 periodes
For i=1 To 156/2 '156
'erase previous pixel by eraing a whole vertical line
Line 2*i+160,2,2*i+160,76,1,RGB(black)
Line 2*i+161,2,2*i+161,76,1,RGB(BLACK)
'Line i+161,2,i+161,76,1,rgb(BLACK)
'plot the new pixel/line
Line 2*i+160,d(i+wp-1),2*i+162,d(i+wp),1,wavColor
'Line i+160,d(i+wp-1),i+161,d(i+wp),1,wavColor
Next i
Box 161,1,158,78,1,RGB(white)
End Sub

Sub drawFFT
'draw the outline
Box 161,80,158,78,1,RGB(white)
'  Text 3*curHSz/2,3*curVSz/2,"FFT","C",1,1,RGB(cyan)
Math add c(),-adcRange/2,c()  'remove offset before fft
Math fft magnitude c(),d()    'do fft

'scale the graph to fit the window
fftMax=n*adcRange/8       'not sure why this is /8
gain=-58/fftMax           'pure sine has all n samples in 1 peak
Math scale d(),gain,d()   'use d() to double buffer c()
Math add d(),154,d()

'now plot the first n/2 samples or as much as fit the graph size
fftRange = Min(n/2,158)-3
For i=1 To fftRange
'erase previous pixel by eraing a whole line
Line i+162,94,i+162,155,1,RGB(black)
'plot the new pixel
Line i+161,d(i),i+162,d(i+1),1,fftColor
Next i

'find the highest peak (since d() contains plot values (screen origin is top
'so data is inverted) >= find min.
'dummy = Math(min d(),p%)    'find what sample represents the peak to align text
'If p% < n/2 And p% > 1 Then  'not calc error or offset
'  calcfreq=(127*calcfreq+(p%-1)/n*freq)/128
'  'Print "freq ";calcfreq;" Hz"
'EndIf
'Text 163+p%,83,Left$(Str$(calcfreq),4)+" Hz","L",1,1,fftColor

'alternative frequency indication through gp8
measfreq=(Pin(gp8)+avg%*measfreq)/(avg%+1)
avg%=Min(avg%+1,15) 'max 15 averages
Text 163,83,Left$(Str$(measfreq),4)+" Hz","L",1,1,fftColor

End Sub

'interrupt routine gets data from ADC and does pre-processing
'interrups happen every n/freq seconds
Sub INT_RDY
'Timer =0

'restart adc with new buffer
If pingpong Then
'start filling b() and process a()
ADC start b()
rms = Math(SD a())
'Math add b(),0,c()  'make a copy for wave and fft
Else
'start filling a() and process b()
ADC start a()
rms = Math(SD b())
Math add b(),0,c()  'make a copy for wave and fft
EndIf
pingpong=1-pingpong

'calculate rms
rms = scale * rms

'proces values in trend sample timeframe
rms_high = Max(rms, rms_high)
'rms_min = Min(rms, rms_min)
ap=ap+1
If ap=m Then
'fill circular buffer
rmt(tp)=rms_high
tp=(tp Mod t)+1
ap=0
rms_high=rms
EndIf

'check rms value against event list
If rms > Math(max rmp()) Then
'check if it is a milivolt duplicate
Local isnew=1
For i=1 To 5
If rmh$(i)=Left$(Time$,5)+" "+Left$(Date$,5) Then
rmp(i)=rms             'do not add a new entry, but update value old
isnew=0
EndIf
Next i
If isnew Then
dummy=Math(min rmp(),p%)  'remove the lowest in the list
rmp(p%)=rms
rmh$(p%)=Left$(Time$,5)+" "+Left$(Date$,5)
EndIf
'    Math v_print rmp()
EndIf

'Print Timer
'ready=1
End Sub


' Draw the trend graph at the lower half of the screen
' scale the trend buffer to the window defined
Sub drawTrend

'convert the rmt data to display data (rmd)
gain=76/(rmsmin-rmsmax)
Math add rmt(),-rmsmax,rmd()  'rmd is also a double buffer
Math scale rmd(),gain,rmd()
Math add rmd(),161,rmd()      'put the data in the window

'find the peaks in this data array
pk = Math(max rmt(),pp%)
mn = Math(min rmt(),pv%)

'and plot the graph starting from tp (the last data entered)
trp = tp
For i=2 To t
'erase previous pixel by erasing a whole vertical line
Line i,162,i,238,1,RGB(black)
'plot the new pixel
Line i-1,rmd(trp),i,rmd((trp Mod t)+1),1,rmsColor
trp=(trp Mod t)+1
'map indicators to rolling trend
Next i

'map indicatorss to rolling graph
pp%=((t+pp%-tp) Mod t)+1
pv%=((t+pv%-tp) Mod t)+1

'plot the peak in the graph
Line pp%,163,pp%,238,1,trendpeak
Line pv%,163,pv%,238,1,trendvaley
'write the value in place position depending location peak
If pp% < 160 Then
Text pp%+2,163,Left$(Str$(pk),5)+" V","L",1,1,trendpeak
Else
Text pp%-2,163,Left$(Str$(pk),5)+" V","R",1,1,trendpeak
EndIf
If pv% < 160 Then
Text pv%+2,227,Left$(Str$(mn),5)+" V","L",1,1,trendvaley
Else
Text pv%-2,227,Left$(Str$(mn),5)+" V","R",1,1,trendvaley
EndIf

'draw box
Box 1,161,319,118,1,RGB(white)
End Sub

Sub get_time
'debug not needed with OPTION RTC AUTO ENABLE
'Time$="10:10:10"
'Date$="20-10-2010"
Exit Sub

'for manual entry
Local string dummy$
Input "Set time HH:MM:SS ";dummy$
Time$ = dummy$
Input "Set date DD-MM-YYYY ";dummy$
Date$ = dummy$
End Sub




This is a 512 sample FFT
The key parts are :

Get ADC samples

'open the ADC and start first conversion, from here on the ADC is
'restarted in the interrupt routine.
adcRange=3.3
ADC open freq,1,INT_RDY
ready=0:pingpong=1
ADC start a()


'interrupt routine gets data from ADC and does pre-processing
'interrups happen every n/freq seconds
Sub INT_RDY

'restart adc with new buffer
If pingpong Then
'start filling b() and process a()
ADC start b()
Math add b(),0,c()  'make a copy for wave and fft
Else
'start filling a() and process b()
ADC start a()
Math add b(),0,c()  'make a copy for wave and fft
EndIf
pingpong=1-pingpong

End Sub



Create FFT:

Sub drawFFT
'draw the outline
Box 161,80,158,78,1,RGB(white)
'  Text 3*curHSz/2,3*curVSz/2,"FFT","C",1,1,RGB(cyan)
Math add c(),-adcRange/2,c()  'remove offset before fft
Math fft magnitude c(),d()    'do fft

'scale the graph to fit the window
fftMax=n*adcRange/8       'not sure why this is /8
gain=-58/fftMax           'pure sine has all n samples in 1 peak
Math scale d(),gain,d()   'use d() to double buffer c()
Math add d(),154,d()

'now plot the first n/2 samples or as much as fit the graph size
fftRange = Min(n/2,158)-3
For i=1 To fftRange
'erase previous pixel by eraing a whole line
Line i+162,94,i+162,155,1,RGB(black)
'plot the new pixel
Line i+161,d(i),i+162,d(i+1),1,fftColor
Next i

'find the highest peak (since d() contains plot values (screen origin is top
'so data is inverted) >= find min.
dummy = Math(min d(),p%)    'find what sample represents the peak to align text
If p% < n/2 And p% > 1 Then  'not calc error or offset
 calcfreq=(127*calcfreq+(p%-1)/n*freq)/128
 'Print "freq ";calcfreq;" Hz"
EndIf
Text 163+p%,83,Left$(Str$(calcfreq),4)+" Hz","L",1,1,fftColor

End Sub

Edited 2023-04-07 02:47 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Amnesie
Guru

Joined: 30/06/2020
Location: Germany
Posts: 396
Posted: 08:40pm 06 Apr 2023
Copy link to clipboard 
Print this post

Hi Volhout,

interestingly enough, I searched for your mains monitor program a few days ago but couldn't find it because I forgot how it's called (mains monitor obviously!).

I would like to build this project, too. It is really really interesting. I saw in your linked topic a schematic; is it still "up-to-date"?

Why is the FFT removed in your latest version? I am just curious  

Will there be another topic (or a ressurection of the old one) with your latest program code? Questions over questions, but it is such a nice little device, which I would like to build after your original draft, since Netherlands and Germany have the same mains voltage.  

Greetings
Daniel
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 4255
Posted: 08:59pm 06 Apr 2023
Copy link to clipboard 
Print this post

Hi Daniel,

I created the mains monitor full of idea's of analysis tools. But after using it for few months I decided the fft was not used at all, and I would like to have more information about minimum and maximum voltages. Maybe I would look at the THD (harmonic distortion, or "klirrfactor"), that could be calculated from FFT, but the graph did not say much.

I also thought about registering (log files) ADC samples of each peak, so I could reconstruct what happened. But I did not look at it ever.

So the mains monitor changed. Simpler, and printing trend files (BMP's) to SD card. That gives far more insight in far less time.

No more FFT's, no log files with samples, no long lists of events, simply only trend graphs.

The hardware (the opamp circuit) is still used. It gives less distortion. Remember when you build the opamp circuit that the 2 input resistors provide the isolation from the mains. They are high voltage resistors, not the common 1/4 watt carbo resitors. Since typical 1/4 watt resistors are 250V max, you should at least put 4 normal resistors in series to achieve the same safety.

Volhout
Edited 2023-04-07 07:07 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Amnesie
Guru

Joined: 30/06/2020
Location: Germany
Posts: 396
Posted: 09:23pm 06 Apr 2023
Copy link to clipboard 
Print this post

Hi Volhout,

thanks for your response! I think you are right. FFT is really nice but as you wrote, it is better to have some hard facts like minium / max voltages etc. Following the mains trend via images on sd card sounds good.

Since I am originally coming from the tube amp world I am somewhat experienced with high voltage. I built a lot of tube amplifiers (but not Hifi, I am a guitar player), so I have all high voltage resistors. My geiger counter uses 10M ohms on the geiger tube, too.

  Quote  When I get the the installation where the monitor works(this weekend), I will get the code and add it.


It would be nice, if you'd keep us updated how things are going. I definitly plan to build it!  

Greetings
Daniel
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024