2015년 7월 17일 금요일

구글글래스와 온도계

이전 편에서 구글글래스를 '기술적인 난이도를 전혀 고려 하지 않고~' 선택하는 바람에 ..Orz..

'음성 명령 (온도?) -> 온도 센서 읽기 -> 구글글래스에 온도 보여 주기' 를 하기 위해 공부 해야 할 것이 너무 많았습니다.

우선,
1. 구글글래스를 쓸 줄 모릅니다.ㅠㅠ
2. 안드로이드 개발을 해 본 적이 없습니다.
3. 구글글래스와 PC 통신 방법을 모릅니다.
4. PC 에서 아두이노를 통해 센서 값을 읽어 오는 방법을 모릅니다.

아!!!! 왜 제가 구글 글래스를 선택 했었을 까요~~~
며칠 동안 힘들게 공부를 마치고 겨우겨우 위 4가지 모르는 부분을 공부하고 동작하는 sample 을 만들 었습니다. 지금 부터 정리 해 보겠습니다.

서두에 말씀 드리지만 전 위 4가지 모두 초보자 입니다. 초보의 눈높이로 설명하기 때문에 수정이 필요하거나 부연 설명이 있으면 코멘트 부탁 드립니다.

1. 구글 글래스 사용 방법
  구매하고 어떻게 켜는 지 몰라서 10분 고생 했습니다.
  박스에 뭐가 있을까 싶어서 한참 뒤적거렸는데 유일한 설명은 아래 사진 밖에 없습니다.

구글글래스 사용법
  위부터 차례대로 '듣고 / 켜고_끄고 / 터치하고 / 사진찍고' 입니다.

  맨 처음 켜는 버턴을 눌러 봤습니다.
  화면에 GLASS 라는 흰색 글씨가 나옵니다. 그리고는 화면의 변화가 없습니다.
  '부팅인가?' 라고 생각하기 좀 그랬던 것이 진행을 알려주는 뭔가 (깜빡거림 같은거)가 있어야 하는데 그냥 화면 유지 됩니다. 적응하는데 많이 당황했습니다.
  시간을 재 보니 30초 정도가 부팅 시간에 소요 됩니다.

30초 부팅 화면
  그렇게 부팅이 끝나면 시계가 나옵니다.

부팅 후 첫 화면
전 중고로 구매 했기 때문에 구글 계정이나 wifi 설정을 다시 해 줬습니다.
설정을 하기 위해서는 사진에서 '터치'에 해당하는 안경 바깥 부분을 앞뒤로 터치해서 맨 왼쪽 화면 으로 이동합니다.

설정 메뉴
  이 화면에서 한번 터치 해 주면 하위 메뉴로 들어가서 와이파이/블루투스/기타 동작 들을 설정할 수 있습니다.

  그리고 한가지 주의 할 점은 구글글래스 펌웨어를 최신으로 맞춰 줘야 합니다.

펌웨어 버젼 확인
  현재 기준으로 XE22 가 최신 버젼입니다.
  업그레이드는 별도로 해 주지 않으셔도 인터넷이 연결 되어 있다면 자동으로 됩니다.
  제가 구매한 버젼이 XE17 이었는데 XE22 까지 차례 차례 계속 업그레이드를 하더군요.
  거의 반나절은 업그레이드만 한 것 같습니다.

  여기서 문제 상황이 하나 발생했습니다.
  XE17 이후에는 한글 폰트가 바이너리에서 빠져 있어서 한글이나오지 않는 문제가 있습니다.
  다행히 폰트를 복사 해 주는 방법이 있습니다.

출처 : http://www.appilogue.kr/2844555
참고 : https://developers.google.com/glass/tools-downloads/system

NanumGothic.ttf 을 구글글래스에 복사하는 방법입니다.
  1. NanumGothic.ttf 을 http://goo.gl/xdVtD2 다운로드 합니다.
  2. boot.img 를 https://dl.google.com/glass/xe22/boot.img (for XE22) 받습니다
2-1. 위의 것은 루팅 바이너리 이기 때문에 나중에 순정으로 돌리기 위해  http://dl.google.com/glass/xe22/signed-glass_1-img-1511057.zip 다운로드 합니다.
PC 에 adb 와 fastboot 을 설치 해 줍니다.
  1. 구글글래스의 개발자 모드를 켜 줍니다. (위 펌웨서 정보를 보는 창에서 터치하면 세부항목에 'Turn on debug' 라는 것이 있습니다
  2. Terminal 을 열고 아래 처럼 해 줍니다.
$ adb reboot-bootloader
$ fastboot flash boot boot.img
$ fastboot reboot
$ adb root
$ adb remount
$ adb push NanumGothic.ttf /system/fonts/
$ adb reboot-bootloader
$ fastboot flash boot boot.img
$ fastboot reboot
  뭔가 많이 복잡하지만, 한글을 볼 수 있다면 이정도는 감내해야 하는 ... 세계네요 ㅠㅠ

  마지막으로 휴대폰에 'My Glass' 라는 앱을 설치해 줍니다.
  이것을 통해 '스크린캐스트- 글래스 화면을 휴대폰에 보여줌' 도 쓸 수 있고 glass app store 에 있는 다양한 app 도 설치 할 수 있습니다.

2. 안드로이드 개발 (구글 글래스에 국한)
  안드로이드 개발 경험이 없어서 오로지 google glass 개발자 페이지 가이드만 따라 했습니다.
  개발 환경 구축을 위한 Quick Start 를 보고 Android-Studio 를 설치 했습니다.

  자세한 설명은 링크 페이지로 대체 하겠습니다.
  1. android studio 설치
  2. sample source 받기 (https://github.com/googleglass/gdk-apidemo-sample.git)
  3. 빌드해서 구글글래스와 usb 연결하고 실행해 보기

  android studio 가 뭔지 처음 사용해 보는 제가 페이지의 가이드대로 1시간 남짓한 시간에 sample app 을 돌려 볼 수 있었습니다. 초보자가 따라하기 무난한 수준인것 같습니다.

3. 구글글래스와 PC 통신
  여기까지 하고가서... 긴 공백이 있었습니다.
  안드로이드를 전혀 모른 상태에서 PC 와 통신을 한다는 것은 아무래도 무리가 있었습니다.
  우여곡절 끝에 Wifi 가 모두 연결 되어 있기 때문에 socket 통신을 선택 했습니다.
  구글글래스에는 안드로이드 app 개발을 하고 Ubuntu PC 에는 C 로 unix socket 서버를 작업 했습니다.

  2번 항목에서 받은 gdk-apidemo-sample 코드에 약간의 수정을 했습니다.
  - 안드로이드에서 socket 을 사용하려면 Manifest 파일에 아래 권한을 추가 해야 합니다.
<uses-permission android:name="android.permission.INTERNET"/>
  추가로 '음력입력' 또 구현 할텐데 영어 입력이 아닌 한국어 입력을 위해 아래도 추가 합니다.
<uses-permission android:name="com.google.android.glass.permission.DEVELOPMENT"/>
  그리고 Card 디렉토리에 SimpleSocket 을 하나 추가 해 줍니다.
package com.google.android.glass.sample.apidemo.card;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RemoteViews;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.glass.timeline.LiveCard;

/** * Created by gouache on 15. 7. 17. */public class SimpleSocket {
private String return_msg;
private int isDone = 0;

public void create() {
TCPclient tcpThread = new TCPclient("temperature");
Thread thread = new Thread(tcpThread);
Log.d("TCP", "L: 작업시작");
thread.start();
while (isDone == 0) {
Log.d("TCP", "L: 조회중");
}

Log.d("TCP", "L: 작업완료");
}

public String getMsg () {
return return_msg;
}
private class TCPclient implements Runnable {
private static final String serverIP = "192.168.0.17";
private static final int serverPort = 9999;
private Socket inetSocket = null;
private String msg;

public TCPclient(String msg) {
this.msg = msg;
}

public void run() {
try {
Log.d("TCP", "L: 연결중...");
inetSocket = new Socket(serverIP ,serverPort );

try {
Log.d("TCP", "L: 명령어: '" + msg + "'");
PrintWriter out = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(
inetSocket.getOutputStream())), true);

out.println(msg);
Log.d("TCP", "L: 전송 완료");

BufferedReader in = new BufferedReader(
new InputStreamReader(inetSocket.getInputStream()));
return_msg = in.readLine();

Log.d("TCP", "L: 현재 온도는? " + return_msg);
isDone = 1;

} catch (Exception e) {
Log.e("TCP", "E: 전송 실패", e);
} finally {
inetSocket.close();
}
} catch (Exception e) {
Log.e("TCP", "E: 소켓 생성 실패", e);
}
}
}
}

  여기까지 하면 소켓 통신까지 준비가 된 것이구요.
  추가로 '음성인식'을 위해 string.xml 파일에 voice_trigger 를 설정 해 줍니다.
<string name="voice_trigger">ondo</string>
  영어로 temperature 라고 쓸 수도 있지만 아들 녀석이 할 수 있어야 하기 때문에 한글발음의 온도를 영어로 'ondo' 라고 적었습니다. 동작 잘 합니다.

  마지막으로 '온도' 라고 실행 되었을 때 온도를 받아 오는 실제 코드를 CardBuilderActivity.java
  에 수정해 넣습니다.
cards.add(new CardBuilder(context, CardBuilder.Layout.TEXT)
.setText(mSocket.getMsg()));
/*cards.add(new CardBuilder(context, CardBuilder.Layout.TEXT) .setText(R.string.text_card_text_not_fixed) .setFootnote(R.string.text_card_footnote) .setTimestamp(R.string.text_card_timestamp) .setAttributionIcon(R.drawable.ic_smile));*/
  주석이 원래 코드구요 위에 코드가 추가한 코드 입니다.
  여기 까지가 구글글래스에서 '온도' 로 실행 된 app 에서 socket client 를 동작 시키는 방법입니다.

  다음은 ubuntu pc 에서 socket server 를 돌려 보겠습니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

#define BUF_LEN 128
intopen_port(void)
{
int fd;

fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyACM0 - ");
} else fcntl(fd, F_SETFL, 0);

return (fd);
}


int main(int argc, char *argv[])
{
char buffer[BUF_LEN];
char tty_buf[BUF_LEN];

struct sockaddr_in server_addr, client_addr;
char temp[20];
int server_fd, client_fd;
int len, msg_size;
int tty_fd, n;

if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("Server : Can't open stream socket\n");
exit(0);
}
memset(&server_addr, 0x00, sizeof(server_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(9999);

if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) <0)
{
printf("Server : Can't bind local address.\n");
exit(0);
}

if(listen(server_fd, 5) < 0)
{
printf("Server : Can't listening connect.\n");
exit(0);
}

memset(buffer, 0x00, sizeof(buffer));
printf("Server : wating connection request.\n");
len = sizeof(client_addr);
while(1)
{
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &len);
if(client_fd < 0)
{
printf("Server: accept failed.\n");
exit(0);
}
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, temp, sizeof(temp));
printf("Server : %s client connected.\n", temp);

msg_size = read(client_fd, buffer, 1024);
printf("Server : client request command : %s.\n", buffer);

tty_fd = open_port();
n = read (tty_fd, tty_buf, sizeof(tty_buf));
printf("Server : temperature = %s\n", strstr(tty_buf, "\n")+1);
close(tty_fd);

write(client_fd, tty_buf, msg_size);
close(client_fd);
printf("Server : %s client closed.\n", temp);
}

close(server_fd);
return 0;
}
  위 코드는 ubuntu 에서 server socket 을 열고 대기 하다가 command 가 들어오면 센서를 열어 값을 읽어 온뒤 socket client 에게 회신합니다.

4. PC 에서 아두이노를 통해 센서 값을 읽어 오는 방법
  위에 적은 코드에서 밑줄 친 부분이 시리얼로 아두이노의 값을 읽어 오는 방법입니다.

  휴.. 다시 정리하면
  구글 글래스 한테 '온도'라고 하면
오케이 글라스로 '온도' 라고 명령해 줌
  >> 앱이 실행 되고 >> 그걸 터치하면 >> ubuntu pc 에 socket 을 통해 온도를 물어보고

앱이 뜨면 터치
  >> ubuntu pc 는 serial 통신으로 아두이노의 값을 읽어와서 구글래스에게 알려주고

최종 결과물
  >> 화면에 표시 됩니다.

오늘은 여기까지 정리 하겠습니다.

최종에는 우분투와 통신이 아니라 거미하고 통신을 해야 하기 때문에, 거미 부품이 도착하면 다음 단계를 진행 해 보도록 하겠습니다.

ㅠㅠ. 아들과 할 부분은 graphical programming 인데 그걸 하기 위해 준비해야 하는 것들이 정말 만만치 않네요.

댓글 없음: