안녕하세요 열코입니다.


이번에는 안드로이드에서 블루투스 연동하는 방법에 대해 알아보겠습니다.


먼저 안드로이드 프로젝트를 빈 프로젝트로 생성합니다.


그리고 안드로이드에서 블루투스에 관한 권한을 얻기 위해


app - src - main - AndroidManifest.xml에 다음과 같이 추가합니다.



1. AndroidManifest.xml에 추가


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

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


- BLUETOOTH : 블루투스 연결 요청, 연결 수락 및 데이터 전송과 같은 블루투스 통신을 수행하기 위해 선언합니다.


- BLUETOOTH_ADMIN : 블루투스 설정 조작 및 앱에서 기기를 검색하기 위해 선언합니다.



2. activity_main.xml에 추가


매인 화면의 액티비티는 다음과 같이 구성되어 있습니다.


<LinearLayout

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">


    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="수신 받은 데이터 : "/>


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:id="@+id/textView_receive"/>


    </LinearLayout>


    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="송신 할 데이터 : "/>


        <EditText

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:id="@+id/editText_send"

            android:ems="10"/>


    </LinearLayout>


    <Button

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="데이터 송신"

        android:id="@+id/button_send"/>

</LinearLayout>



3. MainActivity.java에 추가


* 함수들의 호출 구조가 약간 복잡하게 되어있습니다. 설명 하는 순서를 잘 따라와 주시기 바랍니다.


매인 액티비티 클래스 안에 다음과 변수들을 선언합니다. 변수들의 용도는 주석으로 설명돼있습니다.


private static final int REQUEST_ENABLE_BT = 10; // 블루투스 활성화 상태

private BluetoothAdapter bluetoothAdapter; // 블루투스 어댑터

private Set<BluetoothDevice> devices; // 블루투스 디바이스 데이터 셋

private BluetoothDevice bluetoothDevice; // 블루투스 디바이스

private BluetoothSocket bluetoothSocket = null; // 블루투스 소켓

private OutputStream outputStream = null; // 블루투스에 데이터를 출력하기 위한 출력 스트림

private InputStream inputStream = null; // 블루투스에 데이터를 입력하기 위한 입력 스트림

private Thread workerThread = null; // 문자열 수신에 사용되는 쓰레드

private byte[] readBuffer; // 수신 된 문자열을 저장하기 위한 버퍼

private int readBufferPosition; // 버퍼 내 문자 저장 위치


private TextView textViewReceive; // 수신 된 데이터를 표시하기 위한 텍스트 뷰

private EditText editTextSend; // 송신 할 데이터를 작성하기 위한 에딧 텍스트

private Button buttonSend; // 송신하기 위한 버튼


위 변수들을 모두 선언해주시고 onCreate 함수 안에 다음과 같이 작성합니다.



@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);


    // 각 컨테이너들의 id를 매인 xml과 맞춰준다.

    textViewReceive = (TextView)findViewById(R.id.textView_receive);

    editTextSend = (EditText)findViewById(R.id.editText_send);

    buttonSend = (Button)findViewById(R.id.button_send);


    // 블루투스 활성화하기

    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 블루투스 어댑터를 디폴트 어댑터로 설정


    if(bluetoothAdapter == null) { // 디바이스가 블루투스를 지원하지 않을 때

        // 여기에 처리 할 코드를 작성하세요.

    }

    else { // 디바이스가 블루투스를 지원 할 때

        if(bluetoothAdapter.isEnabled()) { // 블루투스가 활성화 상태 (기기에 블루투스가 켜져있음)

            selectBluetoothDevice(); // 블루투스 디바이스 선택 함수 호출                

        }

        else { // 블루투스가 비 활성화 상태 (기기에 블루투스가 꺼져있음)

            // 블루투스를 활성화 하기 위한 다이얼로그 출력

            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 

            // 선택한 값이 onActivityResult 함수에서 콜백된다.

            startActivityForResult(intent, REQUEST_ENABLE_BT); 

        }

    }

}


마찬가지로 간단한 설명은 모두 주석으로 처리했습니다.


onCreate 함수는 프로그램(App)이 실행되고 MainActivity가 자동으로 호출되며 호출되는 순간 onCreate 함수가 호출됩니다.


따라서 가장 먼저 호출되는 함수라고 생각하시면 됩니다. 이 함수안에 블루투스를 활성화 하는 코드를 작성했습니다.


해당 코드를 실행하면 다음과 같은 다이얼로그(대화상자)가 나오며 '사용'을 누르면 디바이스의 블루투스를 켜주는 코드가 되겠습니다.



만약 블루투스를 지원하지 않는 기기의 경우 본 프로그램이 정상 작동하지 않으니 꼭 디바이스가 블루투스를 지원하지 않을 때 < 조건 문에서 처리할 코드를 작성하시기 바랍니다.


☞ "예를들어 finish() 함수를 호출하여 프로그램을 종료시킬 수 있습니다."



권한 요청 다이얼로그에서 '사용'을 누르면 onActivityResult() 함수가 호출됩니다.



onActivityResult() 함수는 Alt + insert 키를 눌러 Override Methods를 눌러 onActivityResult를 선택하면 자동으로 생성됩니다.


생성된 onActivityResult() 함수에 다음과 같이 코드를 작성합니다.



@Override

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

    super.onActivityResult(requestCode, resultCode, data);


    switch (requestCode) {

        case REQUEST_ENABLE_BT :

            if(requestCode == RESULT_OK) { // '사용'을 눌렀을 때

                selectBluetoothDevice(); // 블루투스 디바이스 선택 함수 호출

            }

            else { // '취소'를 눌렀을 때

                // 여기에 처리 할 코드를 작성하세요.

            }

            break;

    }

}


'사용'을 눌렀다면 selectBluetoothDevice() 함수를 호출합니다.


만약 '취소'를 눌렀다면 블루투스를 활성화 할 수 없기 때문에 처리 할 코드를 작성 해 주시면 됩니다.


여기까지 블루투스가 비활성화 돼있을 때 수행되는 코드였으며,



아래부터는 블루투스가 이미 활성화 돼있을 때 수행되는 코드입니다.


디바이스가 블루투스를 지원하며 블루투스가 이미 켜져있는 상태라면 selectBluetoothDevice() 함수를 호출합니다.


selectBluetoothDevice() 함수를 다음과 같이 작성합니다. 


public void selectBluetoothDevice() {

    // 이미 페어링 되어있는 블루투스 기기를 찾습니다.

    devices = bluetoothAdapter.getBondedDevices();

    // 페어링 된 디바이스의 크기를 저장

    pariedDeviceCount = devices.size();

    // 페어링 되어있는 장치가 없는 경우

    if(pariedDeviceCount == 0) {

        // 페어링을 하기위한 함수 호출

    }

    // 페어링 되어있는 장치가 있는 경우

    else {

        // 디바이스를 선택하기 위한 다이얼로그 생성

        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setTitle("페어링 되어있는 블루투스 디바이스 목록");

        // 페어링 된 각각의 디바이스의 이름과 주소를 저장

        List<String> list = new ArrayList<>();

        // 모든 디바이스의 이름을 리스트에 추가

        for(BluetoothDevice bluetoothDevice : devices) {

            list.add(bluetoothDevice.getName());

        }

        list.add("취소");


        // List를 CharSequence 배열로 변경

        final CharSequence[] charSequences = list.toArray(new CharSequence[list.size()]);

        list.toArray(new CharSequence[list.size()]);


        // 해당 아이템을 눌렀을 때 호출 되는 이벤트 리스너

        builder.setItems(charSequences, new DialogInterface.OnClickListener() {

            @Override

            public void onClick(DialogInterface dialog, int which) {

                // 해당 디바이스와 연결하는 함수 호출

                connectDevice(charSequences[which].toString());

            }

        });


        // 뒤로가기 버튼 누를 때 창이 안닫히도록 설정

        builder.setCancelable(false);

        // 다이얼로그 생성

        AlertDialog alertDialog = builder.create();

        alertDialog.show();

    }

}


페어링 되어있는 장치가 없는 경우 페어링을 직접 수행하는 함수를 호출해야합니다.


블루투스 페어링 하는 함수는 최대한 빨리 작성하겠습니다.


현재 프로그램은 이미 페어링 되어있는 블루투스 디바이스만 찾을 수 있습니다.





페어링 되어있는 장치가 있는경우 그 목록을 리스트로 만들어 다이얼로그에 띄웁니다.


해당 리스트 아이템을 눌렀을 때 해당 디바이스와 연결하는 함수(connectDevice 함수)를 호출합니다.


connectDevice() 함수는 다음과 같이 작성합니다.


public void connectDevice(String deviceName) {

    // 페어링 된 디바이스들을 모두 탐색

    for(BluetoothDevice tempDevice : devices) {

        // 사용자가 선택한 이름과 같은 디바이스로 설정하고 반복문 종료

        if(deviceName.equals(tempDevice.getName())) {

            bluetoothDevice = tempDevice;

            break;

        }

    }

    // UUID 생성

    UUID uuid = java.util.UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

    // Rfcomm 채널을 통해 블루투스 디바이스와 통신하는 소켓 생성

    try {

        bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);

        bluetoothSocket.connect();

        // 데이터 송,수신 스트림을 얻어옵니다.

        outputStream = bluetoothSocket.getOutputStream();

        inputStream = bluetoothSocket.getInputStream();

        // 데이터 수신 함수 호출

        receiveData();

    } catch (IOException e) {

        e.printStackTrace();

    }

}


이 함수는 위 다이얼로그에서 선택한 블루투스 디바이스와 연결하는 함수입니다.


블루투스를 연결하고 데이터를 수신하기 위한 함수 receiveData() 함수를 호출합니다.


receiveData() 함수는 다음과 같이 작성합니다.



public void receiveData() {

    final Handler handler = new Handler();

    // 데이터를 수신하기 위한 버퍼를 생성

    readBufferPosition = 0;

    readBuffer = new byte[1024];


    // 데이터를 수신하기 위한 쓰레드 생성

    workerThread = new Thread(new Runnable() {

        @Override

        public void run() {

            while(Thread.currentThread().isInterrupted()) {

                try {

                    // 데이터를 수신했는지 확인합니다.

                    int byteAvailable = inputStream.available();

                    // 데이터가 수신 된 경우

                    if(byteAvailable > 0) {

                        // 입력 스트림에서 바이트 단위로 읽어 옵니다.

                        byte[] bytes = new byte[byteAvailable];

                        inputStream.read(bytes);

                        // 입력 스트림 바이트를 한 바이트씩 읽어 옵니다.

                        for(int i = 0; i < byteAvailable; i++) {

                            byte tempByte = bytes[i];

                            // 개행문자를 기준으로 받음(한줄)

                            if(tempByte == '\n') {

                                // readBuffer 배열을 encodedBytes로 복사

                                byte[] encodedBytes = new byte[readBufferPosition];

                                System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);

                                // 인코딩 된 바이트 배열을 문자열로 변환

                                final String text = new String(encodedBytes, "US-ASCII");

                                readBufferPosition = 0;

                                handler.post(new Runnable() {

                                    @Override

                                    public void run() {

                                        // 텍스트 뷰에 출력

                                        textViewReceive.append(text + "\n");

                                    }

                                });

                            } // 개행 문자가 아닐 경우

                            else {

                                readBuffer[readBufferPosition++] = tempByte;

                            }

                        }

                    }

                } catch (IOException e) {

                    e.printStackTrace();

                }

                try {

                    // 1초마다 받아옴

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    });

    workerThread.start();

}


수신 된 데이터를 쓰레드를 통해 1초마다 바이트 배열로 받아 인코딩 후 스트링 변수로 변환 하여 텍스트 뷰에 출력하는 코드입니다.


여기까지 블루투스를 통한 데이터 수신 과정이었습니다.



아래는 블루투스를 통한 데이터 송신 과정에 대해 설명하겠습니다.


먼저 이전에 만든 버튼에 다음과 같은 버튼 리스너를 추가합니다. (위치는 onCreate안 buttonSend findViewbyId 밑에 만들면 됩니다.)


buttonSend.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

        sendData(editTextSend.getText().toString());

    }

});


버튼을 누르면 sendData() 함수를 호출하는 이벤트 리스너입니다.


sendData()함수를 다음과 같이 작성합니다.


void sendData(String text) {

    // 문자열에 개행문자("\n")를 추가해줍니다.

    text += "\n";

    try{

        // 데이터 송신

        outputStream.write(text.getBytes());

    }catch(Exception e) {

        e.printStackTrace();

    }

}


여기까지 잘 따라오셨다면 블루투스 데이터 송,수신 프로그램이 올바르게 작동 할 것입니다.




이상 안드로이드 블루투스 연동이었습니다.


부족한 부분이나 오류가 나는 부분, 기타 질문사항은 댓글로 남겨주세요!


공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.



to Top