Maker Pro
Maker Pro

Sallen Key Low Pass Filter, bandpass filter

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
so Ive managed to make this shape on the filter wizard app I've got part of the puzzle just trying to link them now. If I use the LM3914 what cut off frequencies should I use, and how do I translate the peak of the graph to the highest analog voltage that the LM3914 to light the LED's in a bar graph type thanks
1700072664432.png

1700073032474.png
1700073079160.png
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
I want to coincide the peak with the lm3914 input and want the bar graph type LED's to rise and deminish as it goes up and down again this peak is 32Hz
 

danadak

Feb 19, 2021
677
Joined
Feb 19, 2021
Messages
677
What is your architecture right now of choice ?

if you have 16 bands you need 16 filters, and with 20 leds / band thats 2 LM3914 / band
or 32 LM3914. So 48 parts so far, and a ton of passives.

You sure this is the way you want to go ? Versus one part doing a FFT with 16 bins,
(thats software setup of FFT) and muxing the display for drivers ?

To ans your ques your mic puts out a V, a peak value, and that passes thru filter
to produce a corresponding V at its output. That peak V is then fed to the 2
cascaded LM3914s and should be of a value that lights the whole chain. Data
sheet tells you how to set them for that, eg. the V range.

Regards, Dana.
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
well I'm not sure, your correct though it would be
16 bands you need 16 filters, and with 20 leds / band thats 2 LM3914 / band
or 32 LM3914. So 48 parts so far, and a ton of passives.
but a man said that...

On the KISS principal I still think keeping it analogue is the way to go. Sure, try doing it in code and spend weeks/months getting it right and frustration at not being able to identify the problems..... coding a filter? then coding charlieplex of whatever.....???

so OK I should be looking at the data sheet and the V range. I am considering the FFT if you could delve a little more into the mechanics of getting it to work that way, thank you!
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
here's the code that I want to modify

C:
// (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];
 }
 }
 }
 }

I'm not using CHIPSET WS2812B
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
the code I posted has the frequency bins you were on about, I know what multiplexing (muxing) is, but I have no idea of how to implement it in the code, see what you can come up with, thanks!
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
danadak an you help? the archetecture I think you are on about is the esp23 dev board
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
so i'm on the data sheet for the LT6220 and I cant find the voltage range

1700212836055.png
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
actually I think its this part
Input Offset Voltage: 350μV Max
Open-Loop Gain: 100V/mV Typ
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
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.

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

View attachment 61439

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.

View attachment 61440

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.
ok i get what "work backwards" means now
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
what is magnitude v/v and magnitude db and how do they relate to voltage range
 
Last edited:

bertus

Moderator
Nov 8, 2019
3,156
Joined
Nov 8, 2019
Messages
3,156
Hello,

Here are some screenshots as example for the 32 Hz filter:
BP_filter_response.png
BP_filter_circuit.png
Bertus
 

Maglatron

Jul 12, 2023
729
Joined
Jul 12, 2023
Messages
729
I need a little more explanation can you put the pictures into paint and annotate the drawing please?
 
Top