Аналого-цифровая частота дискретизации зависит от функции String () на ESP8266? - PullRequest
0 голосов
/ 08 февраля 2019

Я использую плату разработки ESP8266 NodeMCU 12-E для захвата звука с предварительно усиленного электретного микрофона, а затем загружаю его в Интернет, где он будет преобразован в файл WAV.Моей первой мыслью было привести целочисленные значения analogRead(A0) на ESP8266 к типу String, а затем объединить их в более длинную строку, которую я могу опубликовать в брокере MQTT.

Мои подписчики MQTT-клиентов не делалиКажется, я не получаю правильных звуковых файлов, потому что все, что я слышал, было серией ритмичных звуков.

Я решил проверить, действительно ли мой код на плате ESP8266 записывал вещи правильно.Я сократил код до следующих нескольких строк, которые, кажется, вызывают проблемы:

#include <ESP8266WiFi.h>

const char *ssid =  "____";  // Change it
const char *pass =  "____";  // Change it

void setup()
{
  Serial.begin(115200);
  Serial.println(0);      //start
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
}


void loop()
{
    int analog = analogRead(A0);

    if (analog > 255) {
      analog = 255;
    }
    else if (analog < 0){
      analog = 0;
    }

    Serial.print(String(analog));
    Serial.print(" ");

}

Вот как я использую код выше, чтобы создать файл wav, чтобы проверить, соответствует ли звук тому, что я ожидаю:

- I start up the ESP8266 development board
- I turn on the Serial Monitor and clear all previous output
- I power up my electret microphone and speak into it
- I power down my electret microphone
- I copy the contents of the Serial Monitor (which is a series of integers) into a text file called `audio.raw`
- I copy `audio.raw` to a linux machine that has ffmpeg installed
- I issue the command `ffmpeg -f u8 -ar 11111 -ac 1 -i audio.raw -y audio.wav` on the linux machine

Когда я слушаю файл audio.raw, я слышу свой голос, но скорость может быть в 5-10 раз выше обычной.(Я также получаю много шума и искажений, но это может быть отдельной проблемой с качеством входного сигнала.)

Затем я попытался изменить эту строку кода Serial.print(String(analog)) на Serial.print(analog).Затем я повторил шаги выше.Но на этот раз мой голос звучит примерно в 2 раза быстрее, чем обычно.

Почему изменение этой строки с Serial.print(String(analog)) на Serial.print(analog) имеет такое большое значение?

Это потому, что функция String() является очень дорогой операцией, которая занимает много времени?И когда сценарию требуется больше времени для обработки каждой строки кода, тогда у сценария появляется меньше времени для сбора достаточного количества analogRead(A0) точек данных?И если я запускаю ту же команду ffmpeg, используя все те же флаги, то ffmpeg попытается удовлетворить требование -ar 11111, ускорив воспроизведение звука?Что означало бы, что моя частота дискретизации зависит от скорости выполнения моего скрипта?Что означает, что я должен учитывать переменную скорость выполнения на других платах той же модели из-за различий в точности изготовления, температуре окружающей среды и т. Д ...?

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

Ваша частота дискретизации связана с вашей реализацией цикла (как вы обнаружили).Это также вызовет дрожание в вашей частоте дискретизации, поскольку разные пути кода будут занимать разное количество времени, а подпрограммы обработки прерываний также украдут циклы ЦП.

Этот джиттер будет одной из причин искажения в ваших выходных данных.

Когда я слушаю файл audio.raw, я слышу свой голос, но скорость может быть в 5-10 раз выше обычной.

ESP8266 имеет аппаратное обеспечениеUART, поэтому код может потенциально загружать буфер FIFO UART быстрее, чем он может выводить.Это будет источником воспринимаемой более высокой частоты дискретизации, но также может вызвать дрожание или потерю данных при заполнении буфера.В зависимости от реализации, когда буфер заполняется, он отбрасывает данные или, альтернативно, блокирует (вызывая дрожание).

Почему меняется эта строка с Serial.print (String (аналог)) на Serial.print(аналог) так много значит?

Это потому, что функция String () - это очень дорогая операция, которая отнимает много времени?И когда сценарию требуется больше времени для обработки каждой строки кода, сценарий затем имеет меньше времени для сбора достаточного количества точек данных аналогового чтения (A0)?

Да, да и да.

Одна из причин различия в производительности заключается в том, что String() включает выделение и управление памятью в куче для хранения символов.

Serial.print(analog) использует фиксированный размер буфера в стеке, так как код знает максимумколичество символов, необходимое для отображения целого числа.

И если я запусту ту же команду ffmpeg, используя все те же флаги, то ffmpeg попытается выполнить требование -ar 11111, ускорив воспроизведение звука?

Да.ffmpeg предполагает, что сэмплы имеют фиксированную частоту сэмплирования, но это не соответствует сэмплам, которые распечатываются.

Что означало бы, что моя частота дискретизации зависит от скорости выполнения моего сценария?

Да!

Что означает, что я долженучитывайте переменную скорость выполнения на других платах той же модели из-за различий в точности изготовления, температуре окружающей среды и т. д.? *

Да.Там будет множество переменных, которые влияют на скорость выполнения.

Что вы можете сделать?

Отделить выборку данных от выполнения кода.

Это можно сделать с помощьюреализация подпрограммы обработки прерываний .Свяжите ISR с аппаратным таймером, чтобы он выполнялся с фиксированной частотой дискретизации и избегал дрожания.

ISR может записывать в буфер, который код в loop() передает по последовательному соединению.ISR и код последовательной передачи должны управлять буфером, чтобы ни один из них не был переполнен.Одним из способов сделать это является использование альтернативных буферов, которые используют ISR и код передачи.

0 голосов
/ 08 февраля 2019

Поскольку вы используете Serial.begin (115200), микроконтроллер ESP8266 будет передавать 115200 бит в секунду через последовательный порт.Это 115200/8 = 14400 байт в секунду, и это означает, что поскольку вы используете формат u8 (8-битный без знака) для аудио, каждый сэмпл состоит из одного байта.Просто измените параметр ffmpeg -ar на 14400.

У меня нет микрофонов, которые я могу подключить к MCU для тестирования, но он должен работать должным образом.Другой параметр -ac является правильным, поскольку он является моноканальным звуком.

Редактировать: Также не используйте конструктор String () при печати в последовательном режиме.

При использовании звука конструктора Serial ()ускоряется примерно в 5 раз, потому что String преобразует ваше 1-байтовое значение в 3 байта, например;byte: 255 -> String: "2", "5", "5", вам не нужно учитывать скорость выполнения микроконтроллера, он будет выдавать 115200 бит в секунду, как если бы вы определили.Вам просто нужно учитывать это вывод.

Окончательно удалите строку

Serial.print ("");

Также измените

int аналог =analogRead (A0);

до

байтовый аналоговый = (байт) analogRead (A0);

с int состоит из 4 байтов, вы не хотите отправлять дополнительные 3 байта в последовательный.

И после изменения int на байт вы можете избавиться от этого блока кода

if (analog > 255) {
  analog = 255;
}
else if (analog < 0){
  analog = 0;
}

Если вы подключаете ESP8266 к устройству linux через usb, на котором есть ffmpeg, вы можете использовать

ttylog -b 115200 -d / dev / ttyUSB0 |ffmpeg -f u8 -ar 14400 -ac 1 -i - -y audio.wav

для захвата аудиоданных в реальном времени из ESP8266.

...