Ironman – 아두이노와 구글 음성인식 프로젝트 공유

이전글에서 소개한 아두이노 프로젝트 공유

구글의 음성인식 서비스를 이용해서 집안의 전자 제품을 제어함.

Arduino 부품 구성

1366606244729m0
아두이노 UNO
1435213141307m0
블루투스 모듈 (HC-06)
TA189_m
모션 인식 센서
41VwSSx
릴레이

Arduino 회로도

Circuit

안드로이드 앱
Github 소스
APK Download

아두이노 소스

#include <SoftwareSerial.h>

SoftwareSerial mySerial(11,12); // RX, TX

const int kDPinActivity = 2;
const int kDPinLight = 13;
const int kAPinBright = 5;

// Commands strings from remote controller
const String kCmdLightOn = "light on";
const String kCmdLightOff = "light off";
const String kCmdSetPrefix = "set:";

// Status string
const String kCmdPrefix = "command:";
const String kStatusActivityDetected = "activity detected";

const char kPacketDelimiter = '#';

// The base brightness to decide day.
int min_brightness_of_day = 10;
int max_brightness_of_night = 5;

void setup() {
  pinMode(kDPinLight, OUTPUT);
  pinMode(kDPinActivity, INPUT);
  pinMode(kAPinBright, INPUT);
  
  digitalWrite(kDPinLight, HIGH); // Trun off
  
  Serial.begin(9600);
  Serial.println("Start");
  mySerial.begin(9600);
}

bool needLightOn(int activity, int brightness) {
  // the lower the value is, the brighter.
  if (brightness > max_brightness_of_night) {
    // during the day
    return false;
  }
  if (activity) {
    return true;
  }
  return false;
}

bool needLightOff(int brightness) {
  if (brightness > min_brightness_of_day) {
    return true;
  }
  return false;
}

// command structure 
// [command name]:[value]
void processSettingCmd(String cmd) {
  const char* kBrightnessDayValue = "brightness day:";
    const char* kBrightnessNightValue = "brightness night:";
  
  if (cmd.startsWith(kBrightnessNightValue)) {
    String value = cmd.substring(String(kBrightnessNightValue).length());
    max_brightness_of_night = value.toInt();
  } else if (cmd.startsWith(kBrightnessDayValue)) {
    String value = cmd.substring(String(kBrightnessDayValue).length());
    min_brightness_of_day = value.toInt();
  }
}

void processCmd(String cmd) {
  if (cmd.equals(kCmdLightOn)) {
    digitalWrite(kDPinLight, LOW); // Turn on
    Serial.println(kCmdLightOn);
  } else if (cmd.equals(kCmdLightOff)) {
    digitalWrite(kDPinLight, HIGH); // Turn off
    Serial.println(kCmdLightOff);
  } else if (cmd.startsWith(kCmdSetPrefix)) {
    processSettingCmd(cmd.substring(kCmdSetPrefix.length()));
  }
}

// Push partial string of command
// and return a command when there is a command completed
String pushCommandFragment(String fragment) {
  static String buffer;
  
  buffer += fragment;
  int idx = buffer.indexOf(kPacketDelimiter);
  if (idx == -1) {
    return "";
  }
  
  String complete_cmd = buffer.substring(0, idx);
  if (buffer.length() > idx + 1) { 
    buffer = buffer.substring(idx + 1);
  } else {
    buffer = "";
  }
  
  return complete_cmd;
}

// check activity is detected 
// and create a response packet if response is needed.
String checkActicityAndCreatePacket(int activity) {
  static int last_status = 0;
  // prevent creating duplicated packets.
  if (last_status == activity) {
    return "";
  }
  last_status = activity;
  
  if (!activity) {
    return "";
  }
  
  String packet = kStatusActivityDetected;
  packet += kPacketDelimiter;
  return packet;
}

// The loop function runs over and over again forever
void loop() {
  String send_packet = "";
  
  int activity = digitalRead(kDPinActivity);
  send_packet += checkActicityAndCreatePacket(activity);
  //Serial.print("activity: ");
  //Serial.println(activity);
  
  int brightness = analogRead(kAPinBright);
  //Serial.print("bright: ");
  //Serial.println(brightness);
  
  // Parse command from remote controller
  String cmd_fragment = "";
  while (mySerial.available()) {
    char c = mySerial.read();
    cmd_fragment.concat(c);
    delay(1);
  }

  while(1) {
    // Combine the fragment with the ones recieved before.
    String cmd = pushCommandFragment(cmd_fragment);
    if (cmd.length() == 0) {
      break;
    }
    
    processCmd(cmd);
    Serial.print("command in: ");
    Serial.println(cmd);
    
    // run more loop with empty fragment
    // in case that there are more packets available.
    cmd_fragment = "";
  }
  
  if (Serial.available())
    mySerial.write(Serial.read());

  // send packet if there's one to send.
  if (send_packet.length() > 0) {
    mySerial.print(send_packet);
  }
}

 

간략 설명

  1. 아두이노 모션 센서에서 인체 동작이 인식이 되면 블루투스를 통해 안드로이드 앱으로 패킷을 보냄.
  2. 안드로이드 앱에서는 모션이 인식되면 구글 음성 인식서비스를 작동시킴.
    1. 음성인식 서비스를 항상 활성화시키면 좋겠지만 음성인식 라이브러리 자체가 최대 5초까지만 작동하게 제한되어 있음.
    2. 5초마다 재시작 시키는 방식도 고려했지만 각 재시작시 생기는 딜레이로 음성인식이 안되는 문제가 있음.
    3. 따라서 사람의 모션을 인식했을때만 작동하는 방식이 가장 효율적임.
  3.  음성인식 결과 문자열중 미리 입력해둔 명령어가 있다면 해당 명령어를 아두이노로 전송
    1. 현재 명령어는 : light on, light off의 두 가지.
    2. 명령어를 말하기 전에 Signal Speech를 말해야 하는데 현재 입력된 값은 “Lucy”임
    3. 따라서 “Lucy light on” 이라고 말해야 정상 인식
  4. 아두이노에서는 해당 명령 처리.
    1. 가령 light on이 전달되면 릴레이에 신호를 주어 전등을 켬.