산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

알렉사 연동 아두이노 홈 컨트롤러: 음성으로 스마트홈 제어하기

부품 및 소모품

부품수량
Arduino Yun1
ControlEverything.com 4채널 I2C 릴레이 컨트롤러1
DHT11 온습도 센서 (4핀)1
10kΩ 저항2
SparkFun RedBot 부저1
범용 NPN 트랜지스터1
LM2596 DC-DC 벅 컨버터1
Amazon Alexa Echo Dot1
아두이노 프로토 실드1
USB 웹캠1
흰색 정션 박스 (200×155×80mm)1

필요한 도구 및 장비

앱 및 온라인 서비스

프로젝트 개요

개요

아마존 알렉사와 에코 닷 출시로 자연어 음성 명령으로 제어하는 스마트홈의 가능성이 열렸습니다. 이 프로젝트는 다양한 센서와 기기를 인터넷에 연결하고, 아마존 알렉사 스킬로 제어하는 아두이노 홈 컨트롤러를 만듭니다.

제 스킬 "Arduino Home Controller"를 설치해 바로 쓰거나, 이 튜토리얼을 따라 직접 스킬을 만들 수 있습니다.

제어 대상

데모 영상에서는 브레드보드 위 LED 4개로 각 방 조명을 시뮬레이션했습니다. 원격 모듈로 실제 조명을 제어하려면 제 다른 프로젝트 "Arduino Home Controller Remote Modules"를 참조하세요.

블록 다이어그램

4대 핵심 구성요소:

  1. Amazon Echo Dot: 음성 명령 수신 → AWS 전송
  2. AWS (Alexa Skill + Lambda Function): 명령 해석. 스킬명 "Arduino Home Controller"
  3. MQTT 브로커 (PubNub 클라우드): 제어 메시지 중계. 설정/읽기용 2개 채널 사용. 데모용으로 제 PubNub 계정 사용 가능.
  4. 홈 컨트롤러 (Arduino Yun): 이더넷 내장 + 리눅스 머신 탑재. 파이썬으로 일부 동작 처리. 릴레이 제어, 센서 읽기, 부저, 웹캠 제어.

소프트웨어 설치 및 설정

옵션 1: 데모용 - 내 스킬 사용

알렉사 앱에서 "Arduino Home Controller" 스킬 검색 → 활성화 → 아마존 계정 연동 (이메일은 ID용, 메일 발송 안 함).

옵션 2: 직접 스킬 만들기

1. 스킬 생성 (Amazon Developer Console)

2. Lambda 함수 생성 (AWS Console)

3. 스킬 설정 마무리

4. 테스트 스킬 설치 알렉사 앱 → Your Skills → Dev Skills → TestSkill 활성화 → 계정 연동 → "알렉사, 테스트 스킬에게 물어봐"로 테스트.

아두이노 Yun 설정

처음 사용하는 경우 Greg Baugues 가이드 참조해 초기 설정 필요.

추가 소프트웨어 설치 (SSH 접속 필요):

ssh root@yourYunName.local  # 기본 비밀번호: arduino
opkg update
opkg install kmod-video-uvc      # UVC 드라이버
opkg install python-openssl      # 이메일용
opkg install fswebcam            # 사진 촬영 유틸

SD 카드 마운트 확인(mount → /dev/sda1 on /mnt/sda1 type vfat), 카메라 테스트(fswebcam test.png).

이메일 발송 파이썬 스크립트 (AHC_SendEmail.py) SD 카드에 복사 후 Gmail 계정/비밀번호, 서버 정보 수정. 테스트: python /mnt/sda1/AHC_SendEmail.py /mnt/sda1/ test.png

하드웨어 연결: 회로도 참조해 DHT11, 부저 등 연결.

스케치 업로드 (ArduinoHomeMQTT.ino): 필요 라이브러리(ArduinoJson, DHT_sensor_library, pubsubclient-master)를 libraries.zip으로 제공. 아두이노 IDE에서 보드 "Arduino Yun" 선택, 코드 내 Amazon 이메일(16번 줄)과 UUID(50번 줄) 수정 후 업로드. 시리얼 모니터로 PubNub 연결 및 센서 초기화 확인.

알렉사 발화 예시 테스트

소프트웨어 상세 구조

GitHub에서 전체 파일 다운로드 가능.

워크플로우: (1) Echo Dot → 음성 명령 → Skill (2) Skill → Lambda 함수 (index.js) (3) Lambda → 의도 해석 → MQTT JSON 메시지 → PubNub 브로커 (4) 메시지 형식: {"topic": "Topic_xxxx", "command": "on/off", "id": "이메일주소"} (5) 채널 AHC_IOT_01로 발행, AHC_IOT_02로 응답 수신 (6) 아두이노 윤 스케치 → 브로커 구독/발행 (7) Bridge로 리눅스와 통신 (WAN/LAN, 명령 실행) (8) 이메일 전송 시 파이썬 스크립트, 사진 촬영 시 fswebcam 호출

하드웨어 상세

핵심: Arduino Yun. 디지털 핀 할당:

Pin 2: DHT11 센서
Pin 8: 부저 알람 (NPN 트랜지스터로 구동)
Pin 9: 침실 조명
Pin 10: 주방 조명
Pin 11: 차고 조명
Pin 12: 거실 조명

웹캠: USB 포트 연결. 12V DC 전원 → 벅 컨버터로 5V 변환해 전체 공급. 프로토 실드에 커넥터, 인터페이스 회로, 전원 회로 구성. 확장용 여분 공간 확보. 플라스틱 박스에 아두이노 윤, 실드, 릴레이, 레귤레이터 수납, 알람/웹캠/센서는 외부에 노출.

데모용으로는 브레드보드 LED 4개로 조명 시뮬레이션.

이 가이드를 읽어주셔서 감사합니다. 제안 사항은 연락 주세요. 작년 돌아가신 아버지를 추모하며 이 프로젝트를 만듭니다. R.I.P Dad.

코드

ArduinoHomeMQTT.ino (아두이노)

// ArduinoHomeMQTT v2.1
// Copyright (C)2018 Jose Cruz. All rights reserved.
// web: https://sites.google.com/view/raeiot/home
#include <YunClient.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <Process.h>

#define MyID "YOUR AMAZON EMAIL LINKED IN ALEXA APP"
#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
#define alarm 8
#define room_light 9
#define kitchen_light 10
#define garage_light 11
#define livingroom_light 12

int mqtt_server_port = 1883;
const char* mqtt_server = "mqtt.pndsn.com";
const char* topic = "AHC_IOT_01";
const char* topic2 = "AHC_IOT_02";
const char* clientId = "pub-c-e93def7f-95aa-475a-aa60-cc3cd32ee8a7/sub-c-ec04dbbc-0893-11e8-8e75-cea83f8405bb/YOUR UUID GENERATED";

YunClient ethClient;
PubSubClient client(ethClient);
Process picture;
String filename;
String path = "/mnt/sda1/";

void TakePhoto() {
  filename = "";
  picture.runShellCommand("date +%s");
  while (picture.running());
  while (picture.available() > 0) { char c = picture.read(); filename += c; }
  filename.trim(); filename += ".jpg";
  picture.runShellCommand("/usr/bin/fswebcam -i 0 --jpeg 95 --no-banner --fps 1 -S 1 -r 352x288 --save " + path + filename);
  while (picture.running());
  picture.runShellCommand("python /mnt/sda1/AHC_SendEmail.py " + path + " " + filename);
  while (picture.running());
}

void SendTemperature() {
  char cstr[16];
  float t = dht.readTemperature();
  client.publish(topic2, itoa(t, cstr, 10));
}

void SendHumidity() {
  char cstr[16];
  float h = dht.readHumidity();
  client.publish(topic2, itoa(h, cstr, 10));
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] ");
  for (int i = 0; i < length; i++) Serial.print((char)payload[i]);
  Serial.println();
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject((char*)payload);
  String alexa_topic = root["topic"];
  String alexa_command = root["command"];
  String alexa_id = root["id"];
  if (alexa_id.equals(MyID)) {
    if (alexa_topic.endsWith("Topic_room")) digitalWrite(room_light, alexa_command.endsWith("on") ? LOW : HIGH);
    else if (alexa_topic.endsWith("Topic_kitchen")) digitalWrite(kitchen_light, alexa_command.endsWith("on") ? LOW : HIGH);
    else if (alexa_topic.endsWith("Topic_garage")) digitalWrite(garage_light, alexa_command.endsWith("on") ? LOW : HIGH);
    else if (alexa_topic.endsWith("Topic_living room")) digitalWrite(livingroom_light, alexa_command.endsWith("on") ? LOW : HIGH);
    else if (alexa_topic.endsWith("Topic_temperature")) SendTemperature();
    else if (alexa_topic.endsWith("Topic_humidity")) SendHumidity();
    else if (alexa_topic.endsWith("Topic_alarm")) digitalWrite(alarm, alexa_command.endsWith("on") ? HIGH : LOW);
    else if (alexa_topic.endsWith("Topic_photo")) TakePhoto();
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(clientId)) {
      Serial.println("MQTT broker connected");
      client.subscribe(topic);
    } else {
      Serial.print("Failed, rc = "); Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void setup() {
  Bridge.begin(); delay(2000);
  Serial.begin(115200);
  Serial.println("Init Arduino Home Controller v2.1...");
  pinMode(alarm, OUTPUT);
  pinMode(room_light, OUTPUT);
  pinMode(kitchen_light, OUTPUT);
  pinMode(garage_light, OUTPUT);
  pinMode(livingroom_light, OUTPUT);
  digitalWrite(room_light, HIGH);
  digitalWrite(kitchen_light, HIGH);
  digitalWrite(garage_light, HIGH);
  digitalWrite(livingroom_light, HIGH);
  client.setServer(mqtt_server, mqtt_server_port);
  client.setCallback(callback);
  client.connect(clientId);
  Serial.println("Connected to PubNub MQTT broker OK ...");
  client.subscribe(topic);
  Serial.println("Suscribe to topic at MQTT broker Ok ...");
  dht.begin(); delay(4000);
  Serial.println("DHT sensor Ok ...");
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();
}

AHC_SendEmail.py (파이썬)

import smtplib, sys, time
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import encoders

fromaddr = "joseacruzp@gmail.com"
toaddr = "joseacruzp@gmail.com"
emailpassword = "YOUR PASSWORD"
subject = "Arduino Home Controller by Alexa Skill"
body = "Photo taken in " + time.strftime('%a, %d %b %Y %H:%M:%S %Z(%z)')
filedir = sys.argv[1]
filename = sys.argv[2]

msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))

attachment = open(filedir + filename, "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, emailpassword)
server.sendmail(fromaddr, toaddr, msg.as_string())
server.quit()

index.js (AWS Lambda, Node.js)


// Alexa SDK for Arduino Home Controller
// Copyright (c) 2014-2015 Amazon.com, Inc. or its affiliates.
var PubNub = require("pubnub");
var pubnub = new PubNub({
  ssl: true,
  publish_key: "pub-c-e93def7f-95aa-475a-aa60-cc3cd32ee8a7",
  subscribe_key: "sub-c-ec04dbbc-0893-11e8-8e75-cea83f8405bb",
  uuid: "YOUR UUID"
});
var channel = 'AHC_IOT_01';
var channel2 = 'AHC_IOT_02';
var slotStates = ['on', 'off'];
var APP_ID = 'YOUR APP ID FOR THE SKILL';
var AlexaSkill = require('./AlexaSkill');
var ArduinoHomeSkill = function() { AlexaSkill.call(this, APP_ID); };

var mqttListener, access_token, url;
var https = require("https");

function SendMessage(topicname, commandvalue, speechOutput, iresponse) {
  https.get(url, res => {
    res.setEncoding("utf8");
    let body = "";
    res.on("data", data => { body += data; });
    res.on("end", () => {
      pubnub.publish({
        channel: channel,
        message: { "topic": topicname, "command": commandvalue, "id": JSON.parse(body).email }
      }).then(response => {
        console.log("message Published w/ timetoken", response.timetoken);
        if (speechOutput) iresponse.tell(speechOutput);
      }).catch(error => {
        console.log("publishing failed w/ status: ", error);
        iresponse.ask("Sorry, I didn't catch what you said");
      });
    });
  });
}

ArduinoHomeSkill.prototype = Object.create(AlexaSkill.prototype);
ArduinoHomeSkill.prototype.constructor = ArduinoHomeSkill;

ArduinoHomeSkill.prototype.eventHandlers.onSessionStarted = function(sessionStartedRequest, session) {
  console.log("ArduinoHomeSkill onSessionStarted requestId: " + sessionStartedRequest.requestId + ", sessionId: " + session.sessionId);
  pubnub.removeListener(mqttListener);
  pubnub.unsubscribeAll();
};

ArduinoHomeSkill.prototype.eventHandlers.onLaunch = function(launchRequest, session, response) {
  console.log("ArduinoHomeSkill onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
  if (session.user.accessToken == undefined) {
    response.tellWithCardLink('To start using this skill, please use the companion app to authenticate on Amazon.');
    return;
  }
  response.ask("Welcome to Arduino Home Controller. What would you like to do?", "I am ready");
};

ArduinoHomeSkill.prototype.eventHandlers.onSessionEnded = function(sessionEndedRequest, session) {
  console.log("ArduinoHomeSkill onSessionEnded requestId: " + sessionEndedRequest.requestId + ", sessionId: " + session.sessionId);
};

ArduinoHomeSkill.prototype.intentHandlers = {
  "AboutIntent": function(intent, session, response) {
    response.ask("Arduino Home Controller skill lets you control internet connected devices... What would you like to do?");
  },
  "LightIntent": function(intent, session, response) {
    var slotHabRooms = ['room', 'kitchen', 'garage', 'living room'];
    var lightSlotValue = intent.slots.light ? intent.slots.light.value : "";
    var whichSlotValue = intent.slots.which ? intent.slots.which.value : "";
    if (lightSlotValue && whichSlotValue && slotStates.indexOf(lightSlotValue.toLowerCase()) > -1 && slotHabRooms.indexOf(whichSlotValue.toLowerCase()) > -1) {
      SendMessage('Topic_' + whichSlotValue, lightSlotValue, "The light is now " + lightSlotValue, response);
    } else { response.ask("Sorry, I didn't catch what you said"); }
  },
  "AlarmIntent": function(intent, session, response) {
    var alarmSlotValue = intent.slots.alarm ? intent.slots.alarm.value : "";
    if (alarmSlotValue && slotStates.indexOf(alarmSlotValue.toLowerCase()) > -1) {
      SendMessage('Topic_alarm', alarmSlotValue, "The alarm is now " + alarmSlotValue, response);
    } else { response.ask("Sorry, I didn't catch what you said"); }
  },
  "TakePhotoIntent": function(intent, session, response) {
    SendMessage('Topic_photo', 'take', "Taken home photo", response);
  },
  "GetTemperatureIntent": function(intent, session, response) {
    mqttListener = {
      status: function(statusEvent) {},
      message: function(message) {
        response.tell("Inside Temperature is " + message.message + " degrees C");
      },
      presence: function(presenceEvent) {}
    };
    pubnub.addListener(mqttListener);
    pubnub.subscribe({ channels: [channel2] });
    SendMessage('Topic_temperature', 'Ok', null, response);
  },
  "GetHumidityIntent": function(intent, session, response) {
    mqttListener = {
      status: function(statusEvent) {},
      message: function(message) {
        response.tell("Inside Humidity is " + message.message + " %");
      },
      presence: function(presenceEvent) {}
    };
    pubnub.addListener(mqttListener);
    pubnub.subscribe({ channels: [channel2] });
    SendMessage('Topic_humidity', 'Ok', null, response);
  },
  "AMAZON.HelpIntent": function(intent, session, response) {
    response.ask("With Arduino Home Controller skill and Alexa you can control internet connected devices... What would you like to do?");
  },
  "AMAZON.StopIntent": function(intent, session, response) {
    response.tell("Thanks for using Arduino Home Controller. Bye see you later");
  },
  "AMAZON.CancelIntent": function(intent, session, response) {
    response.tell("Thanks for using Arduino Home Controller. Bye see you later");
  },
  default: function(intent, session, response) { response.ask("Try again"); }
};

exports.handler = function(event, context) {
  try {
    access_token = event['context']['System']['user']['accessToken'];
    url = 'https://api.amazon.com/user/profile?access_token=' + access_token;
  } catch (error) { console.log(error); }
  var ArduinoHomeControl = new ArduinoHomeSkill();
  ArduinoHomeControl.execute(event, context);
};

GitHub 저장소

https://github.com/jcruzp/ArduinoHomeController

회로도

알렉사 연동 아두이노 홈 컨트롤러: 음성으로 스마트홈 제어하기

제조공정

  1. 웹 제어 DMX 조명 시스템 - 아두이노 마스터 컨트롤러
  2. 아두이노 디지털 주사위: LCD 기반 랜덤 넘버 제너레이터 만들기
  3. DIY 아두이노 USB 게임 컨트롤러 - 나만의 고성능 게임패드 만들기
  4. 픽셀 체이서: 아두이노 나노로 만든 인터랙티브 원탭 LED 게임
  5. 아두이노 나노 기반 스마트 홈 식물 자동 급수 시스템
  6. 아두이노 기반 척력 자기 부상: 간단 회로 & DIY 가이드
  7. 아두이노 기반 진공 형광 디스플레이(VFD) 컨트롤러 키트
  8. 자율 홈 어시스턴트 로봇 'O'nine' 제작 – ROS, Alexa, MoveIt, 내비게이션 완전 가이드
  9. 아두이노 나노와 네오픽셀 매트릭스로 만드는 네온 퐁 게임
  10. Arduino DMX-512 테스터 컨트롤러 – 안정적인 조명 제어를 위한 풀 파츠 키트