1. $45 스마트 워치 만들기 (DIY How-to)

웨어러블의 바람을 타고 스마트 워치가 혁신의 아이콘이라도 된 듯 여러회사에서 스마트 워치를 내놓기 시작했습니다. 페블에서부터 갤럭시 기어, 소니 MN2?, 퀄컴 토크에 구글과 애플도 가세할 기세니… 하지만 다양한 기능 만큼이나 가격도 비싼 제품인지라 실용성이 확인되지 않은 현 시점에 선뜻 구입하기는 껄끄럽죠. 배터리 문제도 있고 디자인도 그닥…

어차피 전 스마트 워치가 작은 폰이 되는걸 바라지 않으니, 그저 시계 본연의 기능에 폰에서 생긴 중요 이벤트를 알려주는 정도만 해줘도 만족합니다. 그리고 이 정도면 직접 만들어도 될 법해 보여서 아두이노 보드를 이용해서 DIY를 해봤습니다. 제작에 사용된 아두이노 스케치(코드)와 안드로이드 소스 및 실행파일을 맨 아래에 공유해 두었습니다.

parts_resized

준비물입니다. 16×2 Character LCD(왼쪽 위, $15) + 블루투스 슬레이브 모듈(왼쪽 아래, HC-06, $10), 아두이노 Nano(오른쪽 아래, $15) 그리고 집에 남는 배터리와 연결선들 입니다. 아마존 검색하니 주요 모듈이 $40 정도니까 잡다한 부품가격 합해서 $45 정도라고 해두죠. (우리나라에서 구입하는 부품 가격으로 환산하면 꽤 비쌉니다. 호환보드나 카페 공구로 저렴하게 구해보세요.)

16×2 Character LCD 는 디스플레이용 모듈입니다. 사진에서는 가려서 안보이는데 뒤쪽에 LCD 를 I2C를 통해 간편하게 연결할 수 있게 해주는 Interface converter 모듈이 붙어 있습니다. 16글자씩 2줄을 출력하는데 한글사용 불가. 첫 줄에는 시간정보를 표시하고 둘째 줄에는 안드로이드 폰에서 받아온 알림(Notification) 정보를 표시하도록 할겁니다.

블루투스 슬레이브 모듈은 안드로이드와의 통신용.

아두이노 보드는 NANO 버전을 사용했습니다. 보통 많이 사용하는 UNO R3 버전과 기능은 똑같은데 사이즈가 작아서 이놈을 썼습니다.

이제 이놈들을 조립하고 코드를 올려보겠습니다.

.

2. 제작 순서 

2-1. 아두이노

아두이노 Nano 보드는 구입했을 때 핀 구멍만 있고 납땜이 안되어 있어 납땜 작업을 해뒀습니다. 그리고 보드 전원용으로 사용할 사각 9V 전지와의 연결선도 납땜해 두었습니다.

LCD 모듈은 아래 방법으로 연결을 했습니다. I2C Interface converter를 붙여둬서 4개선만 아래 순서대로 연결하면 됩니다. 상세정보는 [여기를] 참고하세요.

LCD 모듈 Arduino Uno Board
GND GND
VCC 5V
SDA A4 (아날로그 4번핀)
SCL A5 (아날로그 5번핀)

블루투스 모듈은 다음과 같이 연결하세요.

VCC -> 3.3V, GND -> GND 연결하고 TX, RX 핀은 아두이노 D2, D3에 연결 했습니다. (HC-06 모듈은 3.3V, 베이스보드에 납땜된 모듈은 5V를 공급해야 한다고 하네요. 전 그냥 3.3V에 해서 동작 됐습니다.)

상세정보는 [여기를] 참고하세요. TX, RX 핀은 다른 핀을 사용해도 될겁니다. 대신, 소스에 핀 지정하는 코드를 수정해야함.

여기까지 완료되면 배선은 끝납니다. 아래는 배선을 마친 모습입니다.

connected_resized

이제 아두이노 코드를 올릴 차례입니다.

아두이노에 전원을 넣으면 “SMART WATCH DIY”, ” Waiting for host..”  메시지를 출력하고 연결되기를 기다립니다. 폰과 연결되고 시간, 알림(Notification) 정보를 받아와야 그때부터 정상 동작합니다. 최대 받아올 수 있는 알림은 20개. 첫 줄에 [AM 07:34 MON] 형태로 시간정보를 표시하고 둘째 줄에 현재 등록되어 있는 Notification 정보를 보여줍니다. ‘@’, ‘#’, ‘+’, ‘;’ 문자는 시간세팅, 알림재설정, 알림1개 추가 시작, 알림1개 추가 종료 커맨드로 사용합니다. 빨간색, 파란색 코드를 유의해서 보세요.

주의 !!! 아래 소스를 컴파일 했을 때 [error: stray ‘\’ in program] 에러가 나시는 분은 인코딩 문제가 생긴겁니다. [도구 -> 인코딩 수정 & 새로고침]을 눌러 인코딩을 변환하고 문제가 생긴 부분들을 수정해 줘야 합니다.

//Compatible with the Arduino IDE 1.0
//Library version:1.1
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#include <SoftwareSerial.h>

LiquidCrystal_I2C lcd(0x27,16,2);  // set the LCD address to 0x20

SoftwareSerial BTSerial(2, 3); //Connect HC-06, TX,RX 핀 번호 설정

/* LCD control */
unsigned long prevDisplayTime = 0;  // Noti를 순환해서 보여주는데 사용
unsigned long curDisplayTime = 0;
int displayInterval = 5000;  // 다음 Noti 를 보여주는 간격
int currentDisplayLine = 0;  // 현재 출력되는 Noti 버퍼의 index
int displayLineLimit = 0;  // 폰에서 받아온 noti의 갯수

/* Notification item buffer */
int noti_max = 20;  // 최대 저장가능한 noti
int noti_char_max = 17;  // 출력 가능한 문자수 + 1
char noti[20][17];  // 받아온 Noti를 저장하는 버퍼, 20라인::17문자
int currentLine = 0;  // 수신한 데이터를 버퍼에 쓰는데 사용하는 index
int currentChar = 0;  //
수신한 데이터를 버퍼에 쓰는데 사용하는 index 

/* Time setting */
int isWritingTime = 0;    // 0: disabled, 1: waiting am/pm, 2: waiting hour, 3: waiting minutes, 4: waiting week
int ampm = -1;    // 0:AM, 1:PM
int hour = -1;
int minute = -1;
int week = -1;
unsigned long prevClockTime = 0; // 1분 간격 시간 업데이트용

void init_noti_array() {  // 버퍼 전체 초기화
for(int i=0; i<noti_max; i++) {
for(int j=0; j<noti_char_max; j++) {
noti[i][j] = ‘\n’;
}
}
}

void setup()
{
Serial.begin(9600);  // PC 시리얼 출력시작
Serial.println(“Initializing”);
BTSerial.begin(9600);  // BT port의 data rate 설정, BT 모듈과 통신 시작

init_noti_array();  // 버퍼 초기화

lcd.init();  // LCD 초기화
lcd.backlight();  // 백라이트 on
lcd.print(“SMART WATCH DIY”);  // 초기 메시지 출력
lcd.setCursor(0, 1);
lcd.print(“Searching host..”);
}

void loop()
{
curDisplayTime = millis();

// BT에서 데이터를 가져오는 루틴
// BT –> Data –> Serial
if (BTSerial.available()) {
char c = BTSerial.read();
Serial.print(c);

// 시간 설정을 받아옴
if(c == ‘@’) {
isWritingTime = 1;  // 시간 설정 시작, AM/PM 값 받기 위해 대기
}
else if(isWritingTime == 1) {
(c == 0) ? ampm = 0 : ampm = 1;
isWritingTime = 2;  // 시간 값 받기 위해 대기
}
else if(isWritingTime == 2) {
hour = c;
isWritingTime = 3;  // 분 값 받기 위해 대기
}
else if(isWritingTime == 3) {
minute = c;
isWritingTime = 4;  // 요일 값 받기 위해 대기
}
else if(isWritingTime == 4) {
week = c;
isWritingTime = 0;  // 시간 설정 완료
}
// 알림 1개 추가 시작
else if(c == ‘+’) {

}
// 알림 1개 추가 종료
else if(c ==’;’) {
// 현재 기록한 라인에 종료문자 설정
noti[currentLine][currentChar + 1] = ‘\n’;

// 문자열 설정이 끝난 상태, 버퍼의 다음 라인으로 index 이동
currentChar = 0;
currentLine++;
if(currentLine >= noti_max) {
currentLine = 0;
}
// 새로 시작할 라인 초기화
for(int j=0; j<noti_char_max; j++) {
noti[currentLine][j] = ‘\n’;
}
// LCD 출력용 index에 1 추가
if(displayLineLimit < noti_max – 1) {
displayLineLimit++;
}
}
// 알림 초기화, 버퍼 전체를 지우고 새로 알림을 받게됨.
else if(c == ‘#’) {
displayLineLimit = 0;
currentDisplayLine = 0;
currentLine = 0;
init_noti_array();
}
// 받은 데이터를 버퍼에 기록. 16자 까지만
else {
if(currentChar < noti_char_max – 1) {
noti[currentLine][currentChar] = c;
currentChar++;
} else {
// Oversized string. Do not add
}
}

}  // End of if (BTSerial.available())

// 데이터를 BT를 통해 Remote 로 전송
// Serial –> Data –> BT
if (Serial.available()) {
BTSerial.write(Serial.read());
}

// LCD 첫째 줄에 시간을 출력, 60초에 한번씩 시간 값 재설정 
  if(minute > -1 && isWritingTime==0) {
if(prevClockTime == 0 || curDisplayTime – prevClockTime > 60000) {
String strTime = “”;
// 1분 증가
minute++;
if(minute >= 60) {
minute = 0;
hour++;
if(hour > 12) {
hour = 1;
(ampm == 0) ? ampm=1 : ampm= 0;
}
}
// 먼저 라인을 지움
lcd.setCursor(0, 0);    // set cursor
lcd.print(”                “);  // Clear line
lcd.setCursor(0, 0);    // set cursor
// AM / PM 출력
if(ampm == 0)
strTime += “AM “;
else
strTime += “PM “;
// Hour
if(hour < 10)
strTime += “0”;
strTime += hour;
// Divider
strTime += “:”;
// Minutes
if(minute < 10)
strTime += “0”;
strTime += minute;
// Week
if(week == 1) strTime += ” SUN”;
if(week == 2) strTime += ” MON”;
if(week == 3) strTime += ” TUE”;
if(week == 4) strTime += ” WED”;
if(week == 5) strTime += ” THU”;
if(week == 6) strTime += ” FRI”;
if(week == 7) strTime += ” SAT”;
lcd.print(strTime);

prevClockTime = curDisplayTime;
}
  }

// LCD 2번째 라인에 알림을 출력
if(curDisplayTime – prevDisplayTime > displayInterval) {
// 받아온 알림을 순환하면서 출력
if(currentDisplayLine < displayLineLimit) {
// Clear line
lcd.setCursor(0, 1);    // set cursor
lcd.print(”                “);

// 알림 출력
lcd.setCursor(0, 1);    // set cursor
for(int i=0; i<noti_char_max; i++) {
if(noti[currentDisplayLine][i] == ‘\n’) {
break;
}
lcd.write(noti[currentDisplayLine][i]);
}

// 다음 알림으로 index 전환
currentDisplayLine++;
if(currentDisplayLine >= displayLineLimit)
currentDisplayLine = 0;
}

prevDisplayTime = curDisplayTime;
}

}

코드 컴파일 후 아두이노에 올려서 LCD에 “SMART WATCH DIY”, ” Searching host..” 메시지가 출력되고, 블루투스 모듈 LED가 깜빡이는 상태가 되면 정상적으로 준비가 된겁니다.

init

2-2. 안드로이드

스마트 워치 안드로이드 앱에서는 폰에서 발생하는 알람을 수집해서 아두이노로 전송하는 역할을 합니다. 최초 BT에 연결되면 시간정보와 알림 전체를 전송하고 이후에는 알림이 발생할 때 하나씩 전송합니다. 60분에 한 번씩 시간정보와 알림 전체를 재전송합니다. 알림이 삭제되었을 경우에는 5초 후에 알림 전체를 재전송 합니다.

안드로이드에서는 알림(Notification)을 수집하는 API가 젤리빈 4.3 버전부터 제공됩니다. 현재 젤리빈 4.3이 올라간 폰이 많지 않아서 테스트하기 어려울 수 있습니다만.. 구현하기 편한 방법이라 그리 하였습니다. (ㅡㅡ;;) 4.3 이하 버전에서 가능한 앱은 추후에 구현해서 공유하겠습니다. 전 넥서스 7 태블릿으로 테스트 했습니다.

안드로이드 소스는 여기서 코드를 모두 설명하기가 힘듭니다. 대강의 구조만 언급하겠습니다. 소스코드와 APK 파일은 아래에서 받으실 수 있습니다.

MainActivity.java : 앱이 시작하면 실행되는 첫 화면으로 화면 구성과 스마트 워치로 전달할 알림을 여기서 처리. 알림 정보 중 package 이름 (예를들면 com.android.xxx)에서 마지막 이름만 추출합니다.(한글과 특수문자가 없어서…) 처리할 필요가 없는 알림이거나 스마트 워치로 전송하기전 이름 변경이 필요한 경우는 mFilters, mConverterBefore, mConverterAfter 배열을 추가, 변경해서 사용하시면 됩니다.

DeviceListActivity.java : BT로 탐색된 Device 목록을 보여주고 연결할 스마트 워치를 선택할 수 있도록 해주는 Dialog 형태의 activity.

BtService.java : BT 연결 설정 및 소켓 통신을 처리하는 class

NLService : Notification 을 받아오는 역할을 하는 Service. Activity에서 직접 접근이 안되므로 broadcast 를 주고받는 형태로 동작.

.

3. 작동 방법

  1. 스마트 워치에 전원 연결
  2. 초기 메시지가 보이고 블루투스 모듈의 LED가 깜빡임
  3. 안드로이드 폰에서 [설정 > 블루투스 설정] 항목에서 블루투스 on 시킴
  4. 스마트 워치 안드로이드 앱 실행
  5. 앱 상단 최우측 Notification setting 선택
  6. 스마트 워치 앱 우측의 체크박스 클릭해서 앱에 알림 접근 권한을 부여
  7. 스마트 워치 앱에 현재 등록된 알림 리스트가 표시됨. (가끔 알림이 있어도 표시되지 않는 경우가 생기는데 이때는 5-6번을 다시 시도
  8. 앱으로 돌아와서 앱 상단 좌측 Connect a device 선택
  9. 스마트 워치 블루투스 모듈의 이름을 선택
  10. 스마트 워치와 연결을 시도
  11. 정상 연결이 될 경우 [스마트 워치 블루투스 모듈의 LED가 on 상태로 유지됨] [스마트 워치 앱에 Connected 토스트가 표시] [스마트 워치에 시간과 알림 정보가 표시됨]

이상의 과정을 보여주는 동영상입니다. 동영상에는 Notification 접근 권한이 이미 설정되어 있는 상태라서 5-6번이 생략되었습니다. 스마트 워치와 태블릿을 연결하고, 제 3의 폰에서 Gmail로 이메일을 하나 보냅니다. 태블릿 스마트 워치 앱에 수신된 메일에 대한 Gmail 알림이 보입니다. 이 알림은 스마트 워치로 전달되어서 Gmail 이 출력됩니다.

.

4. 작업 후기

여기저기서 스마트 워치가 쏟아져나오고 있습니다만 딱 이거다 싶을 만큼 매력적인 녀석은 아직 없는 것 같습니다. 기존 스마트 워치에서 뭔가 부족함을 느끼는 분들이라면 직접 DIY로 자신만의 작품을 시도해 보시길 추천합니다. 지금 필요한 것은 기술 보다도 아이디어니까요.

집에 가지고 있는 부품들로 만들다보니 외모가 좀 빠지네요. 정말 시계다운 디자인을 원하신다면 비용은 조금 더 들지만 유용한 방법들이 있습니다. 해외 구매의 부담이 있긴 하지만요.

블루투스 포함한 초소형 아두이노 보드 :  Cortado, BLEduinoRFduino – 저전력 Bluetooth LE 모듈을 자체 내장한 보드들 입니다. 특히 RFduino 는 손가락 한마디 보다도 작습니다!!

초소형 I2C OLED display : http://www.ebay.com/sch/i.html?_nkw=i2c+oled – 상대적으로 저전력에 0.97inch 사이즈!!

올라갈 코드는 Display 부분만 수정해주면 되겠고, 문제는 배터리인데.. 시계용 동그란 수은전지 같은걸 겹쳐 사용하면 될 듯 합니다. 이상을 조합하면 실제 차고 다닐만한(?!) 결과물이 나올겠네요. 부품 구입하면 시도해 봐야겠습니다.

즐거운 DIY 하세요.