На частоту дискретизации аналого-цифрового сигнала влияет функция String() на ESP8266?

Я использую плату разработки 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, ускорив воспроизведение звука? Что означает, что моя частота дискретизации зависит от скорости выполнения моего скрипта? Что означает, что я должен учитывать переменную скорость выполнения на других платах той же модели из-за различий в точности изготовления, температуре окружающей среды и т. д.?


person John    schedule 08.02.2019    source источник


Ответы (2)


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

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

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

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

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

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

Да, да и да.

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

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

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

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

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

Да!

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

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

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

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

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

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

person Ben T    schedule 08.02.2019
comment
Итак, что-то вроде библиотеки Ticker.h — это то, что я могу использовать для асинхронного захвата и записи аналоговых данных в буфер? Я только что прочитал об этой библиотеке здесь: google.com/amp/s/circuits4you.com/2018/01/02/ - person John; 08.02.2019
comment
Ticker.h использует API os_timer, который по-прежнему основан на программном обеспечении и имеет точность до 500 микросекунд. Вместо этого вам нужно использовать аппаратные таймеры. Попробуйте techtutorialsx.com/2017/10/07/esp32-arduino- прерывания таймера, который ссылается на пример в github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/ - person Ben T; 09.02.2019

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

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

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

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

Наконец удалите строку

Серийный.принт(" ");

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

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

to

байтовый аналог = (байт) аналоговыйЧтение(A0);

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

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

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.

person yildizmehmet    schedule 08.02.2019