음성 제어 LED 프로젝트: 아두이노 마이크로 + BitVoicer 서버로 음성 인식
부품 및 소모품
| 아두이노 마이크로 |
× |
1 |
| 스파크펀 일렉트렛 마이크 브레이크아웃 |
× |
1 |
| BitVoicer 서버 |
× |
1 |
| BitVoicer 서버 1.0 |
× |
1 |
| LED (일반) |
× |
3 |
| 저항 330옴 |
× |
3 |
프로젝트 소개
이 글에서는 아두이노 보드와 BitVoicer 서버를 사용해 음성 명령으로 LED 몇 개를 제어하는 방법을 보여드리겠습니다. 아두이노 마이크로를 쓰지만, 가진 아두이노 보드 아무거나 써도 됩니다.
음성 명령이 LED 동작으로 변환되는 과정:
- 음파가 스파크펀 일렉트렛 브레이크아웃 보드에서 포착되고 증폭됩니다;
- 증폭된 신호는 아두이노의 아날로그-디지털 변환기(ADC)에서 디지털화되어 버퍼링됩니다;
- 오디오 샘플이 아두이노 시리얼 포트를 통해 BitVoicer 서버로 스트리밍됩니다;
- BitVoicer 서버가 오디오 스트림을 처리하고 음성을 인식합니다;
- 인식된 음성이 미리 정의된 명령에 매핑되어 아두이노로 다시 전송됩니다;
- 아두이노가 명령을 식별하고 적절한 동작을 수행합니다.
위 영상은 이 글의 최종 결과를 보여줍니다. 영상에서 BitVoicer 서버가 음성 합성 피드백도 제공하는 걸 볼 수 있습니다. 이 음성 피드백은 서버에서 정의되고 서버 오디오 어댑터로 재생되지만, 합성 오디오를 아두이노로 보내 디지털-아날로그 변환기(DAC)로 재생할 수도 있습니다. 다음 글에서는 아두이노 듀(DUE)와 증폭기, 스피커로 아두이노 자체에서 합성 음성을 재생하는 방법을 보여드리겠습니다.
재료 목록:
- 아두이노 마이크로 (또는 다른 아두이노 보드): ~$20.00
- 스파크펀 일렉트렛 마이크 브레이크아웃: $7.95
- BitVoicer 서버 1.0: $9.90
- 브레드보드: ~$10.00
- LED 3개: ~$1.00
- 330옴 저항 3개: ~$0.75
- 점퍼 와이어: ~$0.30
1단계: 배선
아두이노와 브레드보드에 아래 사진처럼 부품을 배선하세요.
가장 중요한 세부 사항은 아두이노 ADC에 공급되는 아날로그 레퍼런스입니다. 테스트 결과 스파크펀 일렉트렛 브레이크아웃에 3.3V를 쓸 때 결과가 더 좋았습니다. 그래서 3.3V 핀과 AREF 핀 사이에 점퍼를 연결했습니다. 3.3V가 AREF 핀에 인가된 상태에서 analogRead 함수를 어떤 이유로든 써야 한다면, analogRead 사용 전 반드시 analogReference(EXTERNAL)를 호출해야 합니다. 그렇지 않으면 활성 레퍼런스 전압(내부 생성)과 AREF 핀이 단락돼 아두이노 보드 마이크로컨트롤러가 손상될 수 있습니다.
2단계: 아두이노에 코드 업로드
아래 코드를 아두이노에 업로드하세요. 편의를 위해 아두이노 스케치는 이 글 하단 첨부 파일 섹션에도 있습니다. 업로드 전 BitVoicer 서버 라이브러리를 아두이노 IDE에 제대로 설치해야 합니다 (ZIP 라이브러리 가져오기).
BVS_Demo1.ino
이 스케치는 네 가지 주요 부분으로 구성됩니다:
- 라이브러리 참조 및 변수 선언: 처음 두 줄은 BVSP와 BVSMic 라이브러리를 포함합니다. 이 라이브러리는 BitSophia에서 제공하며 BitVoicer 서버 설치 폴더에서 찾을 수 있습니다. 나머지 줄은 스케치 전반에서 쓰이는 상수와 변수를 선언합니다. BVSP 클래스는 BitVoicer 서버와의 통신용, BVSMic 클래스는 오디오 샘플 캡처 및 저장용입니다.
- Setup 함수: 시리얼 통신, BVSP 클래스, BVSMic 클래스를 초기화하고 frameReceived 이벤트용 "이벤트 핸들러"(실제로는 함수 포인터)를 설정합니다.
- Loop 함수: 세 가지 중요한 동작을 수행합니다: 서버에 상태 정보 요청(keepAlive() 함수), 서버가 데이터 보냈는지 확인하고 수신 데이터 처리(receive() 함수), 오디오 스트림 녹음 및 전송 제어(isSREAvailable(), startRecording(), stopRecording(), sendStream() 함수).
- BVSP_frameReceived 함수: receive() 함수가 완전한 프레임 수신을 감지할 때마다 호출됩니다. 여기선 BitVoicer 서버에서 보낸 명령을 실행합니다. 명령은 2바이트입니다. 첫 바이트는 핀 번호, 두 번째 바이트는 핀 값입니다.
analogWrite() 함수로 핀에 적절한 값을 설정합니다.
3단계: BitVoicer 서버 솔루션 객체 가져오기
이제 아두이노와 작동하도록 BitVoicer 서버를 설정해야 합니다. BitVoicer 서버에는 4가지 주요 솔루션 객체가 있습니다: 위치(Locations), 장치(Devices), 바이너리 데이터(BinaryData), 음성 스키마(Voice Schemas).
위치는 장치가 설치된 물리적 위치를 나타냅니다. 제 경우 'Home'이라는 위치를 만들었습니다.
장치는 BitVoicer 서버 클라이언트입니다. Mixed 장치를 만들어 'ArduinoMicro'라고 이름 짓고 통신 설정을 입력했습니다. 아두이노 마이크로 참고: RTS와 DTR을 사용하므로 통신 탭에서 이 설정을 활성화해야 합니다. 서버 오디오 어댑터로 음성 합성용 SystemSpeaker 장치도 만들었습니다.
바이너리 데이터는 BitVoicer 서버가 클라이언트 장치로 보낼 수 있는 명령 유형입니다. 실제로는 명령에 연결할 수 있는 바이트 배열입니다. BitVoicer 서버가 해당 명령 관련 음성을 인식하면 대상 장치로 바이트 배열을 전송합니다. 핀 값마다 BinaryData 객체를 하나씩 만들어 'ArduinoMicroGreenLedOn', 'ArduinoMicroGreenLedOff' 등으로 이름 지었습니다. 총 18개 BinaryData 객체가 생겨서 아래 VoiceSchema.sof 파일에서 객체를 다운로드해 가져오길 권장합니다.
음성 스키마는 모든 게 모이는 곳입니다. 어떤 문장을 인식할지, 어떤 명령을 실행할지 정의합니다. 문장마다 필요한 만큼 명령을 정의하고 실행 순서를 정할 수 있습니다. 명령 간 딜레이도 정의 가능합니다. 이렇게 영상에서 본 동작 시퀀스를 수행했습니다.
이 글에서 쓴 모든 솔루션 객체는 아래 파일에서 가져오기(솔루션 객체 가져오기) 할 수 있습니다. 하나는 장치, 다른 하나는 음성 스키마와 명령들을 담고 있습니다.
Devices.sof
VoiceSchema.sof
4단계: 결론
이게 다입니다! 마음에 드셨길 바랍니다.
영상에서 본 데모 실행에 필요한 모든 게 준비됐습니다. 영상에서 BitVoicer 서버 매니저에서 ArduinoMicro 장치를 활성화하는 걸로 시작하는 걸 볼 수 있습니다. 활성화되자마자 아두이노가 사용 가능한 음성 인식 엔진을 감지하고 BitVoicer 서버로 오디오 스트리밍을 시작합니다.
BitVoicer 서버 매니저의 서버 모니터 도구에서 인식 결과를 확인할 수 있습니다.
다음 글에서는 아두이노 듀로 합성 음성을 재생하는 방법을 보여드리겠습니다.
코드
BVS_Demo1.ino 아두이노
#include <BVSP.h>
#include <BVSMic.h>
// 오디오 캡처에 쓸 아두이노 핀 정의
#define BVSM_AUDIO_INPUT 5
// LED 핀 정의
#define RED_LED_PIN 6
#define YELLOW_LED_PIN 9
#define GREEN_LED_PIN 10
// BVSP.begin 함수에 매개변수로 전달될 상수 정의
const unsigned long STATUS_REQUEST_TIMEOUT = 1000;
const unsigned long STATUS_REQUEST_INTERVAL = 2000;
// 오디오 버퍼 크기 정의
const int AUDIO_BUFFER_SIZE = 64;
// 수신 버퍼 크기 정의
const int RECEIVE_BUFFER_SIZE = 2;
// BVSP 클래스 전역 인스턴스 초기화
BVSP bvsp = BVSP();
// BVSMic 클래스 전역 인스턴스 초기화
BVSMic bvsm = BVSMic();
// BVSMic 클래스에서 녹음된 샘플 읽을 버퍼 생성
byte audioBuffer[AUDIO_BUFFER_SIZE];
// BitVoicer 서버에서 보낸 명령 읽을 버퍼 생성
// 바이트 0 = 핀 번호
// 바이트 1 = 핀 값
byte receiveBuffer[RECEIVE_BUFFER_SIZE];
void setup() {
// 핀 모드 설정
pinMode(RED_LED_PIN, OUTPUT);
pinMode(YELLOW_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
// 115200 bps로 시리얼 통신 시작
Serial.begin(115200);
// 통신에 쓸 아두이노 시리얼 포트, 상태 요청 타임아웃, 상태 요청 간격 설정
bvsp.begin(Serial, STATUS_REQUEST_TIMEOUT, STATUS_REQUEST_INTERVAL);
// frameReceived 이벤트 처리할 함수 정의
bvsp.frameReceived = BVSP_frameReceived;
// BVSMic 클래스 타이머 준비
bvsm.begin();
}
void loop() {
// 상태 요청 간격 경과했는지 확인하고, 지났으면 BitVoicer 서버에 상태 요청
bvsp.keepAlive();
// 시리얼 포트 버퍼에 데이터 있는지 확인하고 BitVoicer 서버 프로토콜 사양에 따라 처리
bvsp.receive();
// 사용 가능한 SRE 있는지 확인. 있으면 녹음 시작.
if (bvsp.isSREAvailable()) {
// BVSMic 클래스가 녹음 중이 아니면 오디오 입력 설정하고 녹음 시작
if (!bvsm.isRecording) {
bvsm.setAudioInput(BVSM_AUDIO_INPUT, EXTERNAL);
bvsm.startRecording();
}
// BVSMic 클래스에 사용 가능한 샘플 있는지 확인
if (bvsm.available) {
// 스트림 전송 전 인바운드 모드가 STREAM_MODE인지 확인
if (bvsp.inboundMode == FRAMED_MODE)
bvsp.setInboundMode(STREAM_MODE);
// BVSMic 클래스에서 오디오 샘플 읽기
int bytesRead = bvsm.read(audioBuffer, AUDIO_BUFFER_SIZE);
// 오디오 스트림을 BitVoicer 서버로 전송
bvsp.sendStream(audioBuffer, bytesRead);
}
} else {
// 사용 가능한 SRE 없음. BVSMic 클래스가 녹음 중이면 멈춤.
if (bvsm.isRecording)
bvsm.stopRecording();
}
}
// frameReceived 이벤트 처리
void BVSP_frameReceived(byte dataType, int payloadSize) {
// 수신 프레임에 바이너리 데이터 있는지 확인
// 0x07 = 바이너리 데이터 (바이트 배열)
if (dataType == DATA_TYPE_BINARY) {
// 2바이트 수신되면 명령 처리
if (bvsp.getReceivedBytes(receiveBuffer, RECEIVE_BUFFER_SIZE) == RECEIVE_BUFFER_SIZE) {
analogWrite(receiveBuffer[0], receiveBuffer[1]);
}
}
}
회로도
회로도 이미지 포함