Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Компоненты и материалы

Arduino Nano 33 BLE ×1
Электретный микрофон с усилителем MAX9814 ×1
OLED I2C 0.91" 128×32 (SSD1306) ×1
Конденсатор 4.7 нФ (для RC ФНЧ, зависит от частоты среза) ×1
Конденсатор 470 мкФ (стабилизация питания) ×1
Резистор 2 кОм (для RC ФНЧ) ×1

Приложения

О проекте

Введение

Визуализация звука на маленьком OLED: столбиковая диаграмма спектра в реальном времени. Низкие частоты — слева, высокие — справа.

Сборка и подключение

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Проблема низкочастотного гудения (50/60 Гц): от сетевых наводок. Решение — фарадеевская клетка: схему в коробке, корпус на землю.

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Arduino Nano 33 BLE + analogRead() дает ~35 000 сэмплов/с. Найквист → макс. частота 17.5 кГц. С учетом накладных расходов берем 8 кГц верхнюю границу. RC ФНЧ отсекает выше.

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Использую R=2кОм, C=4.7нФ → fc≈17 кГц.

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Оценка максимальной частоты дискретизации

Тестовый скетч для замера скорости analogRead():

#define ANALOG_PIN A0
#define SAMPLES 1000000.0
int newTime, analogValue;
void setup() {
  Serial.begin(9600);
  Serial.println("Sampling... please, wait.");
}
void loop() {
  newTime = micros();
  for (int i = 0; i < (int)SAMPLES; i++) {
    analogValue = analogRead(ANALOG_PIN);
  }
  float elapsedTimePerSampleUs = (micros() - newTime) / SAMPLES;
  Serial.print("Time per sample (us): "); Serial.println(elapsedTimePerSampleUs);
  Serial.print("Max sampling freq(hz): "); Serial.println(pow(10,6) / elapsedTimePerSampleUs);
}
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

~35 ксэмпл/с → Найквист 17.5 кГц. В коде busy-wait с micros() для точного периода дискретизации:

for (int i = 0; i < SAMPLES; i++) {
  unsigned long newTime = micros();
  int value = analogRead(ANALOG_PIN);
  vReal[i] = value; vImag[i] = 0;
  while (micros() < (newTime + sampling_period_us)) { yield(); }
}

Пользовательский интерфейс

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Экран разбит на 11 частотных зон по логарифмической шкале (как восприятие уха). Человек слышит 20 Гц–20 кГц (взрослые ~15-17 кГц). Используем зоны 3–13.

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Каждый столбик: пунктирная линия — максимум в полосе, закрашенный прямоугольник — среднее. Значения в децибелах относительно эмпирического референса.

Как это работает

  1. Аудиосэмплы → FFT (быстрое преобразование Фурье) → частотный спектр.
  2. Спектр делится и группируется в 14 зон, 11 отображаются в дБ.

О микрофонном модуле MAX9814

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Электретный микрофон: квази-постоянно заряженный диэлектрик. Диафрагма меняет емкость → напряжение на конденсаторе. Встроенный FET-усилитель. Малый размер, высокая чувствительность, широкий АЧХ, низкая цена.

MAX9814: усилитель с АРУ (авт. регулировкой усиления), низкий шум, bias 1.25В. 3 настройки усиления: 40/50/60dB (джампер GAIN к VCC/GND/висячий). Выход ~2В пик-пик на bias 1.25В — идеально для 3.3В АЦП. Attack/Release настраиваем: 1:4000 (умолч.), 1:2000, 1:500.

Для Line In — разделительный конденсатор >1мкФ (33-100мкФ) для блокировки DC.

Питание: 2.7–5.5В.

Чувствительность: отношение выходного сигнала к звуковому давлению. Эталон: 1 кГц, 94 дБ SPL (1 Па). Выше чувствительность (менее отрицательное dBV) → больше выходной сигнал.

АЧХ: границы ±3 дБ от уровня на 1 кГц (референс 0 дБ).

OLED 0.91" 128×32 I2C

Маленький дисплей ~1", 128×32 белых пикселей, без подсветки — мало энергии. 4 пина, I2C. Подключение к Nano: SDA→A4, SCL→A5.

Библиотеки: Adafruit_SSD1306.h, Adafruit_GFX.h (установка через Library Manager).

Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Дальнейшие планы: Nano 33 BLE умеет быстрее читать АЦП (nrf52840). Следующая версия — оптимизированный FFT и ADC.

Код

Полный скетч использует arduinoFFT, Adafruit_GFX, Adafruit_SSD1306:

/* Sound Spectrum Visualizer for Arduino Nano 33 BLE
   @author Enrique Albertos
   Hardware: Arduino Nano 33 BLE, SSD1306 128x32 I2C, MAX9814, опционально батарея
   Libraries: arduinoFFT, Adafruit_GFX, Adafruit_SSD1306
   Connections: MIC GAIN (VCC/GND/float), A0←MIC OUT, 3.3V/GND/SDA/SCL→OLED
   License: GPL v3+ */

#include "arduinoFFT.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SAMPLES 1024
#define SAMPLING_FREQ 24000
#define AMPLITUDE 100
#define FREQUENCY_BANDS 14
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define BARWIDTH 11
#define BARS 11
#define ANALOG_PIN A0
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
double vImag[SAMPLES], vReal[SAMPLES];
unsigned long sampling_period_us;
arduinoFFT fft = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
float reference = log10(50.0);
double coutoffFrequencies[FREQUENCY_BANDS];

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { for(;;); }
  display.clearDisplay(); display.display();
  display.setRotation(0); display.invertDisplay(false);
  sampling_period_us = (1.0 / SAMPLING_FREQ) * 1e6;
  double basePot = pow(SAMPLING_FREQ / 2.0, 1.0 / FREQUENCY_BANDS);
  coutoffFrequencies[0] = basePot;
  for (int i = 1; i < FREQUENCY_BANDS; i++) coutoffFrequencies[i] = basePot * coutoffFrequencies[i-1];
  for (int i = 0; i < BARS-1; i++)
    for (int j = 0; j < SCREEN_HEIGHT; j += 4)
      display.writePixel((i+1)*BARWIDTH+2, j, SSD1306_WHITE);
  display.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SSD1306_WHITE);
}

int oldHeight[20], oldMax[20];
double maxInFreq;

void loop() {
  for (int i = 0; i < SAMPLES; i++) {
    unsigned long newTime = micros();
    int value = analogRead(ANALOG_PIN);
    vReal[i] = value; vImag[i] = 0;
    while (micros() < (newTime + sampling_period_us)) yield();
  }
  fft.DCRemoval();
  fft.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  fft.Compute(FFT_FORWARD);
  fft.ComplexToMagnitude();

  double median[20] = {0}, max[20] = {0};
  int index = 0;
  double hzPerSample = (1.0 * SAMPLING_FREQ) / SAMPLES;
  double hz = 0, sum = 0; int count = 0;
  for (int i = 2; i < SAMPLES/2; i++) {
    count++; sum += vReal[i];
    if (vReal[i] > max[index]) max[index] = vReal[i];
    if (hz > coutoffFrequencies[index]) {
      median[index] = sum / count; sum = 0; count = 0;
      index++; max[index] = 0; median[index] = 0;
    }
    hz += hzPerSample;
  }
  if (sum > 0) {
    median[index] = sum / count;
    if (median[index] > maxInFreq) maxInFreq = median[index];
  }

  int bar = 0;
  for (int i = FREQUENCY_BANDS-1; i >= 3; i--) {
    int newHeight = 0, newMax = 0;
    if (median[i] > 0 && max[i] > 0) {
      newHeight = 20.0 * (log10(median[i]) - reference);
      newMax = 20.0 * (log10(max[i]) - reference);
    }
    if (newHeight < 0 || newMax < 0) { newHeight = 1; newMax = 1; }
    if (newHeight >= SCREEN_HEIGHT-2) newHeight = SCREEN_HEIGHT-3;
    if (newMax >= SCREEN_HEIGHT-2) newMax = SCREEN_HEIGHT-3;

    int barX = bar * BARWIDTH + 5;
    if (oldHeight[i] > newHeight)
      display.fillRect(barX, newHeight+1, 7, oldHeight[i], SSD1306_BLACK);
    if (oldMax[i] > newHeight)
      for (int j = oldMax[i]; j > newHeight; j -= 2)
        display.drawFastHLine(barX, j, 7, SSD1306_BLACK);
    for (int j = newMax; j > newHeight; j -= 2)
      display.drawFastHLine(barX, j, 7, SSD1306_WHITE);
    display.fillRect(barX, 1, 7, newHeight, SSD1306_WHITE);
    oldMax[i] = newMax; oldHeight[i] = newHeight;
    bar++;
  }
  display.drawFastHLine(0, SCREEN_HEIGHT-1, SCREEN_WIDTH, SSD1306_WHITE);
  display.display();
}

Схемы

soundspectrumvisualizer_qLzRsVPVlc.fzz
Спектральный анализатор звука на Arduino Nano 33 BLE с OLED-дисплеем

Производственный процесс

  1. Создание классической игры Pong на Arduino UNO с OLED-дисплеем: пошаговый урок
  2. Датчик CO2 K30: мониторинг качества воздуха в реальном времени на Arduino
  3. Устройство для общения глухосліпых людей с помощью 1Sheeld и Arduino
  4. Умная копилка: управление монетоприёмником на Arduino Nano
  5. Управление светодиодом по Bluetooth с Arduino: простой DIY-гид
  6. Управление двумя шаговыми двигателями через джойстик на Arduino Nano — простой урок (Visuino)
  7. Передача аудиоданных с Arduino Nano 33 BLE Sense: Руководство по Chirp SDK
  8. Ручной дозиметр (Гейгер-счетчик) на Arduino Nano: сборка в 3D-корпусе
  9. Измерение солнечной радиации на Arduino UNO с Ethernet Shield
  10. Робот-«едобот» на Arduino Nano: открывает рот при подаче еды (HC-SR04 + сервопривод)