HomeAbout Me

음성으로 TV제어하기 - 2.TV 제어

By kuper0201
Published in 임베디드
2020-03-03
2 min read
음성으로 TV제어하기 - 2.TV 제어

Table Of Contents

01
보드/라이브러리 추가하기
02
프로그래밍
03
회로 구성
04
테스트

이 전 글에서는 리모컨의 적외선 신호를 복제하는 방법에 대하여 다뤘다. 이번 글에서는 복제한 신호를 이용해 실제로 TV를 제어 할 수 있는 장치를 만들어 보도록 하겠다.

아두이노를 이용해 외부와의 연결 없이 테스트를 할 수도 있지만, 다른 장치에서 인식된 음성으로 제어하기 위해 외부와의 연결이 필요하다. 물론 음성인식 엔진을 아두이노에 직접 올린다면 오프라인으로 음성 인식과 제어 둘 다 가능하겠지만, 성능상의 문제와 직접 음성인식 엔진을 구현해야 한다는 문제가 있다. 이 때문에 필자는 WIFI 모듈을 이용하여 외부의 음성인식 엔진과 통신을 하는 방식으로 구현 할 생각이다.

ESP-01 모듈은 Espressif Systems 에서 개발한 와이파이 모듈로, 가격이 저렴하고 소형이기 때문에 와이파이 모듈 중에서는 가장 범용적이고, 상당히 많은 개발 환경을 지원한다고 한다. 겉핥기 식이긴 하지만, 직접 다른 개발 환경(Lua, Sming)을 사용해 본 결과 복잡하고 불편하다고 느꼈기 때문에 이 글에서는 비교적 간단한 아두이노 스케치를 이용할 것이다.


보드/라이브러리 추가하기

ESP-01을 아두이노 스케치로 프로그래밍 하려면 먼저 아두이노 스케치에 보드 정보와 라이브러리를 추가 해 주어야 한다. 아두이노 IDE의 Preferences로 가서 "추가적인 보드 매니저 URLs"에 아래의 링크를 추가 해 주도록 한다.

http://arduino.esp8266.com/stable/package_esp8266com_index.json

그 후, 툴-보드-보드 매니저를 실행하여 검색 창에 esp를 입력하면 esp8266 보드를 찾을 수 있을 것이다. 설치하도록 하자.

이렇게 하면 일단 보드에 관한 설정은 끝난 것이다. 이제 사용할 라이브러리들을 추가해 주도록 하겠다. 아두이노 IDE의 스케치-라이브러리 포함하기-라이브러리 관리를 클릭하면 라이브러리 매니저가 보일 것이다. 검색 창에 irremoteesp 라고 입력 하여 esp01에서 적외선 센서를 제어 할 수 있게 해 주는 라이브러리를 설치하자.


프로그래밍

위의 모든 작업을 마쳤으면 파일-새 파일을 클릭해 새 파일을 만들고, 내부의 모든 내용을 지우고 아래의 코드를 붙여넣기 하면 된다. 와이파이 변수 부분과 리모컨 각각 기능의 신호값은 자신의 환경에 맞게 설정하도록 하자.

코드 보기
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>

const char* ssid = "자신의 WIFI ssid";
const char* password = "자신의 WIFI 암호";

IRsend send(3);

WiFiUDP Udp;
unsigned int localUdpPort = 8011;
char incomingPacket[255];

void setup() {
  pinMode(3, FUNCTION_3);
  pinMode(3, OUTPUT);
  digitalWrite(3, LOW);

  pinMode(2, OUTPUT);
  digitalWrite(2, LOW);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) delay(500);

  Udp.begin(localUdpPort);
}

void tvTurn() {
  //전원 상태 변경
  send.sendNEC(0x1FE807F, 32);
  send.sendNEC(0x1FEFF00, 32);
  send.sendNEC(0xE0E040BF, 32);
  send.sendNEC(0x20DF10EF, 32);
  send.sendNEC(0x1FE8F80, 32);
}

void volumeUp() {
  //볼륨 업
  send.sendNEC(0xE0E0E01F, 32);
  send.sendNEC(0x20DF40BF, 32);
}

void volumeDown() {
  //볼륨 다운
  send.sendNEC(0xE0E0D02F, 32);
  send.sendNEC(0x20DFC03F, 32);
}

void channelUp() {
  //채널 업
  send.sendNEC(0x1FE02FD, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void channelDown() {
  //채널 다운
  send.sendNEC(0x1FE827D, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void zero() {
  //0
  send.sendNEC(0x1FE04FB, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void one() {
  //1
  send.sendNEC(0x1FE847B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void two() {
  //2
  send.sendNEC(0x1FE44BB, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void three() {
  //3
  send.sendNEC(0x1FEC43B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void four() {
  //4
  send.sendNEC(0x1FE24DB, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void five() {
  //5
  send.sendNEC(0x1FEA45B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void six() {
  //6
  send.sendNEC(0x1FE649B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void seven() {
  //7
  send.sendNEC(0x1FEE41B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void eight() {
  //8
  send.sendNEC(0x1FE14EB, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void nine() {
  //9
  send.sendNEC(0x1FE946B, 32);
  send.sendNEC(0xFFFFFFFF, 32);
  send.sendNEC(0x1FEFF00, 32);
}

void loop()
{
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    int len = Udp.read(incomingPacket, 255);

    if (len > 0) incomingPacket[len] = 0;

    if (strcmp("trn", incomingPacket) == 0) tvTurn();
    else if (strcmp("cUp", incomingPacket) == 0) channelUp();
    else if (strcmp("cDn", incomingPacket) == 0) channelDown();
    else if (strcmp("vUp", incomingPacket) == 0) volumeUp();
    else if (strcmp("vDn", incomingPacket) == 0) volumeDown();
    else if (strcmp("0", incomingPacket) == 0) zero();
    else if (strcmp("1", incomingPacket) == 0) one();
    else if (strcmp("2", incomingPacket) == 0) two();
    else if (strcmp("3", incomingPacket) == 0) three();
    else if (strcmp("4", incomingPacket) == 0) four();
    else if (strcmp("5", incomingPacket) == 0) five();
    else if (strcmp("6", incomingPacket) == 0) six();
    else if (strcmp("7", incomingPacket) == 0) seven();
    else if (strcmp("8", incomingPacket) == 0) eight();
    else if (strcmp("9", incomingPacket) == 0) nine();
  }
}


회로 구성

위의 코드를 보드에 업로드 하기 위해서 회로를 구성해야 한다. 하지만 ESP-01모듈에는 펌웨어를 업로드할수 있는 프로그래머가 없기 때문에 FTDI라는 외장 프로그래머를 사용해야 한다. 아래 그림에서 빨간 부품이 FTDI이고 파란 부품이 ESP-01이다.

플래싱 회로 구성
플래싱 회로 구성

회로 구성을 완료 한 후 아두이노 IDE의 툴-보드를 Generic ESP8266 Module로 설정하고 컴파일/업로드를 한다. 업로드 중에 보드에 연결중이라는 메세지가 나오면 ESP-01의 CH_PD핀을 뽑았다가 다시 꽂아준다. ESP-01의 CH_PD핀은 MCU의 활성 상태를 나타내는데, High이면 활성 Low이면 비활성 상태이다. 핀을 뽑았다가 다시 꽂아주는 행위는 esp8266칩(MCU)을 재부팅 시킨다 라고 이해하면 되겠다.

이렇게 보드에 업로드작업까지 완료했다. 이제 펌웨어가 구워진 보드를 가지고 실제로 TV를 제어할 수 있도록 플래싱용 회로를 실행용 회로로 다시 꾸며야 한다. 아래의 그림과 같이 적외선 LED를 포함하여 회로를 재구성하도록 하자.

실행 회로 구성
실행 회로 구성


테스트

마지막으로 완성된 회로가 잘 동작하는지 확인하기 위해 직접 UDP로 신호를 보내보겠다. UDP신호를 생성하기 위해 여기의 프로그램(PacketSender)을 다운로드/설치 실행 한다.

Packetsender 설정
Packetsender 설정

그럼 위와 같은 창이 열릴텐데 ASCII 부분에는 전송하고싶은 메세지 (여기에서는 TV를 켜거나 끄라는 의미로 trn을 넣었다), Address는 ESP-01의 ip주소, Port는 8011로 설정한 뒤 TCP라고 써있는 부분을 클릭해 UDP로 변경해 주도록 한다. 그리고 Send버튼을 클릭하면 ESP-01로 TV를 켜라는 메세지가 가게 된다.

TV앞에 두고 UDP신호를 전송하여 잘 동작이 되는지 테스트 하면 된다. 만일 TV앞에 가는것이 귀찮거나 불가능하다면 휴대폰의 카메라를 통해 적외선 LED를 보면 적외선 LED가 깜빡이는지 확인 할 수 있다. 휴대폰 카메라를 이용한 확인은 적외선 신호가 잘 나오는지 아닌지에 대해서만 확인 가능하며, 신호가 잘 나온다 하더라도 잡음이나 신호 캡쳐의 문제 등 다른 이유에 의해 TV의 전원은 on/off되지 않을 수 있다.

이번에는 udp 패킷을 수동으로 생성하여 신호를 전송하는 방식으로 구현했으므로 다음 글에서는 특정 음성이 인식되면 자동으로 udp 패킷을 생성하여 신호를 전송하도록 해 보겠다.


Tags

#embedded#projects
Previous Article
음성으로 TV제어하기 - 1.신호 복제
kuper0201

kuper0201

안녕하세요!

Related Posts

음성으로 TV제어하기 - 3.음성 인식
음성으로 TV제어하기 - 3.음성 인식
2020-03-04
2 min

Quick Links

HomeAbout Me

Social Media