상세 컨텐츠

본문 제목

[안드로이드] 블루투스(Bluetooth)통신 - 연결하기

Programming/안드로이드

by 영상털이범 2014. 4. 5. 06:09

본문

지금까지 블루투스를 활성화 하고 기기 검색을 하는 단계까지 진행하였다.


이전 글 : 블루투스 활성화 글 보기블루투스 기기 검색하기 글 보기


저번 프로젝트에 이어 블루투스 연결하는 단계를 포스팅 하겠다.


검색한 기기에 연결하기 위해서는 ConnectThread와 ConnectedThread에 대해서 알고 있어야 한다.

BluetoothService.java 파일에 ConnectThread와 ConnectedThread 클래스를 내부 클래스로 삽입해준다.


1. ConnectThread


BluetoothService.java

public class BluetoothService {
	... // 이전 부분 생략

	// RFCOMM Protocol
	private static final UUID MY_UUID = 
		UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	
	private BluetoothAdapter btAdapter;
	
	private ConnectThread mConnectThread;
	private ConnectedThread mConnectedThread;

	... // 이전 부분 생략
		
	private class ConnectThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final BluetoothDevice mmDevice;

		public ConnectThread(BluetoothDevice device) {
			mmDevice = device;
			BluetoothSocket tmp = null;
			
			// 디바이스 정보를 얻어서 BluetoothSocket 생성
			try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                Log.e(TAG, "create() failed", e);
            }
			mmSocket = tmp;
		}

		public void run() {
			Log.i(TAG, "BEGIN mConnectThread");
			setName("ConnectThread");
			
			// 연결을 시도하기 전에는 항상 기기 검색을 중지한다.
			// 기기 검색이 계속되면 연결속도가 느려지기 때문이다.
			btAdapter.cancelDiscovery();

			// BluetoothSocket 연결 시도
			try {
				// BluetoothSocket 연결 시도에 대한 return 값은 succes 또는 exception이다.
				mmSocket.connect();
				Log.d(TAG, "Connect Success");
				
			} catch (IOException e) {
				connectionFailed();		// 연결 실패시 불러오는 메소드
				Log.d(TAG, "Connect Fail");
				
				// socket을 닫는다.
				try {
					mmSocket.close();
				} catch (IOException e2) {
					Log.e(TAG,
							"unable to close() socket during connection failure",
							e2);
				}
				// 연결중? 혹은 연결 대기상태인 메소드를 호출한다.
				BluetoothService.this.start();
				return;
			}

			// ConnectThread 클래스를 reset한다.
			synchronized (BluetoothService.this) {
				mConnectThread = null;
			}

			// ConnectThread를 시작한다.
			connected(mmSocket, mmDevice);
		}

		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) {
				Log.e(TAG, "close() of connect socket failed", e);
			}
		}
	}
}


여기서 UUID라는 변수가 있는데 이것은 Bluetooth 통신 프로토콜이다. 블루투스 통신을 할 때 그에 맞는 프로토콜로 통신을 해 주어야 한다. 간단한 예로 블루투스 기능이 지원되는 이어폰과의 통신을 원한다면 그에 맞는 프로토콜을 사용하여야 하고, 스마트폰 끼리의 블루투스 통신은 또 그에 맞는 프로토콜을 사용해야 한다. 

지금 위에서 사용한 프로토콜은 아마 스마트폰끼리의 블루투스 통신을 하는데 사용하는 프로토콜일 것이다.


블루투스 프로토콜 종류 보기 - [안드로이드] 블루투스 프로토콜 UUID 리스트


2. ConnectedThread


위에서(ConnectThread) 다음단계가 ConnectedThread이다. 위 단계에서는 연결을 하는 중간단계 까지라고 생각하고, 이번 단계는 연결을 완료하는 단계라고 생각하면 될 것 같다. 위와 동일하게 BluetoothService 클래스에 내부 클래스로 정의한다.


BluetoothService.java

public class BluetoothService {
	... // 이 전 부분 생략
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;

	private ConnectThread mConnectThread; // 변수명 다시
	private ConnectedThread mConnectedThread; // 변수명 다시
	
	... // 이 전 부분 생략
	
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // BluetoothSocket의 inputstream 과 outputstream을 얻는다.
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // InputStream으로부터 값을 받는 읽는 부분(값을 받는다)
                    bytes = mmInStream.read(buffer);

                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         * @param buffer  The bytes to write
         */
        public void write(byte[] buffer) {
            try {
            	// 값을 쓰는 부분(값을 보낸다)
                mmOutStream.write(buffer);

            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
}



3. 마무리 작업


지금까지 잘 따라했다면 몇 가지 빨간줄이 있을 것이다. 이 빨간줄을 지워보자

기본 베이스는 처음에 말했듯이 안드로이드 기본 예제인 BluetoothChat이다.

BluetoothChat에서 다음과 같은 메소드를 모두 복사해서 BluetoothService.java에 붙여넣어 빨간줄을 없애보자


BluetoothService.java

public class BluetoothService {
	... // 이 전과 동일
	private int mState;

	// 상태를 나타내는 상태 변수
	private static final int STATE_NONE = 0; // we're doing nothing
	private static final int STATE_LISTEN = 1; // now listening for incoming connections
	private static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
	private static final int STATE_CONNECTED = 3; // now connected to a remote device

	... // 이 전과 동일

	// Bluetooth 상태 set
	private synchronized void setState(int state) {
		Log.d(TAG, "setState() " + mState + " -> " + state);
		mState = state;
	}

	// Bluetooth 상태 get
	public synchronized int getState() {
		return mState;
	}

	public synchronized void start() {
		Log.d(TAG, "start");

		// Cancel any thread attempting to make a connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}
	}

	// ConnectThread 초기화 device의 모든 연결 제거
	public synchronized void connect(BluetoothDevice device) {
		Log.d(TAG, "connect to: " + device);

		// Cancel any thread attempting to make a connection
		if (mState == STATE_CONNECTING) {
			if (mConnectThread == null) {

			} else {
				mConnectThread.cancel();
				mConnectThread = null;
			}
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to connect with the given device
		mConnectThread = new ConnectThread(device);

		mConnectThread.start();
		setState(STATE_CONNECTING);
	}

	// ConnectedThread 초기화
	public synchronized void connected(BluetoothSocket socket,
			BluetoothDevice device) {
		Log.d(TAG, "connected");

		// Cancel the thread that completed the connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to manage the connection and perform transmissions
		mConnectedThread = new ConnectedThread(socket);
		mConnectedThread.start();

		setState(STATE_CONNECTED);
	}

	// 모든 thread stop
	public synchronized void stop() {
		Log.d(TAG, "stop");

		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		if (mConnectedThread != null) {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		setState(STATE_NONE);
	}

	// 값을 쓰는 부분(보내는 부분)
	public void write(byte[] out) { // Create temporary object
		ConnectedThread r; // Synchronize a copy of the ConnectedThread
		synchronized (this) {
			if (mState != STATE_CONNECTED)
				return;
			r = mConnectedThread;
		} // Perform the write unsynchronized r.write(out); }
	}

	// 연결 실패했을때
	private void connectionFailed() {
		setState(STATE_LISTEN);
	}

	// 연결을 잃었을 때 
	private void connectionLost() {
		setState(STATE_LISTEN);

	}

	private class ConnectThread extends Thread {
		... 
	}

	private class ConnectedThread extends Thread {
		...
	}
}


EX_Bluetooth.zip



마지막에 설명을 쫌 대강대강 하고 넘어간 부분이 많은데, 일단은 현재까지의 프로젝트를 압축파일로 올리겠다.

이후 시간이 된다면 스마트폰 끼리의 블루투스 예제를 주석을 달아서 올리도록 하겠다. 

또한, 글쓴이는 임베디드 보드(동글)과 통신을 하였는데 이 부분에 대해서 궁금하신 분이 많다면 서둘러 예제 프로젝트를 올리도록 하겠다.

관련글 더보기

댓글 영역

  • 이전 댓글 더보기
  • 프로필 사진
    2014.08.27 17:32
    정말 감사합니다. 기존의 블루투스 채팅 앱에서 채팅 기능 을 빼고 블루투스 기능만 제 앱에 탑재 할려 고 하는데 블루투스에 대한 이해가 낮아서 고심하던 찰나에 이런 포스팅을 해주셔서 감사합니다. ㅋ 저는 한국해양대학교 조선과 학생입니다. 자주 방문하도록 하겠습니다.
  • 프로필 사진
    2014.09.23 12:14
    포스팅 잘 보았습니다.
    다름이 아니고, 이번에 블루투스 관련해서 프로젝트를 하는데,
    폰과 스피커를 블루투스 연결해서 전화가 왔을 때, 그 소리를 스피커로 전달하려고 하거든요
    그래서 말씀하신대로 UUID를 HSP UUID로 바꿔서 연결해봤더니, 일단 연결되었다는 의미로
    스피커에서 뚜.뚜 하는 소리는 나는데 실제로 전화가 왔을 때, 그게 스피커로 전달은 안 되더군요
    문제가 뭘까요?
    • 프로필 사진
      2014.09.23 13:17 신고
      스피커를 연결했을때 들리는 소리는
      미디어 사운드가 들리고
      전화가 왔을때는 다른 채널?의 사운드기 때문에 안들리는게 아닐까요?
      핸드폰의 소리 옵션에도 미디어 사운드, 통화음, 시스템사운드 등등으로 나눠져 있어서
      그 문제가 아닐까 싶습니다.. 아니면 연결이 잘 안되었거나?
      자세한건 저도 다시 알아보고 답변 달겠습니다.
  • 프로필 사진
    2014.09.24 10:48
    먼저 답변 감사합니다~!
    현재 저희 상황 다시 한번 상세히 설명 좀 드릴게요^^
    먼저 폰의 기본 블루투스 연결(환경설정에서의~)로 스피커를 연결하면, 물론 음악과 전화음 모두 스피커로 전달이 됩니다.
    헌데 올려주신 앱 통해서 UUID를 HFP, HSP로 바꾼 후 블루투스 연결을 해 보면, 스피커에서 뚜,,뚜 소리와 함께
    연결되었을 때의 소리는 나거든요(이건 HSP UUID로 설정했을 경우입니다.). 연결이 되었으니 당연히 핸드폰으로 전화가 왔을 경우, 스피커에서 소리가 나올 줄 알았는데 그게 아니더군요...아니면 HSP, HFP가 동시에 2개 다 연결이 되야하는 걸까요? 아니면 말씀해 주신대로, 소리 Source 옵션을 바꿔줘야 하는 걸까요? 가능성이 여러가지라서 어떤 방식으로 풀어야 할지...ㅎㅎ
    • 프로필 사진
      2014.10.01 15:46 신고
      저도 직접 해본게 아니라서 뭐라고 말씀을 드리지는 못하겠네요 ^^...
      여러가지 가능성을 열어두고
      귀찬겠지만 일일이 테스트해보시는게 제일 도움이 될 듯 싶습니다.
  • 프로필 사진
    2014.10.20 09:00
    블루투스 사용에 도움이 되었네요 ~~ UUID만 바꾸면 블루투스 스피커와 연결이 가능할까요?
  • 프로필 사진
    2014.10.22 10:23
    비밀댓글입니다
  • 프로필 사진
    2014.11.27 17:19
    안드로이드 블루투스 통신 공부중인데요 이해가 잘 되게 정리가 되있네요!
    좋은 자료 감사합니다.
    한가지 궁금한 것이 있는데요, 현재 구현된 코드로 보면 MainActivity 내부에 BluetoothService 객체가 정의되어있잖아요? 그렇게되면 여기 MainActivity를 통해 인텐트를 통해 다른 액티비티를 불러냈을 때 더이상 블루투스 통신을 할 수 없는 것인가요?
    만약 그렇다면 서비스를 구현을 해야하는 것인지 개념이 안잡혀서 이렇게 물어봅니다!

  • 프로필 사진
    2014.12.09 10:46
    좋은글 잘 봤습니다. 설명을 정말 잘해놓으셨는데 임베디드 동글보드 연동예제도 볼수 있을까요??저도 구현중인데
    소스를 보고 하면 더 이해가 빠를꺼같아서요 ㅎㅎ
  • 프로필 사진
    2015.03.29 16:31
    감사합니다. 많은 도움이 됫습니다.
  • 프로필 사진
    2015.04.11 00:49
    안드로이드간 블루투스 통신을 하려고 하는데 포스팅한 것을 보고 그대로 했는데도
    어플이 맨처음 main.xml에 뜨는 연결 버튼을 누르면 프로그램이 죽어버립니다...
    BluetoothService.java에 enableBluetooth() 메소드 else문에서 인텐트 객체 생성하는데까지만 동작하는것같은데
    다음은 왜 안되는걸까요 ㅠㅠ
  • 프로필 사진
    2015.04.20 17:18
    정상적으로 실행되는 예제에다 친절한 주석까지! 깔끔하게 정리된 포스팅에 눈 호강하고 갑니다! 건승하세요 ^^
  • 프로필 사진
    2015.04.23 17:22
    블루투스에 대해 처음 공부중인데 많은 도움이 되었습니다. 감사합니다!
  • 프로필 사진
    2015.05.23 10:34
    정말 많은 도움 받고 있습니다!!
    그런데 블루투스가 켜지는 것까지는 됬는데 블루투스가 켜진 후에는 앱이 중지된다고 하면서 종료됩니다.
    무슨 문제인지 조언해주시면 정말 감사하겠습니다ㅠㅠ
    • 프로필 사진
      2015.11.23 21:59
      저도 그런상황이군요.
      현재 안드로이드 스튜디오 사용중이고
      안드 버전은 4.4로 맞춰서 사용중인데... ㄷㄷ
    • 프로필 사진
      2015.11.29 07:38
      매니페스트에 블루투스 선언하셔야대용ㅋㅋ
    • 프로필 사진
      2016.10.24 19:37
      저도 같은 상황에서 문제가 발생햇는데 혹시 해결하셧나요
  • 프로필 사진
    2015.05.23 11:05
    그리고 제가 지금 만들려고 하는 것이 pc와 앱을 블루투스로 연결하여 앱을 이용해 pc를 제어하도록 하는 것을 하는데요, 위에 포스팅하신 내용이 서버쪽도 완성이 되어있어야 블투루스 검색 리스트가 실행될 수 있는 것인지,
    제 작업에 대한 조언 부탁드려요ㅠㅠ
  • 프로필 사진
    2015.05.30 19:37
    안녕하세요. 블루투스 왕초보입니다.
    글 써주신 것을 완성하였을때는, 어떤 결과화면을 볼 수 있는건가요?
  • 프로필 사진
    2015.12.10 21:24
    임베디드 보드와의 통신예제 혹시 올려주실수 있나요?ㅜ
  • 프로필 사진
    2016.02.17 08:45
    안녕하십니까^^ 지인의 추천으로 이 곳을 알게 된 안드로이드 초보자입니다만, 블루투스에 대해서 학습 중이던 차에 큰 도움을 받게 되어서
    감사드립니다. 아주 이해하기 쉽게 글을 써놓으셔서 다른 강좌도 공부를 할 예정입니다. 그리고 앞의 글을 보니 임베디드 보드와의 통신에 대해서 예제 프로젝트를 올리겠다고 하셨던데 보이지가 않더군요^^ 강좌를 게시한 시기가 많이 지난 것 같은데 지금이라도 혹시 게시가 가능할까요?
    간곡히 부탁 좀 드리겠습니다.^^; 수고하십시오.
  • 프로필 사진
    2016.10.24 20:02
    안녕하세요~ 너무 잘봤습니다. 한가지 궁금한점이 있어서 글을 올립니다~블루투스 활성화 까지는 되지만검색하려고할때 앱이 중지되는데 문제점을 모르겟습니다 ㅠㅠ 가르쳐주세요 ㅠㅠ
  • 프로필 사진
    2016.11.10 22:45
    @Override
    public void onClick(View v) {
    if(btService.getDeviceState()) {
    // 블루투스가 지원 가능한 기기일 때
    btService.enableBluetooth();
    } else {
    finish();
    }
    }

    에서 @override가 에러나는데 어떻게 해야 고쳐질까요??ㅠㅜ
  • 프로필 사진
    2018.08.01 12:26
    좋은 자료 감사합니다.
    글 작성일이 한참 되서 반영이 안된 내용이 있는데

    마시멜로 버전 올라오면서부터 블루투스 스캔 사용하려면

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    이 두개 권한이 더 필요하게 됬습니다 블루투스 쓰는데 로케이션권한이 왜 필요한건지.. 암튼 매니페스토 추가하면 되겠습니다..
    여기서 하나더. DeviceListActivity 사용시 이상하게 location 권한 요청을 안합니다..
    그래서 onCreate에서

    int MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;

    ActivityCompat.requestPermissions(this,

    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},

    MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);

    강제 권한 요청 해주시면 스캔 잘 돌아갑니다.
    이거때매 2일동안 머리털 많이 빠졌습니당.. 저같은 고생 안하시길 바라며 글 남깁니다.

    영상털이범님 항상 감사합니다.
  • 프로필 사진
    2018.11.13 14:07 신고
    블루투스 리스트가 나오고 클릭했을때 페어링을합니다 그런데 페어링하고난 뒤에 연결이 안되는데.. 왜그런건가요????ㅠㅠ 답글부탁드립니다