Освоение распознавания и синтеза речи на Arduino Due
Компоненты и материалы
| Arduino Due | ×1 |
| SparkFun Electret Microphone Breakout | ×1 |
| SparkFun Mono Audio Amp Breakout | ×1 |
| Динамик 0.25 Вт, 8 Ом | ×1 |
| Макетная плата | ×1 |
| LED 5 мм красный | ×3 |
| Резистор 330 Ом | ×3 |
| Перемычки | ×1 |
Инструменты
- Паяльник
Программное обеспечение
- BitVoicer Server 1.0 (платный, $9.90)
О проекте
В предыдущем проекте я показывал управление LED через BitVoicer Server. Здесь усложняем задачу: добавляем синтез речи с помощью встроенного в Arduino Due 12-битного ЦАП (DAC) и DMA. На других Arduino потребуется внешний ЦАП и доп. код (библиотека BVSSpeaker не поможет).
В демо Arduino играет мелодию и мигает LED как клавиши пианино (ноты C, D, E). Мелодия — джингл старого ритейлера Mappin.
Поток данных
- Звук захватывается и усиливается SparkFun Electret Breakout.
- Сигнал оцифровывается ADC Arduino и буферизируется.
- Аудиопоток передаётся в BitVoicer Server по Serial (115200 бод).
- Сервер распознаёт речь, сопоставляет с командами, шлёт ответы.
- Если команда требует синтеза речи — сервер готовит аудиопоток (8-битный моно PCM, 8 кГц) и отправляет на Arduino.
- Arduino ставит сэмплы в очередь BVSSpeaker, воспроизводит через DAC+DMA.
- SparkFun Mono Audio Amp усиливает сигнал для 8-Ом динамика.
Список материалов и стоимость
- Arduino Due: ~$50
- Electret Mic Breakout: $7.95
- Mono Audio Amp Breakout: $7.95
- BitVoicer Server 1.0: $9.90
- Динамик 8 Ом: ~$2
- Макетка: ~$10
- 3×LED + 3×330Ω: ~$1.75
- Перемычки: ~$0.50
Шаг 1: Сборка
Соберите по фото. Под динамик подложите резиновую прокладку — вибрация сильно влияет на качество звука.
Важно: Большинство Arduino работают на 5 В, но Due — на 3.3 В. Electret Breakout лучше работает от 3.3 В. На 5-вольтовых платах добавьте перемычку 3.3V → AREF. На Due AREF подключён через делитель — для использования AREF нужно отпаять резистор BR1.
Шаг 2: Загрузка скетча
Скетч BVS_Demo2.ino (в прикреплённых файлах). Предварительно установите библиотеки BitVoicer Server в Arduino IDE (Import .zip Library).
Скетч состоит из 7 частей:
- Библиотеки и переменные: BVSP, BVSMic, BVSSpeaker, DAC. BVSP — связь с сервером, BVSMic — захват звука, BVSSpeaker — воспроизведение через DAC.
- setup(): пины, Serial 115200, инициализация классов, установка обработчиков событий
frameReceived,modeChanged,streamReceived. - loop(): keepAlive(), receive(), контроль записи/передачи аудио (isSREAvailable, startRecording, stopRecording, sendStream), воспроизведение (play()), синхронизация LED с музыкой (playNextLEDNote).
- BVSP_frameReceived: обработка бинарных команд (2 байта: пин, значение) через analogWrite(). Команда
playLEDNotes(байт 255) запускает синхронизацию LED. - BVSP_modeChanged: при переходе из стримингового режима в фреймовый — останавливает воспроизведение аудио.
- BVSP_streamReceived: приём аудиосэмплов от сервера, постановка в очередь BVSSpeaker.
- playNextLEDNote: синхронизация LED с музыкой по таймингам, полученным через Sonic Visualizer (бесплатный аудио-анализатор).
Шаг 3: Импорт объектов решения BitVoicer Server
Четыре типа объектов: Locations, Devices, BinaryData, Voice Schemas.
- Location: Home.
- Device: Mixed, имя ArduinoDUE. Важно: ограничьте Data Rate до 8000 сэмплов/с — иначе переполнение буфера.
- BinaryData: по одному на каждое состояние LED (вкл/выкл для 3 пинов) — 18 объектов. Лучше импортировать из
VoiceSchema.sof. - Voice Schema: фразы и команды. Пример: «play a little song» → команда байта 255 (запуск LED-нот) + команда аудиострима (мой джингл). Аудио конвертируйте в 8-битный моно PCM 8 кГц (онлайн-конвертер).
Файлы для импорта: Device.sof, VoiceSchema.sof.
Шаг 4: Заключение
Включите устройство в BitVoicer Server Manager — Arduino найдёт доступный движок распознавания и начнёт стриминг. RX LED будет активно мигать при приёме аудио от сервера.
В следующем проекте добавлю WiFi на одной Arduino и управление двумя другими — идеи для игры приветствуются!
Код
BVS_Demo2.ino (Arduino)
#include
#include
#include
#include
#define BVSM_AUDIO_INPUT 7
#define RED_LED_PIN 6
#define YELLOW_LED_PIN 9
#define GREEN_LED_PIN 10
const unsigned long STATUS_REQUEST_TIMEOUT = 3000;
const unsigned long STATUS_REQUEST_INTERVAL = 4000;
const int MIC_BUFFER_SIZE = 64;
const int SPEAKER_BUFFER_SIZE = 128;
const int RECEIVE_BUFFER_SIZE = 2;
BVSP bvsp;
BVSMic bvsm;
BVSSpeaker bvss;
byte micBuffer[MIC_BUFFER_SIZE];
byte speakerBuffer[SPEAKER_BUFFER_SIZE];
byte receiveBuffer[RECEIVE_BUFFER_SIZE];
bool playLEDNotes = false;
unsigned int playStartTime = 0;
void setup() {
pinMode(RED_LED_PIN, OUTPUT);
pinMode(YELLOW_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(YELLOW_LED_PIN, LOW);
digitalWrite(GREEN_LED_PIN, LOW);
Serial.begin(115200);
bvsp.begin(Serial, STATUS_REQUEST_TIMEOUT, STATUS_REQUEST_INTERVAL);
bvsp.frameReceived = BVSP_frameReceived;
bvsp.modeChanged = BVSP_modeChanged;
bvsp.streamReceived = BVSP_streamReceived;
bvsm.begin();
bvss.begin(DAC);
}
void loop() {
bvsp.keepAlive();
bvsp.receive();
if (bvsp.isSREAvailable()) {
if (!bvsm.isRecording) {
bvsm.setAudioInput(BVSM_AUDIO_INPUT, DEFAULT);
bvsm.startRecording();
}
if (bvsm.available) {
if (bvsp.inboundMode == FRAMED_MODE) bvsp.setInboundMode(STREAM_MODE);
int bytesRead = bvsm.read(micBuffer, MIC_BUFFER_SIZE);
bvsp.sendStream(micBuffer, bytesRead);
}
} else {
if (bvsm.isRecording) bvsm.stopRecording();
}
bvss.play();
if (playLEDNotes) playNextLEDNote();
}
void BVSP_frameReceived(byte dataType, int payloadSize) {
if (dataType == DATA_TYPE_BINARY) {
if (bvsp.getReceivedBytes(receiveBuffer, RECEIVE_BUFFER_SIZE) == RECEIVE_BUFFER_SIZE) {
analogWrite(receiveBuffer[0], receiveBuffer[1]);
}
} else if (dataType == DATA_TYPE_BYTE) {
if (bvsp.getReceivedByte() == 255) {
playLEDNotes = true;
playStartTime = millis();
}
}
}
void BVSP_modeChanged() {
if (bvsp.outboundMode == FRAMED_MODE) bvss.finishPlaying();
}
void BVSP_streamReceived(int size) {
int bytesRead = bvsp.getReceivedStream(speakerBuffer, SPEAKER_BUFFER_SIZE);
bvss.enqueue(speakerBuffer, bytesRead);
}
void playNextLEDNote() {
unsigned long elapsed = millis() - playStartTime;
allLEDsOff();
if (elapsed >= 11500) { analogWrite(RED_LED_PIN, 0); playLEDNotes = false; }
else if (elapsed >= 9900) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 9370) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 8900) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 8610) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 8230) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 7970) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 7470) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 6760) analogWrite(GREEN_LED_PIN, 255);
else if (elapsed >= 6350) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 5880) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 5560) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 5180) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 4890) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 4420) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 3810) analogWrite(GREEN_LED_PIN, 255);
else if (elapsed >= 3420) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 2930) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 2560) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 2200) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 1930) analogWrite(YELLOW_LED_PIN, 255);
else if (elapsed >= 1470) analogWrite(RED_LED_PIN, 255);
else if (elapsed >= 1000) analogWrite(GREEN_LED_PIN, 255);
}
void allLEDsOff() {
analogWrite(RED_LED_PIN, 0);
analogWrite(YELLOW_LED_PIN, 0);
analogWrite(GREEN_LED_PIN, 0);
}
Схема подключения
Схема: Electret Mic → A7, Audio Amp → DAC0, LED: D6 (Red), D9 (Yellow), D10 (Green). Питание микрофона от 3.3V Due.
Производственный процесс
- Надежная система учета рабочего времени на Arduino с RFID и Python
- Проект с датчиком DHT11: индикация температуры светодиодами и пьезо-динамиком на Arduino
- Интерактивный гироскопический LED-дисплей на NeoPixel Ring и Arduino
- Монитор температуры и реальные часы на Arduino Due с TFT-дисплеем 3.2"
- Управление Roomba Create 2 через Arduino и Android: пошаговое руководство
- Сборка вольтметра на Arduino с дисплеем Nokia 5110 — Пошаговое руководство
- Точное управление сервоприводом через MPU6050 (Arduino, SG90, MG996R)
- Модуль GPS u-blox LEA-6H 02: Интеграция с Arduino и Python
- Чтение температуры и влажности DHT11 через Blynk — пошаговый урок для Arduino
- Создание интерактивного 4x4x4 светодиодного куба на Arduino Uno и 1Sheeld