Maker Pro
Maker Pro

Sallen Key Low Pass Filter, bandpass filter

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
because in the example I have it shows me how to derive the dBm from a starting point p, and p is in milli-watts
but in the question at the end of the video it asks to add p1 to p2 and their units are dBm
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,752
Joined
Nov 17, 2011
Messages
13,752

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
Depends entirely on your application: For audio 20 kHz is a high frequency, for radio 20 kHz is an extremely low frequency
well I want audio so I'm going to work out the equation that is in the pdf Narrow (Single Frequency) Band Pass
what I was asking for is to know which capacitor to use!
 

Harald Kapp

Moderator
Moderator
Nov 17, 2011
13,752
Joined
Nov 17, 2011
Messages
13,752
which capacitor to use!
You may have to try different values such that the resistors as well as the capacitors result in meaningfull values (e.g. no caps < 100pF or > 1µF, no resistors < 100 Ohm or > 1 MOhm). These are not fixed values, just rough guidelines.

Have a look at this site. It explains the parameters of a band pass filter, how to design one and even has a calculator that helps you select components for the parameters you select.
 

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
You may have to try different values such that the resistors as well as the capacitors result in meaningfull values (e.g. no caps < 100pF or > 1µF, no resistors < 100 Ohm or > 1 MOhm). These are not fixed values, just rough guidelines.
thanks
 

danadak

Feb 19, 2021
780
Joined
Feb 19, 2021
Messages
780
Ok back to basics how do gain in decibels relate the the Narrow (Single Frequency) Band Pass. how do I decide how many dB (how much gain) or change in amplitude and why?

This question has led, over the years, to thousands of ap notes and books on that very subject.

Ideally one wants a single frequency (many applications) to pass thru a BPF type filter and all other
freq content suppressed. Thats the classic "brick wall filter" so often pursued.

1699451404018.png
But thats difficult to achieve, especially with the approach you are taking with linear
filters, due to tolerances, noise, etc.. So you make compromises

1699451497896.png

Now we have freqs coming thru filter we dont want, because its BW is not virtually
zero, and its response slope not vertical. Not to forget we have a non linear ear
response to deal with.

1699451829745.png

So how do we answer the question ? Trial and error, and/or set realistic goals.
The LM3914 is linear in its V to LEDs on relationship, so we supply it with a V
from a filter, max voltage = max loudness you want. And the gain settings thru
use of reference V in the LM3914. So I would set LM3914 for 5V all LEDs on.
So thats max V we want out of filter. Now you have a microphone with its own
freq response so you can either use one design G for all filters, then attenuate
each one with a R divider to compensate for the combination of mic and ear.
Note I have not added speaker translation to ear non flat freq response.....

To the point, so you start at the end of the signal chain of waht you desire,
and set max amplitude to 5V (trial setting from above) and work backward
to figure out what G you need each element in chain to get to that.

mic >> ear >> filter >> LM3914, a G associated with each, and corrections
for each elements non flat freq response.

Note you will have a huge breadboard, tons of wiring, if you do it this way
for 30 channels if you use linear filters. A FFT approach can be done in one
part, including LED drive, all filters (the FFT freq bins that occurs as FFT
output). Just a thought.



Regards, Dana.
 
Last edited:

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
I want the frequencies coming from 3.5 jack not mic but thanks for the fantastic responce also how do you set
set max amplitude to 5V
Does that mean that the when at 5v then the 30 leds will light in each band?
 

Attachments

  • matrix led180.png
    matrix led180.png
    698.5 KB · Views: 0

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
then attenuate
each one with a R divider to compensate for the combination of mic and ear.
Note I have not added speaker translation to ear non flat freq response.....
either use one design G for all filters, then attenuate
each one with a R divider to compensate for the combination of mic and ear.
is G the gain? what do I do with the R divider and whats the part about
To the point, so you start at the end of the signal chain of waht you desire,
and set max amplitude to 5V (trial setting from above) and work backward
to figure out what G you need each element in chain to get to that.
how do I work backward I don't understand you fully!
Is the bottom line trial and error?
 

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
I have posted in my other thread because I was advised to, its the spectrum analizer one please have a look!
 

danadak

Feb 19, 2021
780
Joined
Feb 19, 2021
Messages
780
G - gain.

Yes trial and error with the following caveats :

One can start at either end of the chain, and modify elements in chain
to suit. One overriding considerations is when considering noise
taking the most G at first element in chain is prudent for S/N, signal/
noise considerations.


But I do not think in this case one has to be concerned, just aware
of a general principle.

If we take the max value needed at end of chain, and divide that by
max value that occurs at beginning we get G needed. The idea is
we proceed in a method that minimizes G needed to get minimal
amplification of noise and other effects due to G, like sensitivity
to passive and active elements. Ideally we would like the mic to
deliver 0 - 5V (in this case) so filter G = 1 = low sensitivity in filter.
But that generally means we have to amp mic so either do it as a
preamp to filter or in the filter itself. Tradeoffs, always tradeoffs.

So now, in our case, we have Gmic >> Gfilter >> GLM3914. So filter
wise large G impies high sensitivity to component changes -


The R divider is if your choices wind up with too much G then throw it away
with a attenuator, a R divider. So you dont overdrive the signal chain or the
LM3914.

So if you take your G in the filter then you are trading off noise, sensitivity
of the filter to its element changes, etcc.

"Normally one does Sallen Key at G= 1, and if that is an issue a modified
SK but with G > 1. You can compute all the parameters from s plane analysis,
or just use on web the equations, to design filter (my link above). But we nerds
are attention span deficit people (largely in my humble opinion) so use software
to do the filter. Several utilities on web -





Regards, Dana.
 

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
so I think the code is fine, I should use the LM3914 and the pwm pins of the esp32 dev board (there are 16) each pin to represent a column and band width and set the max signal of the LM3914to 5V so that means that when the signal is at 5v, all the LED's in that column would lit. so I have this code and it compiled fine for the esp32 dev board and I'm not using WS2812B LED strip, I am using bar graph type LED's, can you help me modify the code for my needs and explain please!? with 16 columns and 20 rows (instead of 30) thats 320 LED's! but I need the bar graph type because I want a certain "look", thank you! also I can change the amplitude to make the columbs go up to the desired 20 LED's
So I would set LM3914 for 5V all LEDs on.
So thats max V we want out of filter

20 leds will light in each band? when the pwm signal is 5v

// (Heavily) adapted from https://github.com/G6EJD/ESP32-8266-Audio-Spectrum-Display/blob/master/ESP32_Spectrum_Display_02.ino
// Adjusted to allow brightness changes on press+hold, Auto-cycle for 3 button presses within 2 seconds
// Edited to add Neomatrix support for easier compatibility with different layouts.
#include <FastLED_NeoMatrix.h>
#include <arduinoFFT.h>
#include <EasyButton.h>
#define SAMPLES 1024 // Must be a power of 2
#define SAMPLING_FREQ 40000 // Hz, must be 40000 or less due to ADC conversion time. Determines maximum frequency that can be analysed by the FFT Fmax=sampleF/2.
#define AMPLITUDE 1000 // Depending on your audio source level, you may need to alter this value. Can be used as a 'sensitivity' control.
#define AUDIO_IN_PIN 35 // Signal in on this pin
#define LED_PIN 5 // LED strip data
#define BTN_PIN 4 // Connect a push button to this pin to change patterns
#define LONG_PRESS_MS 200 // Number of ms to count as a long press
#define COLOR_ORDER GRB // If colours look wrong, play with this
#define CHIPSET WS2812B // LED strip type
#define MAX_MILLIAMPS 2000 // Careful with the amount of power here if running off USB port
const int BRIGHTNESS_SETTINGS[3] = {5, 70, 200}; // 3 Integer array for 3 brightness settings (based on pressing+holding BTN_PIN)
#define LED_VOLTS 5 // Usually 5 or 12
#define NUM_BANDS 16 // To change this, you will need to change the bunch of if statements describing the mapping from bins to bands
#define NOISE 500 // Used as a crude noise filter, values below this are ignored
const uint8_t kMatrixWidth = 16; // Matrix width
const uint8_t kMatrixHeight = 16; // Matrix height
#define NUM_LEDS (kMatrixWidth * kMatrixHeight) // Total number of LEDs
#define BAR_WIDTH (kMatrixWidth / (NUM_BANDS - 1)) // If width >= 8 light 1 LED width per bar, >= 16 light 2 LEDs width bar etc
#define TOP (kMatrixHeight - 0) // Don't allow the bars to go offscreen
#define SERPENTINE true // Set to false if you're LEDS are connected end to end, true if serpentine
// Sampling and FFT stuff
unsigned int sampling_period_us;
byte peak[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // The length of these arrays must be >= NUM_BANDS
int oldBarHeights[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int bandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime;
arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
// Button stuff
int buttonPushCounter = 0;
bool autoChangePatterns = false;
EasyButton modeBtn(BTN_PIN);
// FastLED stuff
CRGB leds[NUM_LEDS];
DEFINE_GRADIENT_PALETTE( purple_gp ) {
0, 0, 212, 255, //blue
255, 179, 0, 255 }; //purple
DEFINE_GRADIENT_PALETTE( outrun_gp ) {
0, 141, 0, 100, //purple
127, 255, 192, 0, //yellow
255, 0, 5, 255 }; //blue
DEFINE_GRADIENT_PALETTE( greenblue_gp ) {
0, 0, 255, 60, //green
64, 0, 236, 255, //cyan
128, 0, 5, 255, //blue
192, 0, 236, 255, //cyan
255, 0, 255, 60 }; //green
DEFINE_GRADIENT_PALETTE( redyellow_gp ) {
0, 200, 200, 200, //white
64, 255, 218, 0, //yellow
128, 231, 0, 0, //red
192, 255, 218, 0, //yellow
255, 200, 200, 200 }; //white
CRGBPalette16 purplePal = purple_gp;
CRGBPalette16 outrunPal = outrun_gp;
CRGBPalette16 greenbluePal = greenblue_gp;
CRGBPalette16 heatPal = redyellow_gp;
uint8_t colorTimer = 0;
// FastLED_NeoMaxtrix - see https://github.com/marcmerlin/FastLED_NeoMatrix for Tiled Matrixes, Zig-Zag and so forth
FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(leds, kMatrixWidth, kMatrixHeight,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG +
NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS);
void setup() {
Serial.begin(115200);
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalSMD5050);
FastLED.setMaxPowerInVoltsAndMilliamps(LED_VOLTS, MAX_MILLIAMPS);
FastLED.setBrightness(BRIGHTNESS_SETTINGS[1]);
FastLED.clear();
modeBtn.begin();
modeBtn.onPressed(changeMode);
modeBtn.onPressedFor(LONG_PRESS_MS, brightnessButton);
modeBtn.onSequence(3, 2000, startAutoMode);
modeBtn.onSequence(5, 2000, brightnessOff);
sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQ));
}
void changeMode() {
Serial.println("Button pressed");
if (FastLED.getBrightness() == 0) FastLED.setBrightness(BRIGHTNESS_SETTINGS[0]); //Re-enable if lights are "off"
autoChangePatterns = false;
buttonPushCounter = (buttonPushCounter + 1) % 6;
}
void startAutoMode() {
autoChangePatterns = true;
}
void brightnessButton() {
if (FastLED.getBrightness() == BRIGHTNESS_SETTINGS[2]) FastLED.setBrightness(BRIGHTNESS_SETTINGS[0]);
else if (FastLED.getBrightness() == BRIGHTNESS_SETTINGS[0]) FastLED.setBrightness(BRIGHTNESS_SETTINGS[1]);
else if (FastLED.getBrightness() == BRIGHTNESS_SETTINGS[1]) FastLED.setBrightness(BRIGHTNESS_SETTINGS[2]);
else if (FastLED.getBrightness() == 0) FastLED.setBrightness(BRIGHTNESS_SETTINGS[0]); //Re-enable if lights are "off"
}
void brightnessOff(){
FastLED.setBrightness(0); //Lights out
}
void loop() {
// Don't clear screen if waterfall pattern, be sure to change this is you change the patterns / order
if (buttonPushCounter != 5) FastLED.clear();
modeBtn.read();
// Reset bandValues[]
for (int i = 0; i<NUM_BANDS; i++){
bandValues = 0;
}


// Sample the audio pin
for (int i = 0; i < SAMPLES; i++) {
newTime = micros();
vReal = analogRead(AUDIO_IN_PIN); // A conversion takes about 9.7uS on an ESP32
vImag = 0;
while ((micros() - newTime) < sampling_period_us) { /* chill */ }
}


// Compute FFT
FFT.DCRemoval();
FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
FFT.Compute(FFT_FORWARD);
FFT.ComplexToMagnitude();


// Analyse FFT results
for (int i = 2; i < (SAMPLES/2); i++){ // Don't use sample 0 and only first SAMPLES/2 are usable. Each array element represents a frequency bin and its value the amplitude.
if (vReal > NOISE) { // Add a crude noise filter


/*8 bands, 12kHz top band
if (i<=3 ) bandValues[0] += (int)vReal;
if (i>3 && i<=6 ) bandValues[1] += (int)vReal;
if (i>6 && i<=13 ) bandValues[2] += (int)vReal;
if (i>13 && i<=27 ) bandValues[3] += (int)vReal;
if (i>27 && i<=55 ) bandValues[4] += (int)vReal;
if (i>55 && i<=112) bandValues[5] += (int)vReal;
if (i>112 && i<=229) bandValues[6] += (int)vReal;
if (i>229 ) bandValues[7] += (int)vReal;*/


//16 bands, 12kHz top band
if (i<=2 ) bandValues[0] += (int)vReal;
if (i>2 && i<=3 ) bandValues[1] += (int)vReal;
if (i>3 && i<=5 ) bandValues[2] += (int)vReal;
if (i>5 && i<=7 ) bandValues[3] += (int)vReal;
if (i>7 && i<=9 ) bandValues[4] += (int)vReal;
if (i>9 && i<=13 ) bandValues[5] += (int)vReal;
if (i>13 && i<=18 ) bandValues[6] += (int)vReal;
if (i>18 && i<=25 ) bandValues[7] += (int)vReal;
if (i>25 && i<=36 ) bandValues[8] += (int)vReal;
if (i>36 && i<=50 ) bandValues[9] += (int)vReal;
if (i>50 && i<=69 ) bandValues[10] += (int)vReal;
if (i>69 && i<=97 ) bandValues[11] += (int)vReal;
if (i>97 && i<=135) bandValues[12] += (int)vReal;
if (i>135 && i<=189) bandValues[13] += (int)vReal;
if (i>189 && i<=264) bandValues[14] += (int)vReal;
if (i>264 ) bandValues[15] += (int)vReal;
}
}


// Process the FFT data into bar heights
for (byte band = 0; band < NUM_BANDS; band++) {


// Scale the bars for the display
int barHeight = bandValues[band] / AMPLITUDE;
if (barHeight > TOP) barHeight = TOP;


// Small amount of averaging between frames
barHeight = ((oldBarHeights[band] * 1) + barHeight) / 2;


// Move peak up
if (barHeight > peak[band]) {
peak[band] = min(TOP, barHeight);
}


// Draw bars
switch (buttonPushCounter) {
case 0:
rainbowBars(band, barHeight);
break;
case 1:
// No bars on this one
break;
case 2:
purpleBars(band, barHeight);
break;
case 3:
centerBars(band, barHeight);
break;
case 4:
changingBars(band, barHeight);
break;
case 5:
waterfall(band);
break;
}


// Draw peaks
switch (buttonPushCounter) {
case 0:
whitePeak(band);
break;
case 1:
outrunPeak(band);
break;
case 2:
whitePeak(band);
break;
case 3:
// No peaks
break;
case 4:
// No peaks
break;
case 5:
// No peaks
break;
}


// Save oldBarHeights for averaging later
oldBarHeights[band] = barHeight;
}


// Decay peak
EVERY_N_MILLISECONDS(60) {
for (byte band = 0; band < NUM_BANDS; band++)
if (peak[band] > 0) peak[band] -= 1;
colorTimer++;
}


// Used in some of the patterns
EVERY_N_MILLISECONDS(10) {
colorTimer++;
}


EVERY_N_SECONDS(10) {
if (autoChangePatterns) buttonPushCounter = (buttonPushCounter + 1) % 6;
}


FastLED.show();
}

// PATTERNS BELOW //

void rainbowBars(int band, int barHeight) {
int xStart = BAR_WIDTH * band;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
for (int y = TOP; y >= TOP - barHeight; y--) {
matrix->drawPixel(x, y, CHSV((x / BAR_WIDTH) * (255 / NUM_BANDS), 255, 255));
}
}

}

void purpleBars(int band, int barHeight) {
int xStart = BAR_WIDTH * band;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
for (int y = TOP; y >= TOP - barHeight; y--) {
matrix->drawPixel(x, y, ColorFromPalette(purplePal, y * (255 / (barHeight + 1))));
}
}

}

void changingBars(int band, int barHeight) {
int xStart = BAR_WIDTH * band;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
for (int y = TOP; y >= TOP - barHeight; y--) {
matrix->drawPixel(x, y, CHSV(y * (255 / kMatrixHeight) + colorTimer, 255, 255));
}
}

}

void centerBars(int band, int barHeight) {
int xStart = BAR_WIDTH * band;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
if (barHeight % 2 == 0) barHeight--;
int yStart = ((kMatrixHeight - barHeight) / 2 );
for (int y = yStart; y <= (yStart + barHeight); y++) {
int colorIndex = constrain((y - yStart) * (255 / barHeight), 0, 255);
matrix->drawPixel(x, y, ColorFromPalette(heatPal, colorIndex));
}
}

}

void whitePeak(int band) {
int xStart = BAR_WIDTH * band;
int peakHeight = TOP - peak[band] - 1;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
matrix->drawPixel(x, peakHeight, CHSV(0,0,255));
}

}

void outrunPeak(int band) {
int xStart = BAR_WIDTH * band;
int peakHeight = TOP - peak[band] - 1;
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
matrix->drawPixel(x, peakHeight, ColorFromPalette(outrunPal, peakHeight * (255 / kMatrixHeight)));
}

}

void waterfall(int band) {
int xStart = BAR_WIDTH * band;
double highestBandValue = 60000; // Set this to calibrate your waterfall


// Draw bottom line
for (int x = xStart; x < xStart + BAR_WIDTH; x++) {
matrix->drawPixel(x, 0, CHSV(constrain(map(bandValues[band],0,highestBandValue,160,0),0,160), 255, 255));
}


// Move screen up starting at 2nd row from top
if (band == NUM_BANDS - 1){
for (int y = kMatrixHeight - 2; y >= 0; y--) {
for (int x = 0; x < kMatrixWidth; x++) {
int pixelIndexY = matrix->XY(x, y + 1);
int pixelIndex = matrix->XY(x, y);
leds[pixelIndexY] = leds[pixelIndex];
}
}
}

}
 

Attachments

  • bar graph led.jpg
    bar graph led.jpg
    77.9 KB · Views: 1
  • 16x20led.jpg
    16x20led.jpg
    189.1 KB · Views: 1
Last edited:

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
ok thanks I would be using 3.5 jack >> filter >> LM3914 can you explain step one and I can work it out from there I think?, do you think that it's a good idea to control LM3914's columns with PWM from and esp32, if so how do you think I should go about it? the filters are omitted if I use that idea of using the FFT malarky and the esp32 I think that is the way to go personally just need to mesh it in with the arduino code, ie having the bands activate the different PWM pins, one pin for each column
 

Maglatron

Jul 12, 2023
1,478
Joined
Jul 12, 2023
Messages
1,478
Just trying to navigate the maze
 

Attachments

  • bar graph data.pdf
    102.6 KB · Views: 0
Top