39
2016 12 2 : ( )

ael.chungbuk.ac.krael.chungbuk.ac.kr/AEL-HDBK/automatic-door-lock/송지호-졸업논문.pdf · - 졸업작품전시회 출품 및 결과 발표 송지호 - 쿠도이노를 이용한

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

졸업작품 연구결과보고서

안드로이드와 블루투스를 이용한 스마트 도어락

2016 년 12월 2 일

김 동 식

송 지 호

지도교수: 안 병 철 (사인)

충북대학교 전자정보대학 정보통신공학부장 귀하

목 차

I. 연구 배경 및 필요성 ····································································································· 1 1.1 연구 배경 ·················································································································· 1 1.2 연구의 필요성 ········································································································ 1 1.3 연구 목표 ················································································································ 1

II. 연구 내용 ························································································································ 3 2.1 관련 이론 ·················································································································· 3 2.2 전체 시스템 블록 다이어그램 ············································································ 4 2.3 동작 원리 ················································································································ 5

III. 연구 환경 ····················································································································· 6 3.1 연구진 ······················································································································ 6 3.2 연구 추진 일정 ······································································································ 7 3.3 연구 장비 및 실험재료 ························································································ 7 3.4 소프트웨어 및 툴 ·································································································· 7

IV. 연구 결과 및 분석 ······································································································ 11 4.1 결과물 외형 ············································································································ 11 4.2 작동 과정 ················································································································ 12 4.3 성능 분석 ················································································································ 13 4.4 문제점 및 개선방안 ······························································································ 15

V. 참고문헌 ······················································································································· 16

부록 A. 쿠도이노 소스코드 ····························································································· 17부록 B. 안드로이드 어플리케이션 소스코드 ······························································· 18

- 1 -

I. 연구 배경 및 필요성

1.1 연구 배경

최근 스마트 폰의 보급이 늘어남에 따라, 생활의 편리성을 증가시켜주는 스마트폰 주

변 기기의 출시가 많아지고 어플리케이션이 많이 등장하고 있다. 이에 따라서 우린 더욱

편하게 문을 열수 있는 방법은 없을까 생각을 하다 도어락을 편하게 제어할 수 있는 방

법으로 스마트 도어락을 주제로 선택하게 되었다. 초기에는 NFC(Near Field Communication)방법을 이용하여 스마트 도어락을 만들어 보려 하였지만 NFC 이용한 도어락은 기존에 대중화된 도어락 방식이었다. 또한 NFC방식을이용하면 원거리에서 제어할 수 없다. 그에 따라 블루투스 방법을 이용하여 도어락을 제

어할 수 있는 방법을 생각하게 되었다.

1.2 연구의 필요성

앞서 연구 배경에서 말했듯이 요즘 생활의 편리성을 증가시켜주는 어플리케이션등이

많이 등장하였다. 그에 따라 우리는 도어락을 더욱 편리하게 이용할 수 있는 방법을 생

각해보았다. 기존의 도어락은 무거운 짐 등 양손을 자유롭게 사용할 수 없을 때 짐을 잠

시 내려놓고 도어락을 조작하여 비밀번호를 누르고 문을 열어야 한다. 그리고 요즘 더욱

편리한 생활을 원하는 사람들이 늘어남에 따라 도어락의 조작 없이 핸드폰과 도어락과의

블루투스 페어링만으로 잠금장치가 해제가 된다면 기존의 도어락 조작 방식보다 시간이

단축된다. 1.3 연구 목표

이번 연구 목표는 기존의 도어락기능은 그대로 유지하면서 스마트폰 어플리케이션을

통해 도어락을 조작할 수 있도록 하는 것이 목표이다. 쿠도이노에 아두이노 스케쳐를 이

용해 일정 값이 입력되면 모터를 제어할 수 있도록 코딩해준다. 여기서 쿠도이노란 아주 원초덕인 단 1개의 부품으로 만들어진 아두이노 이다.

쿠도이도는 그림1과 같이 단일 칩으로 구성된 아두이노로서 기존의 아두이노와 기능은

똑같지만 부피는 더욱 작다. 도어락의 내부에 비어있는 공간이 많이 않기 때문에 추후

장치가 다 완성된다면 쿠도이노를 도어락 내부에 매립할 수 있을 것으로 예상된다.

- 2 -

그림 1 예상 구상도

또한 아두이노 스케치와도 100%호환이 되기 때문에 새로운 툴을 다시 공부하여 사용하

지 않아도 된다. 전력사용에서도 기존 아두이노와 대비해 강력한 절전 성능을 가지고 있

다. 아두이노 우노 대비 최대 300배 이상 오래 사용할 수 있다. 기존 도어락도 적지 않은전력소모를 하는데 쿠도이노를 사용함으로써 전력에 대한 부담을 줄일 수 있다.

그림 2 쿠도이노 사진

쿠도이노를 이용한 모터제어를 통해 도어락의 개폐장치를 조작할 수 있게 된 다음

도어락의 기능은 그대로 유지하며 제작한 장치가 작동하는지 실행해본다. 도어락과쿠도이노를 연결 해준다. 앱스토어에 있는 블루투스통신 어플리케이션을 다운받아쿠도이노에 입력된 코딩이 잘 작동하는지 확인해준다. 기존 도어락의 비밀번호키가 잘

작동되는지 확인해준다. 기존의 도어락기능과 같이 블루투스기능을 사용한 이유로는집주인이 아닌 다른 사람들의 핸드폰을 어플리케이션에 따로 등록하지 않고 비밀번호를

통해 출입을 할 수 있도록 하기 위해 사용하였다.

- 3 -

다음은 이클립스를 이용해 어플리케이션을 제작한다. 어플리케이션에는 도어락의잠금장치를 조작할 수 있는 버튼과 일정거리만큼 가까워지면 자동으로 도어락을

개폐하여 별도의 조작을 하지 않고 문만 열고 들어갈 수 있게 쿠도이노와 핸드폰간에

블루투스 페어링이 되면 자동으로 잠금장치가 열릴 수 있도록 조정해준다. 또한 보안성문제를 위해 MAC(Media Access Control Address)주소를 이용하여 어플리케이션을

제작한다. MAC주소란 각 NIC(Network Interface Card)마다 할당된 48비트의고유번호이다. 이처럼 MAC주소를 이용하면 아무 핸드폰이나 다 도어락에 연결하지

못하고 쿠도이노와 어플리케이션에 정해진 MAC주소에만 페어링이 될 수 있도록

만들어주어서 보안성을 강화한다.

II. 연구 내용

2.1 관련 이론

관련 이론으로는 제목에도 있듯이 블루투스 기술이 가장 중요하다. 블루투스기술은 근거리

내에서 하나의 무선 연결을 통해서 장치간에 필요한 여러 케이블 연결을 대신하게 해준다. 예를 들어 블루투스 무선 기술이 휴대폰과 랩탑 컴퓨터 안에 구현되면 케이블 없이도 연결되어

사용할 수 있는 것이다. 사실상 모든 디지털 장비들이 블루투스 시스템의 일부가 될 수 잇다. 장치들을 케이블로부

터 자유롭게 만들어 주는 것뿐 아니라, 블루투스 무선 기술은 기존의 데이터망과 주변장치들

간의 인터페이스, 그리고 고정된 네트워크 하부구조로부터 멀리 떨어진 장치들 간에 특별한

그룹을 형성시켜주는 보편적인 다리 역할을 제공할 것이다. 블루투스는 잡음이 많은 라디오

주파수 환경에서 작동되도록 고안되엇기 때문에, 빠른 인식과 주파수 호핑 방식을 사용하여

연결을 튼튼하게 한다. 또한 핸드폰과 쿠도이노를 연결해주기위해 안드로이드 이론도 중요하다. 세계 각국의 이동

통신 관련 회사 연합체인 '오픈 핸드셋 얼라이언스(OHA Open Handset Alliance)'가 2007년 11월에 공개하였다. 실질적으로는 세계적 검색엔진 업체인 구글(Google)사가 작은 회사인 안드로이드사를 인수하여 개발하였으며, 따라서 '구글 안드로이드'라고도 한다. 안드로이드는 리눅스(Linux) 2.6 커널을 기반으로 강력한 운영체제(OS;operating system)와포괄적 라이브러리 세트, 풍부한 멀티미디어 사용자 인터페이스, 폰 애플리케이션 등을 제공

- 4 -

한다. 안드로이드의 장점은 완전 개방형 플랫폼이라는 점이다. 종전에는 휴대폰 제조업체와

서비스업체마다 운영체제가 달라 개별적으로 응용프로그램을 만들어야 하였다. 이에 비하여 안드로이드는 기반 기술인 '소스 코드'를 모두 공개함으로써 누구라도 이를

이용하여 소프트웨어와 기기를 만들어 판매할 수 있도록 하였다. 응용프로그램은 다른말로 애플리케이션이라고도 하는데 개인이나 조직이 유용한 업무를

수행할 수 있도록 도와주는 프로그램 또는 컴퓨터 체계와 기계장치 혹은 컴퓨터망을 관리하

기 위해 사용하는 프로그램을 의미한다. 응용프로그램은 그 프로그램이 설계된 작업의 특성에

따라 문자, 그림 또는 영상 등과 같은 자료를 처리한다. 여기서 우리가 만들어야할 애플리케

이션은 도어락을 제어할 수 있는 애플리케이션이지만 간단하게 말하면 도어락의 모터를 제어

하며 블루투스 통신을 할 수 있는 애플리케이션이다. 다음은 아두이노에 관련된 이론이다. 아두이노란 물리적인 세계를 감지하고 제어할 수 있

는 인터랙티브 객체들과 디지털 장치를 만들기 위한 도구로, 간단한 마이크로컨트롤러

(Microcontroller) 보드를 기반으로 한 오픈 소스 컴퓨팅 플랫폼과 소프트웨어 개발 환경을 말

한다. 다양한 스위치나 센서로부터 입력 값을 받아들여 LED나 모터와 같은 전자 장치들로 출

력을 제어함으로써 환경과 상호작용이 가능한 물건을 만들어 낼 수 있다. 또한 아두이노는 회

로가 오픈소스로 공개되어 있으므로 누구나 직접 보드를 만들고 수정할 수 있다. 마이크로컨트롤러란 마이크로프로세서와 입출력 모듈을 하나의 칩으로 만들어 특정 기능

을 수행하는 작은 컴퓨터를 말한다. 아두이노는 이러한 마이크로컨트롤러 보드와 관련된 개발

도구 및 환경을 모두 포함한다. 아두이노는 처음 아트멜(Atmel AVR) 마이크로컨트롤러를 기반으로 만들어졌지만, 용도에 따라 다양한 보드가 있다. 우리는 이번 작품에서 쿠도이노를 사용

하였다. 아두이노는 마이크로컨트롤러를 기반으로 하는 작업을 단순화하였으며 다양한 강점을

가진다. 아두이노 통합 개발 환경은 소스 코드를 작성하고 편집할 수 있도록 하며, 코드를 아

두이노 하드웨어가 이해할 수 있는 명령어로 컴파일하여 보드에 이를 업로드 하는 기능을 제

공한다. 소스코드는 C++++ 언어를 기반으로 하기 때문에 아두이노에서는 C++ 언어의 표준라이브러리 함수가 사용 가능하다. 아두이노 하드웨어는 실세계와 연동되어 동작하는 센서, 가속도계, LED, 스피커, 디스플레이 등의 여러 구성품들이 쉽게 탈부착 가능하도록 핀들로 구성되

어 있으며, 소프트웨어 개발 환경을 통해 작성되고 업로드 된 코드(명령어)가 실행된다. 우리가 사용할 쿠도이노는 아두이노와 조금 다르게 직접 기판을 통해 회로를 구성해야한다.

- 5 -

그림 3 쿠도이노 핀 번호

2.2 전체 시스템 블록 다이어그램

그림 4 어플리케이션의 동작 순서

- 6 -

그림 5 어플리케이션 기본 레이아웃

2.3 동작 원리

2.2 블록도에 간략한 순서도를 제시하였는데 우선 어플리케이션을 실행 시키면 미리 등록

한 MAC주소를 이용해 등록되어 있는 기기간 블루투스 페어링을 하게 된다. 페어링이 된후 자동으로 문이 열릴수 있도록 쿠도이노로 데이터값을 보내준다. 기기간 페어링이 된 후에는

Lock/Unlock 버튼이나 명령어를 입력하여 핸드폰으로 원거리에서 도어락을 조작할수 있다.

명령어로 도어락을 조작하는 법은 다음과 같다. if (BTSerial.available())를 이용해 블루투스로명령어가 수신된 경우 실행해주고 Serial.println(BTSerial.read());를 통해 핸드폰에서 보내준명령어를 쿠도이노에서 읽어준다음 명령어에 해당하는 동작을 실행시켜준다. 에를들면‘3’에서 잠금 ‘4’에서 해제 방식으로 진행된다. 이 소스를 사용하여 Lock/Unlock 버튼을눌러주던가 직접 명령어를 입력하면 핸드폰에서 쿠도이노로 명령어를 보내준다.

- 7 -

III. 연구 환경

3.1 연구진

팀원 업무 분담 업무

김동식

- 어플리케이션 프로그램 제작

- 아두이노 스케쳐를 사용하여 쿠도이소 소스 코딩

- 프로그래밍 화면 디자인

- 졸업작품전시회 출품 및 결과 발표

송지호

- 쿠도이노를 이용한 하드웨어적 부분 제작

- 프로그램 디버깅

- 작품 실행 및 성능 평가

- 졸업작품전시회 출품 및 결과 발표

표 3.1 업무 분담표

3.2 연구 추진 일정

15.

10

15.

11

15.

12

16.

1

16.

2

16.

3

16.

4

16.

5

16.

7

16.

8

16.

9

16.

10

주제선정

도어락 회로분석

블루투스 모듈, 쿠도이노set

자료조사

회로구성

블루투스 통신 및 쿠도이노

코딩

도어락 모터제어

오픈소스활용 도어락 블루

투스 제어

어플리케이션 제작

기능 수정

표 2

- 8 -

3.3 연구 기자재 및 실험 재료

3.3.1 연구 기자재

l 개인 노트북(NT910S3Q-MD1S, )l 스마트 폰(갤럭시 SⅡ)

3.3.2 실험 재료

l 쿠도이노

l 도어락

l 모터드라이버

l 블루투스 모듈

3.4 소프트웨어 및 사용 툴

l Microsoft Windows 7 pro 32비트l Eclipsl 안드로이드 스튜디오

l Java SE 2.1al 아두이노 스케쳐

- 9 -

IV. 연구 결과

4.1 결과물 외형

그림 6 작품 전체사진

그림 7 도어락 본체 사진

- 10 -

그림 8 쿠도이노 사진

4.2 작동 과정

- 11 -

그림 9 어플리케이션

4.3 성능 분석- 핸드폰과 블루투스 모듈과의 페어링은 매우 잘 이루어지고 있음.- 핸드폰에서 블루투스 모듈로의 데이터 전송과정에서 실패하는 경우가 있지만 발생 확

률이 낮음.- 핸드폰에서 블루투스 모듈로의 데이터 전송과정에서 전송 지연이 일어나서 데이터가

연속적으로 일어나는 경우가 있지만 발생 확률이 낮음

- 도어락 내부에 설치해놓은 점퍼선이 단단하게 고정되지 않아 충격에 약함.- 블루투스 연결 거리가 매우 길어 원거리에서도 도어락 조작이 가능

- 12 -

4.4 문제점 및 개선방안- 도어락의 건전지에서 제작한 회로의 전원을 끌어쓰다 보니 전력이 약해 모터가 끝까지돌아가지 않는 경우가 생겨 문을 열었을 때 도어락 내부에서 문열림으로 인식하지 못하

어 알람이 울리는 경우가 있음. 해결 방안으로는 쿠도이노의 전원을 따로 설치하여 사용하여야 할 것 같음

- 핸드폰에서 블루투스 모듈로의 데이터 전송이 닫힘-닫힘, 열림-열림 이런식으로 같은데이터가 여러번 전달되면 문을 열었을 때 도어락 내부에서 알람이 울리는 경우가 있음

이 부분은 시연과정에서의 오류로 실생활에서는 중복데이터 전송이 이루어지는 경우가

없을것으로 예상됨

- 블루투스연결거리가 매우 길다보니 핸드폰과 도어락의 페어링시 문이 열렸다가 문앞에도착하기 전에 문이 저절로 잠기는 경우가 있음. 이 부분은 추후 RSSI측정을 이용하여 개선할수 있을것으로 예상됨

- 현재 건전지의 전력을 확인할 수 있는 방법이 없어 건전지 부족시 동작이 안됨. 개선방안으로는 제작한 회로에 건전지의 상태를 알 수 있는 LED등을 설치하여 확인해볼수

있음

- 13 -

V. 참고문헌

[1] 구미서관, “지형 공간정보체계 용어사전”, 네이버 지식백과, 2016년 1월 3일

[2]두산백과, “안드로이드”,네이버 지식백과,

[3]두산백과, “아두이노”,네이버 지식백과,

- 14 -

부록 A. 쿠도이노 소스코드 (블루투스 연결, 데이터 값 전송)#include<SoftwareSerial.h>

SoftwareSerial BTSerial(13, 12);int input_1 = 11;int input_2 = 10; //김동식 & 송지호//void setup(){ Serial.begin(9600); BTSerial.begin(9600);

pinMode(11,OUTPUT); pinMode(10,OUTPUT); }void loop(){ if (BTSerial.available()) { //블루투스로 명령어가 수신된 경우

Serial.println(BTSerial.read()); //명령어 읽기

int abc; abc = BTSerial.read(); //data에 명령어 저장

if(abc=='4') { digitalWrite(11, HIGH); digitalWrite(10, LOW); delay(800); digitalWrite(11, HIGH); digitalWrite(10, HIGH); }

- 15 -

if(abc =='0') { digitalWrite(11, LOW); digitalWrite(10, HIGH); delay(800); digitalWrite(11, HIGH); digitalWrite(10, HIGH); } if(abc =='5') { digitalWrite(11, LOW); digitalWrite(10, LOW): } if(abc =='7') { digitalWrite(11, HIGH); digitalWrite(10, HIGH); } } }

- 16 -

부록 B. 안드로이드 어플리케이션 소스코드<MainActivity.java>package kr.re.Dev.ArduinoEcho;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.lang.reflect.Field;import java.nio.ByteBuffer;import java.util.LinkedList;import java.util.Set;import kr.re.Dev.Bluetooth.BluetoothSerialClient;import kr.re.Dev.Bluetooth.BluetoothSerialClient.BluetoothStreamingHandler;import kr.re.Dev.Bluetooth.BluetoothSerialClient.OnBluetoothEnabledListener;import kr.re.Dev.Bluetooth.BluetoothSerialClient.OnScanListener;import kr.re.Dev.BluetoothEcho.R;import android.app.Activity;import android.app.AlertDialog;import android.app.ProgressDialog;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothSocket;import android.content.DialogInterface;import android.content.Intent;import android.content.DialogInterface.OnCancelListener;import android.graphics.Color;import android.os.Bundle;import android.text.Html;import android.text.method.ScrollingMovementMethod;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewConfiguration;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import arduino.BT.simpleBT.BluetoothService;import arduino.BT.simpleBT.DeviceListActivity;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.EditText;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {

private static final boolean D = false;

- 17 -

private static final String TAG = null;protected static final String mDevices = null;private LinkedList<BluetoothDevice> mBluetoothDevices = new LinkedList<BluetoothDevice>();private ArrayAdapter<String> mDeviceArrayAdapter;

private EditText mEditTextInput;private TextView mTextView;private Button mButtonSend;private Button mRock;private Button mUnrock;private boolean mconnectToSelectdDevice;private ProgressDialog mLoadingDialog;private AlertDialog mDeviceListDialog;private Menu mMenu;private BluetoothSerialClient mClient;private boolean write;private Object mRemoteDevice;private Object mBluetoothAdapter;

@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mClient = BluetoothSerialClient.getInstance(); if(mClient == null) {

Toast.makeText(getApplicationContext(), "Cannot use the Bluetooth device.", Toast.LENGTH_SHORT).show();

finish(); } overflowMenuInActionBar(); initProgressDialog(); initDeviceListDialog(); initWidget();

}private void overflowMenuInActionBar(){ try { ViewConfiguration config = ViewConfiguration.get(this); Field menuKeyField =

ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); if(menuKeyField != null) { menuKeyField.setAccessible(true); menuKeyField.setBoolean(config, false); } } catch (Exception ex) {

// 무시한다. 3.x 이 예외가 발생한다. // 또, 타블릿 전용으로 만들어진 3.x 버전의 디바이스는 보통 하드웨어 버튼이 존재하지 않는다.

- 18 -

}}

@Overrideprotected void onPause() {

mClient.cancelScan(getApplicationContext());super.onPause();

}@Override

protected void onResume() {super.onResume();

enableBluetooth();

}private void initProgressDialog() { mLoadingDialog = new ProgressDialog(this); mLoadingDialog.setCancelable(false);

}private void initWidget() {mTextView = (TextView) findViewById(R.id.textViewTerminal);mTextView.setMovementMethod(new ScrollingMovementMethod());mEditTextInput = (EditText) findViewById(R.id.editTextInput);mButtonSend = (Button) findViewById(R.id.buttonSend);mRock=(Button)findViewById(R.id.Rock);mUnrock=(Button)findViewById(R.id.Unrock);mButtonSend.setOnClickListener(new OnClickListener(){

@Overridepublic void onClick(View v) {

sendStringData(mEditTextInput.getText().toString(), 0);mEditTextInput.setText("");

}});mRock.setOnClickListener(new OnClickListener(){

@Overridepublic void onClick(View v) {

sendStringData(mEditTextInput.getText().toString(), 0); mEditTextInput.setText("잠금(00)\n**************************"); sendStringData(mEditTextInput.getText().toString(), 0);

mEditTextInput.setText("");}});

mUnrock.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {

sendStringData(mEditTextInput.getText().toString(), 0);mEditTextInput.setText("열림(44)\n**************************");sendStringData(mEditTextInput.getText().toString(), 0);

mEditTextInput.setText("");

- 19 -

}});}private void initDeviceListDialog() {mDeviceArrayAdapter =

new ArrayAdapter<String>(getApplicationContext(), R.layout.item_device);ListView listView = new ListView(getApplicationContext());listView.setAdapter(mDeviceArrayAdapter);listView.setOnItemClickListener(new OnItemClickListener() {

@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id)

{String item = (String) parent.getItemAtPosition(position); for(BluetoothDevice device : mBluetoothDevices) {

if(item.contains(device.getAddress())) {connect(device, mClient);mDeviceListDialog.cancel();

}}

}});AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("Select bluetooth device");builder.setView(listView);builder.setPositiveButton("Scan", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) {

scanDevices(); } });mDeviceListDialog = builder.create();mDeviceListDialog.setCanceledOnTouchOutside(false);

}private void addDeviceToArrayAdapter(BluetoothDevice device) {if(mBluetoothDevices.contains(device)) {

mBluetoothDevices.remove(device);mDeviceArrayAdapter.remove(device.getName() + "\n" + device.getAddress());

}mBluetoothDevices.add(device);mDeviceArrayAdapter.add(device.getName() + "\n" + device.getAddress() );mDeviceArrayAdapter.notifyDataSetChanged();

}private void enableBluetooth() {

BluetoothSerialClient btSet = mClient;btSet.enableBluetooth(this, new OnBluetoothEnabledListener() {

@Override

- 20 -

public void onBluetoothEnabled(boolean success) {if(success) {

getPairedDevices();

} else {

finish();}

}

});}

private void addText(String text) { mTextView.append(text);

final int scrollAmount = mTextView.getLayout().getLineTop(mTextView.getLineCount()) - mTextView.getHeight();

if (scrollAmount > 0) mTextView.scrollTo(0, scrollAmount); else mTextView.scrollTo(0, 0);}

private void getPairedDevices() {Set<BluetoothDevice> devices = mClient.getPairedDevices();for(BluetoothDevice device: devices) {

addDeviceToArrayAdapter(device);mconnectToSelectdDevice();

}}

private void mconnectToSelectdDevice() {// TODO Auto-generated method stub

}private void scanDevices() {

BluetoothSerialClient btSet = mClient;btSet.scanDevices(getApplicationContext(), new OnScanListener() {

String message ="";@Overridepublic void onStart() {

Log.d("Test", "Scan Start.");mLoadingDialog.show();message = "Scanning....";mLoadingDialog.setMessage("Scanning....");

- 21 -

mLoadingDialog.setCancelable(true);mLoadingDialog.setCanceledOnTouchOutside(false);mLoadingDialog.setOnCancelListener(new OnCancelListener() {

@Overridepublic void onCancel(DialogInterface dialog) {

BluetoothSerialClient btSet = mClient;btSet.cancelScan(getApplicationContext());

}});

}

@Overridepublic void onFoundDevice(BluetoothDevice bluetoothDevice) {

addDeviceToArrayAdapter(bluetoothDevice); message= "\n" + bluetoothDevice.getName() + "\n" + bluetoothDevice.getAddress();

mLoadingDialog.setMessage(message);

}@Override

public void onFinish() {Log.d("Test", "Scan finish.");message = "";mLoadingDialog.cancel();mLoadingDialog.setCancelable(false);mLoadingDialog.setOnCancelListener(null);mDeviceListDialog.show();

}});

}void connectToSelectdDevice(Object mSocket) { mRemoteDevice = getDeviceFromBondedList(ACCESSIBILITY_SERVICE); java.util.UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); try { // 소켓 생성

mSocket = ((BluetoothDevice) mRemoteDevice).createRfcommSocketToServiceRecord(uuid); // RFCOMM 채널을 통한 연결 ((BluetoothSocket) mSocket).connect(); // 데이터 송수신을 위한 스트림 열기

OutputStream mOutputStream = ((BluetoothSocket) mSocket).getOutputStream(); Object mInputStream = ((BluetoothSocket) mSocket).getInputStream(); // 데이터 수신 준비

beginListenForData();

- 22 -

}catch(Exception e) { // 블루투스 연결 중 오류 발생

finish(); // 어플 종료 }}

private void beginListenForData() {// TODO Auto-generated method stub

}private Object getDeviceFromBondedList(String selectedDeviceName) {

// TODO Auto-generated method stubreturn null;

}private void connect(BluetoothDevice device, BluetoothSerialClient handler) {

mLoadingDialog.setMessage("Connecting....");mLoadingDialog.setCancelable(false);mLoadingDialog.show();BluetoothSerialClient btSet = mClient;btSet.connect(getApplicationContext(), device, mBTHandler);

handler.write("44".getBytes()); /* try {

Thread.sleep(12000);} catch (InterruptedException e) {

// TODO Auto-generated catch blocke.printStackTrace();

} handler.write("00".getBytes());*/

/* 잠금 풀린 후 자동으로 잠금되지 않을 경우 일정시간 후 잠금되도록하는 소스*/ }

private BluetoothStreamingHandler mBTHandler = new BluetoothStreamingHandler() {ByteBuffer mmByteBuffer = ByteBuffer.allocate(1024);

@Overridepublic void onError(Exception e) {

mLoadingDialog.cancel();addText("blue : Connection error - " + e.toString() + "\n");mMenu.getItem(0).setTitle(R.string.action_connect);

}

@Overridepublic void onDisconnected() {

mMenu.getItem(0).setTitle(R.string.action_connect);mLoadingDialog.cancel();addText("blue : Disconnected.\n");

}@Override

- 23 -

public void onData(byte[] buffer, int length) {if(length == 0) return;if(mmByteBuffer.position() + length >= mmByteBuffer.capacity()) {ByteBuffer newBuffer = ByteBuffer.allocate(mmByteBuffer.capacity() * 2);

newBuffer.put(mmByteBuffer.array(), 0, mmByteBuffer.position());mmByteBuffer = newBuffer;

} mmByteBuffer.put(buffer, 0, length);if(buffer[length - 1] == '\0') {

addText(mClient.getConnectedDevice().getName() + " : " + new String(mmByteBuffer.array(), 0, mmByteBuffer.position()) + '\n');

mmByteBuffer.clear();}

}

@Overridepublic void onConnected() {

addText(" 1.최초잠금이 풀렸습니다. ## 김동식 & 송지호 ##\n" 2. 활성화되었습니다. ## 졸업작품 ## \n "

+ "3. 제어가능.\n 4. 5초 뒤 잠금상태로 돌아갑니다.\n # bluetooth name : "

+ " " + mClient.getConnectedDevice().getName() + "\n\n\n");mLoadingDialog.cancel();mMenu.getItem(0).setTitle(R.string.action_disconnect);

}};private BluetoothSerialClient handler; private void sendMessage(CharSequence message) {

if (mBtService.getState() != BluetoothService.STATE_CONNECTED) Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); return; }

public void sendStringData(String data, int length) {data += '\0';byte[] buffer = data.getBytes();if(mBTHandler.write(buffer)) {

addText("blue : " + data + '\n');

}}

//*************************************************************************

protected void onDestroy() {super.onDestroy();

- 24 -

mClient.claer();};@Overridepublic boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main, menu);mMenu = menu;return true;

}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {

boolean connect = mClient.isConnection();if(item.getItemId() == R.id.action_connect) {

if (!connect) {mDeviceListDialog.show();

} else {mBTHandler.close();

}return true;

} else {showCodeDlg();return true;

}}private void showCodeDlg() {

TextView codeView = new TextView(this);codeView.setText(Html.fromHtml(readCode()));codeView.setMovementMethod(new ScrollingMovementMethod());codeView.setBackgroundColor(Color.parseColor("#202020"));new AlertDialog.Builder(this, android.R.style.Theme_Holo_Light_DialogWhenLarge).setView(codeView).setPositiveButton("OK", new AlertDialog.OnClickListener() {

@Overridepublic void onClick(DialogInterface dialog, int which) {

dialog.cancel();}

}).show();}private String readCode() {

try {InputStream is = getAssets().open("HC_06_Echo.txt");int length = is.available();byte[] buffer = new byte[length];is.read(buffer);is.close();String code = new String(buffer);buffer = null;

- 25 -

return code;} catch (IOException e) {

e.printStackTrace();} return "";

} public void Rock(View view){

sendMessage("00");}public void Unrock(View view){

sendMessage("44");}//*************************************************************************

- 26 -

<Bluetoothserialclient.java>package kr.re.Dev.Bluetooth;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.Set;import java.util.UUID;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.atomic.AtomicBoolean;import android.app.Activity;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothSocket;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.os.Handler;import android.os.Looper;public class BluetoothSerialClient {

static final private String SERIAL_UUID = "00001101-0000-1000-8000-00805F9B34FB";static private BluetoothSerialClient sThis = null;private static String address = "20:15:06:10:58:94";

private BluetoothAdapter mBluetoothAdapter;private OnBluetoothEnabledListener mOnBluetoothUpListener;private OnScanListener mOnScanListener;private BluetoothSocket mBluetoothSocket;private UUID mUUID = UUID.fromString(SERIAL_UUID);private AtomicBoolean mIsConnection = new AtomicBoolean(false);private ExecutorService mReadExecutor; private ExecutorService mWriteExecutor;private BluetoothStreamingHandler mBluetoothStreamingHandler;private Handler mMainHandler = new Handler(Looper.getMainLooper());private BluetoothDevice cbnublue;private BluetoothDevice mConnectedDevice= cbnublue;private InputStream mInputStream;private OutputStream mOutputStream;

/** * BluetoothSerialClient 의 싱글 인스턴스를 가져온다. * @return BluetoothSerialClient 의 인스턴스. 만약 블루투스를 사용할 수 없는 기기라면 null.

- 27 -

*/public static BluetoothSerialClient getInstance() {

if(sThis == null) {sThis = new BluetoothSerialClient();

}if(sThis.mBluetoothAdapter == null) {

sThis = null;return null;

}

return sThis;}

private BluetoothSerialClient() {mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();mReadExecutor = Executors.newSingleThreadExecutor();mWriteExecutor = Executors.newSingleThreadExecutor();

}/**

* 연결을 닫고 자원을 해지한다. * 앱 종료시 반드시 호출해 줘야 한다. */public void claer() {

close();mReadExecutor.shutdownNow();mWriteExecutor.shutdownNow();sThis = null;

}/**

* 블루투스를 사용 가능한 상태로 만들어준다. <br/> * 만약 기기 내에서 블루투스 사용이 꺼져있다면, 사용자로 하여 블루투스 사용에 관한 선택을

하게 하는 창을 출력한다. * @param context activity * @param onBluetoothEnabledListener 블루투스 on/off 에 대한 이벤트. */

public void enableBluetooth(Context context, OnBluetoothEnabledListener onBluetoothEnabledListener) {if(!mBluetoothAdapter.isEnabled()) {

mOnBluetoothUpListener = onBluetoothEnabledListener;Intent intent = new Intent(context, BluetoothUpActivity.class);context.startActivity(intent);

} else {onBluetoothEnabledListener.onBluetoothEnabled(true);

}}

/**

- 28 -

* 블루투스가 사용 가능한 상태인지 확인. * @return false 라면 블루투스가 off 된 상태거나 사용할 수 없다. */public boolean isEnabled() {

return mBluetoothAdapter.isEnabled();}

/** * 블루투스 디바이스와 시리얼로 연결한다. * @param context * @param device 블루투스 디바이스. {@link getPairedDevices} 또는 {@link scanDevices} 를 통하여 가져온 블루투스 디바이스 인스턴스. * @param bluetoothStreamingHandler 블루투스 스트리밍 핸들러. * @return 만약 블루투스를 사용할 수 없는 상태라면 false. {@link enableBluetooth} 를 통하여 블루투스를 사용 가능한 상태로 만들어줘야 한다. */public boolean connect(final Context context,final BluetoothDevice device, final BluetoothStreamingHandler bluetoothStreamingHandler) {

if(!isEnabled()) return false;mConnectedDevice = device;mBluetoothStreamingHandler = bluetoothStreamingHandler;if(isConnection()) {

mWriteExecutor.execute(new Runnable() {@Overridepublic void run() {

try {mIsConnection.set(false);mBluetoothSocket.close();Thread.sleep(2000);

} catch (InterruptedException e) {e.printStackTrace();

} catch (IOException e) {e.printStackTrace();

}connect(context, device, bluetoothStreamingHandler);

}});

} else {mIsConnection.set(true);connectClient();

}return true;

}/**

* 과거에 페어링 되었던 블루투스 디바이스 목록을 가져온다. * @return */

- 29 -

public Set<BluetoothDevice> getPairedDevices() {Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();if(pairedDevices.size() > 0){

BluetoothDevice getDeviceFromBondedList;{

BluetoothDevice selectedDevice = null; } }

// 페어링 된 장치가 있는 경우.

return pairedDevices;// TODO Auto-generated method stub

}/** * 주변의 새 블루투스 디바이스를 스캔한다. * @param context * @param OnScanListener 블루투스를 스캔 이벤트. * @return */public boolean scanDevices(Context context, OnScanListener OnScanListener) {

if(!mBluetoothAdapter.isEnabled()) return false;if(mBluetoothAdapter.isDiscovering()) {

mBluetoothAdapter.cancelDiscovery();try {

context.unregisterReceiver(mDiscoveryReceiver);} catch(IllegalArgumentException e) {

e.printStackTrace();}

}mOnScanListener = OnScanListener;IntentFilter filterFound = new IntentFilter(BluetoothDevice.ACTION_FOUND);filterFound.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);filterFound.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);context.registerReceiver(mDiscoveryReceiver, filterFound);mBluetoothAdapter.startDiscovery();return true;

}/**

* 스캔을 취소한다. * @param context */public void cancelScan(Context context) {

if(!mBluetoothAdapter.isEnabled() || !mBluetoothAdapter.isDiscovering()) return;

- 30 -

mBluetoothAdapter.cancelDiscovery();try {

context.unregisterReceiver(mDiscoveryReceiver);} catch(IllegalArgumentException e) {

e.printStackTrace();}if(mOnScanListener != null) mOnScanListener.onFinish();

}/**

* 블루투스 디바이스와 연결 되어있는지를 가져온다. * @return true/false */public boolean isConnection() {

return mIsConnection.get();}

/** * 연결된 블루투스 디바이스를 가져온다. * @return 만약 연결된 블루투스 디바이스가 없다면 null. */public BluetoothDevice getConnectedDevice() {

return mConnectedDevice;}

private void connectClient() { try {

mBluetoothSocket = mConnectedDevice.createRfcommSocketToServiceRecord(mUUID);} catch (IOException e) {

close();e.printStackTrace();mBluetoothStreamingHandler.onError(e);return;

}mWriteExecutor.execute(new Runnable() {

@Overridepublic void run() {

try {mBluetoothAdapter.cancelDiscovery();mBluetoothSocket.connect();manageConnectedSocket(mBluetoothSocket);callConnectedHandlerEvent();

mReadExecutor.execute(mReadRunnable); } catch (final IOException e) { close(); e.printStackTrace(); mMainHandler.post(new Runnable() {

@Overridepublic void run() {

- 31 -

mBluetoothStreamingHandler.onError(e);}

}); mIsConnection.set(false); try { mBluetoothSocket.close(); } catch (Exception ec) { ec.printStackTrace(); } }

}});

}private void manageConnectedSocket(BluetoothSocket socket) throws IOException {mInputStream = socket.getInputStream();

mOutputStream = socket.getOutputStream();}

private void callConnectedHandlerEvent() {mMainHandler.post(new Runnable() {

@Overridepublic void run() {

mBluetoothStreamingHandler.onConnected();}

});}

public boolean write(final byte[] buffer) {if(!mIsConnection.get()) return false; mWriteExecutor.execute(new Runnable() {

@Overridepublic void run() {

try {mOutputStream.write(buffer);

} catch (Exception e) {close();e.printStackTrace();mBluetoothStreamingHandler.onError(e);

}}

});return true;

}private boolean close() {

mConnectedDevice = null;if(mIsConnection.get()) {

mIsConnection.set(false);try {

- 32 -

mBluetoothSocket.close();} catch (IOException e) {

e.printStackTrace();}mMainHandler.post(mCloseRunable);return true;

}return false;

}private Runnable mCloseRunable = new Runnable() {@Overridepublic void run() {

if(mBluetoothStreamingHandler != null) {mBluetoothStreamingHandler.onDisconnected();

}}

};private Runnable mReadRunnable = new Runnable() {@Overridepublic void run() {

try {final byte[] buffer = new byte[256];final int readBytes = mInputStream.read(buffer);mMainHandler.post(new Runnable() {

@Overridepublic void run() {

if(mBluetoothStreamingHandler != null) {mBluetoothStreamingHandler.onData(buffer ,readBytes);

}}

});mReadExecutor.execute(mReadRunnable);

} catch (Exception e) {close();e.printStackTrace();

} }

};

private BroadcastReceiver mDiscoveryReceiver = new BroadcastReceiver() { @Override

public void onReceive(Context context, Intent intent) {String action = intent.getAction();

if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(mOnScanListener != null) mOnScanListener.onFoundDevice(device);

- 33 -

} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { if(mOnScanListener != null) mOnScanListener.onFinish(); try {

context.unregisterReceiver(mDiscoveryReceiver);} catch(IllegalArgumentException e) {

e.printStackTrace();}

} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) { if(mOnScanListener != null) mOnScanListener.onStart(); }

} };

public static class BluetoothUpActivity extends Activity {private static int REQUEST_ENABLE_BT = 2; @Overrideprotected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);getWindow().getDecorView().postDelayed(new Runnable() {

@Overridepublic void run() {

upbluetoothDevice();}

}, 100);}private void upbluetoothDevice() {

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); if (!btAdapter.isEnabled()) {

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) ; }}

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);if(requestCode == REQUEST_ENABLE_BT) {

OnBluetoothEnabledListener onBluetoothEnabledListener = getInstance().mOnBluetoothUpListener;

if (resultCode == Activity.RESULT_OK) { if(onBluetoothEnabledListener != null) onBluetoothEnabledListener.onBluetoothEnabled(true); finish(); } else { if(onBluetoothEnabledListener != null) onBluetoothEnabledListener.onBluetoothEnabled(false);

- 34 -

finish(); }

}}

}// End BluetoothUpActivity

public static interface OnBluetoothEnabledListener {public void onBluetoothEnabled(boolean success);

}public static interface OnScanListener {public void onStart();public void onFoundDevice(BluetoothDevice bluetoothDevice);public void onFinish();

}public abstract static class BluetoothStreamingHandler {public abstract void onError(Exception e);public abstract void onConnected();public abstract void onDisconnected();public abstract void onData(byte[] buffer, int length);public final boolean close() {

BluetoothSerialClient btSet = getInstance();if(btSet != null)

return btSet.close();return false;

}public final boolean write(byte[] buffer) {

BluetoothSerialClient btSet = getInstance();if(btSet != null)

return btSet.write(buffer);return false;

}public void onData(int i, int length) {

// TODO Auto-generated method stub}

}public void Rock(String string) {

// TODO Auto-generated method stub}

public void Unrock(String string) {// TODO Auto-generated method stub

}public static String getAddress() {

return address;}public static void setAddress(String address) {

BluetoothSerialClient.address = address;

- 35 -

}}

<androidmanifest.xml><?xml version="1.0" encoding="utf-8"<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="kr.re.Dev.BluetoothEcho" android:versionCode="1" android:versionName="1.0" > <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:icon="@drawable/dsd" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="kr.re.Dev.ArduinoEcho.MainActivity" android:label="@string/action_back" android:configChanges="screenSize|orientation|keyboard" android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="kr.re.Dev.Bluetooth.BluetoothSerialClient$BluetoothUpActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:configChanges="orientation|screenSize|keyboard"></activity> </application></manifest>

- 36 -

<activity_main.xml><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="kr.re.Dev.ArduinoEcho.MainActivity" tools:ignore="MergeRootFrame" >

<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" >

<TextView android:id="@+id/textViewTerminal" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:background="#F0F8FF" android:scrollbars="vertical" android:textSize="12sp" />

<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <EditText android:id="@+id/editTextInput" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="2" android:ems="4" android:inputType="textPersonName" >

<requestFocus /> </EditText>

<Button android:id="@+id/buttonSend" style="?android:attr/buttonStyleSmall" android:layout_width="80dp" android:layout_height="wrap_content"

- 37 -

android:text="Send" /> <Button android:id="@+id/Rock" android:onClick="Rock" android:layout_width="0px" android:layout_weight="2" android:layout_height="wrap_content" android:text="Rock" android:textSize="12sp" /> <Button android:id="@+id/Unrock" android:onClick="Unrock" android:layout_width="0px" android:layout_weight="2" android:layout_height="wrap_content" android:text="Unrock" android:textSize="12sp" />

</LinearLayout>

</LinearLayout>

</FrameLayout>

<strings.xml><?xml version="1.0" encoding="utf-8"

<resources>

<string name="app_name">5pen </string> <string name="action_connect">connect</string> <string name="action_arduinocode">Arduino code</string> <string name="action_back">Rock 5pen(feat.동식,지호) </string> <string name="action_disconnect">Disconnect</string> <string name="action_settings">설정</string>

</resources>