В поисках экономичного способа общения с микроконтроллером некоторое время назад я пришел к варианту голосового управления со смартфона. Для начала опытов с голосовым управлением потребовалось совсем немного ресурсов: смартфон, Ардуинка и Bluetooth-модуль. Все остальное (за исключением скетча на Ардуинка) предоставил Андроид: система распознавания речи встроена в стандартную клавиатуру (не запускает клик по иконке микрофона в верхней части клавиатуры), а связь с микроконтроллером может обеспечить любое приложение с функцией Bluetooth-терминала. За пару часов был написан скетч, имитирующий управление тележкой с помощью набора команд (“налево”, “направо”, “быстрее”, “медленнее”, …).
В итоге получился вот такой скетч:
#include <SoftwareSerial.h>
SoftwareSerial mySerial(7, 6); // RX, TX
int Azimuth, // направление движения и
Velocity; // скорость тележки
String cmdBufferS,
cmdBufferH,
sResponse;
bool bDebug;
long mSec;
double curX, curY, curA;
void setup() {
Azimuth = 0;
Velocity = 0;
curX = 0;
curY = 0;
curA = 0;
mSec = millis();
bDebug = false;
cmdBufferS = "";
cmdBufferH = "";
mySerial.begin(9600); // инициализация и настройка скорости программного последовательного порта
Serial.begin(9600);
Serial.println(F("----------------------------------------------------------"));
Serial.println(F("-------------------- Sketch avr_Voice --------------------"));
Serial.println(F("----------------------------------------------------------"));
}
void loop() {
readSerialData();
if(millis()-mSec>=999) {
mSec = millis();
updateCrds();
}
}
void changeVelocity(int8_t r,String cmd) {
byte p;
int v;
String sv;
if(p = cmd.indexOf(' ')) {
cmd.remove(0,p+1);
v = cmd.toInt();
if(r<0) {
Serial.print("V-, ");
Velocity = Velocity-v;
if(Velocity<0) Velocity = 0;
}
else {
Serial.print("V+, ");
Velocity = Velocity+v;
}
Serial.print("Parameter: ");
Serial.print(cmd);
Serial.print(" -> ");
Serial.println(Velocity);
}
} // of changeVelocity
void changeAzimuth(int8_t r,String cmd) {
byte p;
int v;
if(p = cmd.indexOf(' ')) {
cmd.remove(0,p+1);
v = cmd.toInt();
if(r<0) {
Serial.print("toleft, ");
Azimuth = Azimuth-v;
if(Azimuth<0) Azimuth = Azimuth + 360;
}
else {
Serial.print("toright, ");
Azimuth = Azimuth+v;
if(Azimuth>360) Azimuth = Azimuth - 360;
}
Serial.print("Parameter: ");
Serial.print(cmd);
Serial.print(" -> ");
Serial.println(Azimuth);
}
}
void updateCrds() {
double R,A;
// направление движения (угол в градусах) переводим в радианы
A = Azimuth*3.1415926/180;
curX = curX + Velocity*sin(A);
curY = curY + Velocity*cos(A);
R = sqrt(curX*curX+curY*curY);
//curA = asin(curX/R);
// получим угол в радианах
if(curY==0) {
if(curX>=0) curA = 0; else curA = 2*3.1415926;
} else curA = atan(curX/curY);
// и переведем его в градусы
curA = curA/3.1416*180;
if(curX<0) curA = 270 - curA;
}
void SendState() {
mySerial.print("A="); mySerial.print(Azimuth);
mySerial.print(", V="); mySerial.print(Velocity);
if(bDebug) {
mySerial.print(" (X="); mySerial.print(curX);
mySerial.print(" / Y="); mySerial.print(curY);
mySerial.print(" / A="); mySerial.print(curA);
mySerial.print(")");
}
mySerial.println("");
}
void interpreteCommand(String &cmdBuffer) {
if(cmdBuffer.length()==0) return;
Serial.print("Command: ");
Serial.println(cmdBuffer);
if(cmdBuffer.startsWith("налево")) changeAzimuth(-1,cmdBuffer);
else if(cmdBuffer.startsWith("направо")) changeAzimuth(+1,cmdBuffer);
else if(cmdBuffer.startsWith("быстрее")) changeVelocity(+1,cmdBuffer);
else if(cmdBuffer.startsWith("медленнее")) changeVelocity(-1,cmdBuffer);
else if(cmdBuffer.startsWith("отладка")) bDebug = true;
SendState();
cmdBuffer = "";
}
void readSerialData() {
char c;
if (mySerial.available()) {
c = (char)mySerial.read();
//if(c<' ') {Serial.print("<"); Serial.print(byte(c)); Serial.print(">");}
//else Serial.print(c);
if (c==0x0A || c==0x0D) interpreteCommand(cmdBufferS);
else cmdBufferS=cmdBufferS+c;
}
if (Serial.available()) {
c = (char)Serial.read();
if(c<' ') {Serial.print("<"); Serial.print(byte(c)); Serial.print(">");}
else Serial.print(c);
if (c==0x0A || c==0x0D) interpreteCommand(cmdBufferH);
else cmdBufferH=cmdBufferH+c;
}
} // of readSerialData
После первых опытов пришло понимание, что такой вариант не слишком комфортен - необходимо постоянно включать-выключать клавиатуру а в ней дополнительно клацать по значку микрофона, чтобы получить возможность с помощью Google-сервиса распознать сказанную фразу, а затем отправить полученный текст на Ардуину.
Было принято решение создать небольшое приложение, которое взяло бы на себя все лишние телодвижения и позволило сконцентрироваться на процессе подачи команд голосом. Через некоторое время приложение-демонстратор появилось и было опубликовано в Google Play под именем Ard Control.
Программные компоненты, необходимые для начала опытов с голосовым управлением, готовы. Осталось добавить к ним смартфон, модуль Bluetooth и Ардуинку любой модели - и можно приступать к изучению возможностей, выявлению преимуществ и недостатков этого метода взаимодействия с микроконтроллером.


