• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Нужна помощь Работа с ADC (микрофоном)

unreg

Member
Доброго! Разбираюсь вот с этим примером ESP32 Voice Streamer - Hackster.io, и не могу понять как же все-таки работать с ADC... В статье автор говорит, что ADC принимает сигнал от 0-1v, поэтому нужен делитель, т.к. микрофон выдает от 0-2.25v. Почему ADC работает 0-1v, а не 0-3.3v? Как использовать флаг adc_atten_t ? Может им можно решить проблему без всяких делителей? Буду благодарен за объяснение "на пальцах" хотяб на моем примере:
ESP32-wrover-b .Есть микрофон, подключен к GPIO36. на OUT выдает 60 mV-2.45v (реально по даташиту). Нужно потоком читать с него сигнал (пусть 12 bit). Все питается от 3.3v
Если возможно - кусочек кода с пояснениями и схему хотя бы словами (нужен делитель или нет и почему). Спасибо!
 

Юрий Ботов

Moderator
Команда форума
По моему с АЦП вы в общем все правильно понимаете, а вот как работать с микрофоном - нет.
Для начала внимательно посмотрите на даташит и ретранслируйте сюда что там написано: вы говорите что
Это...
- пиковое напряжение ноль - пик? Vpeak
- пиковое напряжение пик - пик? V2peak = Vpeak*2
- действующее значение напряжения? Vd = Vpeak*0.707
- что то иное, китайское?
Причем...
- это измерено на максимуме АЧХ?
- это измерено на краю частотного диапазона?
- это среднее значение по диапазону?
Неравномерность в в частотном диапазоне у дешевых микрофонов реально достигает 6 дб. (Китайцы в бытовухе мыслят иными понятиями нежели наши радисты, у китайцев на крохотном приемнике может быть наклейка "200Wt!!!" и указание в инструкции, что потребляемая мощность не превышает 17 Вт...)

Разберитесь какая постоянная составляющая напряжения присутствует на выходе микрофона. Обычно конечно ноль, но не факт. Есть микрофоны которые питаются по сигнальному проводу, некоторые китайцы на активных (с усилителем) микрофонах не ставят на выходе конденсатор - "все равно на усилителе он есть"... а на АЦП его нет. Не забывайте что вам нужно будет "передвинуть" эту постоянную составляющую напряжения на середину диапазона АЦП, так что от пары резисторов точно никуда не денетесь.
 

unreg

Member
Спасибо за внимание к вопросу!
Вот даташит на микрофон: https://datasheets.maximintegrated.com/en/ds/MAX9814.pdf
Поскольку ориентровался на приведенную выше статью, обратил внимание вот на эту инфу:
MICOUT High Output Voltage - 2.45V и MICOUT Low Output Voltage - 3mV (в топе указал неверно)
Vpeak вообще не нашел, но нашел цифру 0.707 :)
На счет постоянной сигнала с микрофона это та постоянная относительно которой меняется уровень аналогового сигнала?
Где найти постоянную составляющую и самое главное - как применить эти знания относительно моей задачи?
Прада в теме с микрофонами у меня большой пробел.
 

Юрий Ботов

Moderator
Команда форума
Я правильно понимаю что вас есть некий динамический микрофон, в который встроена эта микросхема и вы хотите в него же встроить wifi?
У вас есть уже готовая плата микрофонного усилителя или вы ее будете делать самостоятельно?

по Даташиту: 2.45 это пик-пик - абсолютный неискаженный максимум. Это обусловлено тем что постоянная составляющая на выходе микросхемы = 1.23 вольта.
Так что максмиум ноль-пик = 1.225 В, а действующее значение примерно 0.866 В.

Получается интересная вещь: реальный диапазон измерений АЦП esp32 в режиме ADC_ATTEN_DB_11 как раз "150 to 2450mV". (Analog to Digital Converter — ESP-IDF Programming Guide v4.0-dev-402-ga20d02b7f documentation)

То есть, с определенной долей везения можно подключить вход АЦП непосредственно НА ВЫХОД микросхемы MICOUT, там уже присутствует нужное смещение. Не после конденсатора, а именно на выход микросхемы (можно выпаять конденсатор и вместо него поставить такой-же по габаритам резистор 10-100 Ом) . И все само-собой заработает. Понаблюдав за результатами работы возможно вы обнаружите что на сильном сигнале (микрофон близко ко рту) есть искажения. Но и тут возможно не потребуется особого согласования, поскольку у max9814 есть вход GAIN изменением напряжения на котором можно регулировать усиление. Попробуйте посадить его на "землю". Если уж и это не поможет - тогда придется городить внешний аттенюатор и цепь смещения по постоянному току...
 

Юрий Ботов

Moderator
Команда форума
У вышеописанного способа есть и недостаток: Реально вы теряете 1 бит АЦП: 1024 при таком подключении подразумевает 3.9 вольта, которые на самом деле подавать на вход нельзя. С внешним аттенюатором и цепями смещения можно избавиться от этой проблемы... Но сначала определитесь насколько она для вас страшна.
 

unreg

Member
У меня микрофон уже сидит на этой плате. ссылка али. А задача пока разобраться с ацп, пример с микрофоном интересен тем что в последствии собираюсь делать анализатор спектра только от линейного выхода. Попробую приготовить по Вашему рецепту ). raw буду писать на внешнюю флэшку, ну и попробую проиграть что получилось. Спасибо!
 

Алексей.

Active member
Тема интересная, подпишусь.
Анализатор мне не требовался, а вот sip звонилка для встраивания в СКУД пригодилась бы.
 

Юрий Ботов

Moderator
Команда форума
Для начала "классический" пример: espressif/esp-idf
Тут есть все включая встроенный аттенюатор (правда он устанавливается в 0 дБ - вам надо 11 дБ)
 

nikolz

Well-known member
Доброго! Разбираюсь вот с этим примером ESP32 Voice Streamer - Hackster.io, и не могу понять как же все-таки работать с ADC... В статье автор говорит, что ADC принимает сигнал от 0-1v, поэтому нужен делитель, т.к. микрофон выдает от 0-2.25v. Почему ADC работает 0-1v, а не 0-3.3v? Как использовать флаг adc_atten_t ? Может им можно решить проблему без всяких делителей? Буду благодарен за объяснение "на пальцах" хотяб на моем примере:
ESP32-wrover-b .Есть микрофон, подключен к GPIO36. на OUT выдает 60 mV-2.45v (реально по даташиту). Нужно потоком читать с него сигнал (пусть 12 bit). Все питается от 3.3v
Если возможно - кусочек кода с пояснениями и схему хотя бы словами (нужен делитель или нет и почему). Спасибо!
В описании по Вашей ссылке написано:
The chip at the heart of this amp is the MAX9814, and has a few options you can configure with the breakout. The default 'max gain' is 60dB, but can be set to 40dB or 50dB by jumpering the Gain pin to VCC or ground. You can also change the Attack/Release ratio, from the default 1:4000 to 1:2000 or 1:500. The ouput from the amp is about 2Vpp max on a 1.25V DC bias, so it can be easily used with any Analog/Digital converter that is up to 3.3V input. If you want to pipe it into a Line Input, just use a 1-100uF blocking capacitor in series (100uF sounds best).
Чип в основе этого усилителя-MAX9814 и имеет несколько параметров, которые вы можете настроить.
По умолчанию максимальное увеличение составляет 60 дБ, но может быть установлен на 40 дБ и 50 дБ при замыкании получить PIN-код к VCC и землей. Вы также можете изменить соотношение атаки/выпуска с 1:4000 по умолчанию до 1:2000 или 1:500.
Выход из усилителя составляет около 2vpp max при смещении постоянного тока 1,25 В, поэтому его можно легко использовать с любым аналого-цифровым преобразователем до 3,3 В.
Если вы хотите передать его в линейный вход, просто используйте конденсатор 1-100uF последовательно (100uF звучит лучше всего).
-------------------------------
вот здесь читаем:
Analog to Digital Converter — ESP-IDF Programming Guide v4.0-dev-402-ga20d02b7f documentation
Полномасштабное напряжение АЦП по умолчанию составляет 1,1 В. для считывания более высоких напряжений (до максимального напряжения pin, обычно 3,3 в) требуется настройка >0dB затухания сигнала для этого канала АЦП.
--------------
для диапазона до 3.3 вольт надо использовать функцию:
esp_err_t adc1_config_channel_atten(канал adc1_channel_t, adc_atten_t atten )
Установите ослабление определенного канала на ADC1 и настройте связанный с ним GPIO pin mux.
Для любого канала эта функция должна быть вызвана до первого вызова adc1_get_raw () для этого канала.
Эта функция может вызываться несколько раз для настройки нескольких каналов АЦП одновременно. затем можно вызвать adc1_get_raw () для любого настроенного канала.
Когда VDD_A 3,3 в:
  • 0dB амортизатор (ADC_ATTEN_DB_0) дает полномасштабное напряжение 1.1 V
  • 2.5 DB амортизация (ADC_ATTEN_DB_2_5) дает полномасштабное напряжение 1.5 V
  • 6dB (ADC_ATTEN_DB_6) дает полномасштабное напряжение 2.2 V
  • 11dB (ADC_ATTEN_DB_11) дает полномасштабное напряжение 3.9 V
 

unreg

Member
Вроде что-то получилось. Видно, что входящий сигнал с микрофона раскладывается по частотам, и четко видна реакция на низ, средние и высокие. Выделить конкретную "чистую" частоту (например 1KHz) не получается. Как-то грязновато выходит. Сравнивал со смартфоном. Скетч из статьи в топе немного подправил под использование OLED от Adafruit, ну и экран у меня оказался не ssd1306, а SH1106 (алиэкспресс бл). Микрофон на max9814 подключен: OUT -> PIN VP (GPIO36), OLED: SDA->GPIO26, SCL->GPIO25.
Скетч:
Код:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SH1106.h>
#include <splash.h>


#include <Wire.h>
#include "arduinoFFT.h" // Standard Arduino FFT library
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();
                              
#define OLED_SDA 26
#define OLED_SCL 25      

Adafruit_SH1106 display(OLED_SDA, OLED_SCL);


#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 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 200            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = { 0,0,0,0,0,0,0 };
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;

bool save = true;

void setup() {

    Serial.begin(115200);

    display.begin(SH1106_SWITCHCAPVCC, 0x3C);

    display.clearDisplay(); // Adjust to suit or remove
    display.setTextSize(1);
    display.setTextColor(WHITE);
    sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(".1 .2 .5 1K 2K 4K 8K");
  
        for (int i = 0; i < SAMPLES; i++) {
            newTime = micros() - oldTime;
            oldTime = newTime;
            vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
            vImag[i] = 0;
  
            while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
        }

    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    for (int i = 2; i < (SAMPLES / 2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
        if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more

            if (i <= 2)             displayBand(0, (int)vReal[i] / amplitude); // 125Hz
            if (i >3 && i <= 5)   displayBand(1, (int)vReal[i] / amplitude); // 250Hz
            if (i >5 && i <= 7)   displayBand(2, (int)vReal[i] / amplitude); // 500Hz
            if (i >7 && i <= 15)  displayBand(3, (int)vReal[i] / amplitude); // 1000Hz
            if (i >15 && i <= 30)  displayBand(4, (int)vReal[i] / amplitude); // 2000Hz
            if (i >30 && i <= 53)  displayBand(5, (int)vReal[i] / amplitude); // 4000Hz
            if (i >53 && i <= 200) displayBand(6, (int)vReal[i] / amplitude); // 8000Hz
            if (i >200) displayBand(7, (int)vReal[i] / amplitude); // 16000Hz
                                                                 //  Serial.println(vReal[i]);
                                                                 
        }
        for (byte band = 0; band <= 6; band++) drawHorizontalLine(18 * band, 64 - peak[band], 14);//  display.drawHorizontalLine(18 * band, 64 - peak[band], 14);
    }
    if (millis() % 4 == 0) { for (byte band = 0; band <= 6; band++) { if (peak[band] > 0) peak[band] -= 1; } } // Decay the peak
  
    display.display();
  
}

void displayBand(int band, int dsize)
{
    int dmax = 50;
    if (dsize > dmax) dsize = dmax;
    if (band == 7) drawHorizontalLine(18 * 6, 0, 14);
    for (int s = 0; s <= dsize; s = s + 2)
{ drawHorizontalLine(18 * band, 64 - s, 14); }
    if (dsize > peak[band]) { peak[band] = dsize; }
}

void drawHorizontalLine(int16_t x, int16_t y, int16_t l)
{
    display.drawLine(x, y, x + l, y, WHITE);
}
 

nikolz

Well-known member
Вроде что-то получилось. Видно, что входящий сигнал с микрофона раскладывается по частотам, и четко видна реакция на низ, средние и высокие. Выделить конкретную "чистую" частоту (например 1KHz) не получается. Как-то грязновато выходит. Сравнивал со смартфоном. Скетч из статьи в топе немного подправил под использование OLED от Adafruit, ну и экран у меня оказался не ssd1306, а SH1106 (алиэкспресс бл). Микрофон на max9814 подключен: OUT -> PIN VP (GPIO36), OLED: SDA->GPIO26, SCL->GPIO25.
Скетч:
Код:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SH1106.h>
#include <splash.h>


#include <Wire.h>
#include "arduinoFFT.h" // Standard Arduino FFT library
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();
                             
#define OLED_SDA 26
#define OLED_SCL 25     

Adafruit_SH1106 display(OLED_SDA, OLED_SCL);


#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 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 200            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = { 0,0,0,0,0,0,0 };
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;

bool save = true;

void setup() {

    Serial.begin(115200);

    display.begin(SH1106_SWITCHCAPVCC, 0x3C);

    display.clearDisplay(); // Adjust to suit or remove
    display.setTextSize(1);
    display.setTextColor(WHITE);
    sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(".1 .2 .5 1K 2K 4K 8K");
 
        for (int i = 0; i < SAMPLES; i++) {
            newTime = micros() - oldTime;
            oldTime = newTime;
            vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
            vImag[i] = 0;
 
            while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
        }

    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    for (int i = 2; i < (SAMPLES / 2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
        if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more

            if (i <= 2)             displayBand(0, (int)vReal[i] / amplitude); // 125Hz
            if (i >3 && i <= 5)   displayBand(1, (int)vReal[i] / amplitude); // 250Hz
            if (i >5 && i <= 7)   displayBand(2, (int)vReal[i] / amplitude); // 500Hz
            if (i >7 && i <= 15)  displayBand(3, (int)vReal[i] / amplitude); // 1000Hz
            if (i >15 && i <= 30)  displayBand(4, (int)vReal[i] / amplitude); // 2000Hz
            if (i >30 && i <= 53)  displayBand(5, (int)vReal[i] / amplitude); // 4000Hz
            if (i >53 && i <= 200) displayBand(6, (int)vReal[i] / amplitude); // 8000Hz
            if (i >200) displayBand(7, (int)vReal[i] / amplitude); // 16000Hz
                                                                 //  Serial.println(vReal[i]);
                                                                
        }
        for (byte band = 0; band <= 6; band++) drawHorizontalLine(18 * band, 64 - peak[band], 14);//  display.drawHorizontalLine(18 * band, 64 - peak[band], 14);
    }
    if (millis() % 4 == 0) { for (byte band = 0; band <= 6; band++) { if (peak[band] > 0) peak[band] -= 1; } } // Decay the peak
 
    display.display();
 
}

void displayBand(int band, int dsize)
{
    int dmax = 50;
    if (dsize > dmax) dsize = dmax;
    if (band == 7) drawHorizontalLine(18 * 6, 0, 14);
    for (int s = 0; s <= dsize; s = s + 2)
{ drawHorizontalLine(18 * band, 64 - s, 14); }
    if (dsize > peak[band]) { peak[band] = dsize; }
}

void drawHorizontalLine(int16_t x, int16_t y, int16_t l)
{
    display.drawLine(x, y, x + l, y, WHITE);
}
чистой частоты в нестационарном сигнале, каким является речь, не существует.
чистая частота лишь у генератора синусоиды на бесконечном интервале.
 

unreg

Member
nikolz, да я это понимаю конечно. генерил звук со смарта в 1 кгц и анализатор показыввал 1кгц и 2кгц, все рстальные около нуля. жаль нету генератора, наверное можно было бы откалиьровать код и получить эталон. сейчас сложно судить о качестве разложения сигнала.
 

nikolz

Well-known member
nikolz, да я это понимаю конечно. генерил звук со смарта в 1 кгц и анализатор показыввал 1кгц и 2кгц, все рстальные около нуля. жаль нету генератора, наверное можно было бы откалиьровать код и получить эталон. сейчас сложно судить о качестве разложения сигнала.
качество разложение сигнала определяется
стабильностью частоты дискретизации, числом отсчетов и используемым окном.
и определяется этими параметрами однозначно.
Но есть еще методическая погрешность определения амплитуды гармоники, которая составляет примерно 12% и не зависит от этих параметров.
 

unreg

Member
это для меня темный лес на текущий момент, пытабсь разобраться. если правильно понимаю, для определения частоты необходимо учитывать много факторов вплоть до тактов процессора. + погрешность не маленькая. Это не совсем то что нужно для конечной задачи. Выйти хотя бы на явное выделение частоты процентов в 50. Сейчас готовлю еще вопрос. Пытаюсь записать то что идет с микрофона. Новая тема
 

Lucky13

New member
Вроде что-то получилось. Видно, что входящий сигнал с микрофона раскладывается по частотам, и четко видна реакция на низ, средние и высокие. Выделить конкретную "чистую" частоту (например 1KHz) не получается. Как-то грязновато выходит. Сравнивал со смартфоном. Скетч из статьи в топе немного подправил под использование OLED от Adafruit, ну и экран у меня оказался не ssd1306, а SH1106 (алиэкспресс бл). Микрофон на max9814 подключен: OUT -> PIN VP (GPIO36), OLED: SDA->GPIO26, SCL->GPIO25.
Скетч:
Код:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SH1106.h>
#include <splash.h>


#include <Wire.h>
#include "arduinoFFT.h" // Standard Arduino FFT library
// https://github.com/kosme/arduinoFFT, in IDE, Sketch, Include Library, Manage Library, then search for FFT
arduinoFFT FFT = arduinoFFT();
                             
#define OLED_SDA 26
#define OLED_SCL 25     

Adafruit_SH1106 display(OLED_SDA, OLED_SCL);


#define SAMPLES 512              // Must be a power of 2
#define SAMPLING_FREQUENCY 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 200            // Depending on your audio source level, you may need to increase this value
unsigned int sampling_period_us;
unsigned long microseconds;
byte peak[] = { 0,0,0,0,0,0,0 };
double vReal[SAMPLES];
double vImag[SAMPLES];
unsigned long newTime, oldTime;

bool save = true;

void setup() {

    Serial.begin(115200);

    display.begin(SH1106_SWITCHCAPVCC, 0x3C);

    display.clearDisplay(); // Adjust to suit or remove
    display.setTextSize(1);
    display.setTextColor(WHITE);
    sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
}

void loop() {
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println(".1 .2 .5 1K 2K 4K 8K");
 
        for (int i = 0; i < SAMPLES; i++) {
            newTime = micros() - oldTime;
            oldTime = newTime;
            vReal[i] = analogRead(A0); // A conversion takes about 1uS on an ESP32
            vImag[i] = 0;
 
            while (micros() < (newTime + sampling_period_us)) { /* do nothing to wait */ }
        }

    FFT.Windowing(vReal, SAMPLES, FFT_WIN_TYP_HAMMING, FFT_FORWARD);
    FFT.Compute(vReal, vImag, SAMPLES, FFT_FORWARD);
    FFT.ComplexToMagnitude(vReal, vImag, SAMPLES);
    for (int i = 2; i < (SAMPLES / 2); i++) { // Don't use sample 0 and only first SAMPLES/2 are usable. Each array eleement represents a frequency and its value the amplitude.
        if (vReal[i] > 2000) { // Add a crude noise filter, 10 x amplitude or more

            if (i <= 2)             displayBand(0, (int)vReal[i] / amplitude); // 125Hz
            if (i >3 && i <= 5)   displayBand(1, (int)vReal[i] / amplitude); // 250Hz
            if (i >5 && i <= 7)   displayBand(2, (int)vReal[i] / amplitude); // 500Hz
            if (i >7 && i <= 15)  displayBand(3, (int)vReal[i] / amplitude); // 1000Hz
            if (i >15 && i <= 30)  displayBand(4, (int)vReal[i] / amplitude); // 2000Hz
            if (i >30 && i <= 53)  displayBand(5, (int)vReal[i] / amplitude); // 4000Hz
            if (i >53 && i <= 200) displayBand(6, (int)vReal[i] / amplitude); // 8000Hz
            if (i >200) displayBand(7, (int)vReal[i] / amplitude); // 16000Hz
                                                                 //  Serial.println(vReal[i]);
                                                                
        }
        for (byte band = 0; band <= 6; band++) drawHorizontalLine(18 * band, 64 - peak[band], 14);//  display.drawHorizontalLine(18 * band, 64 - peak[band], 14);
    }
    if (millis() % 4 == 0) { for (byte band = 0; band <= 6; band++) { if (peak[band] > 0) peak[band] -= 1; } } // Decay the peak
 
    display.display();
 
}

void displayBand(int band, int dsize)
{
    int dmax = 50;
    if (dsize > dmax) dsize = dmax;
    if (band == 7) drawHorizontalLine(18 * 6, 0, 14);
    for (int s = 0; s <= dsize; s = s + 2)
{ drawHorizontalLine(18 * band, 64 - s, 14); }
    if (dsize > peak[band]) { peak[band] = dsize; }
}

void drawHorizontalLine(int16_t x, int16_t y, int16_t l)
{
    display.drawLine(x, y, x + l, y, WHITE);
}
Здравствуйте! Я тоже пытаюсь сделать анализатор спектра звук на ESP32 и тоже брал за основу приведенный вами пример, но я стабильно получаю частоту вдвое меньше подаваемого сигнала. Попробовал взять непосредственно данные с АЦП из массива vReal и сделать преобразование Фурье в Excell и получил такой же результат. То есть проблема в частоте семплирования сигнала с АЦП.
 
Сверху Снизу