본문 바로가기
PROGRAMMING CODE/ANDROID STUDIO

[Android Studio] 라즈베리, 안드로이드 스튜디오 블루투스 통신 (안드로이드 스튜디오 코드 2021기준 최신본 / 주석)

by daye_ 2021. 12. 5.

 

 

안드로이드로 블루투스 통신 코드를 찾다가 다 오래된거고, 같은 코드만 올라와있어서

안드로이드 자체에서 사라진 메소드랑 v7코드 오류 수정, 주석 등등 달아서 올려본다.

( 틀린부분 있으면 댓글 남겨주세요! )

 

그리고 필자는 통신 연결은 되고 송수신은 안되는 문제를 겪었는데

라즈베리파이에 Port번호를 3에서 1로 바꾸니 통신이 되었다. 

 

 

 

 

https://webnautes.tistory.com/995?category=924825

 

Raspberry Pi 3와 Android 앱 간에 Bluetooth 통신 테스트

bluetoothctl 명령을 사용하여 Bluetooth 모듈이 내장되어 있는 Raspberry Pi 3와 안드로이드폰 간에 페어링을 하고나서 Bluetooth 통신 테스트를 진행했습니다. Android 앱에서 문자열을 전송하면 Raspberry Pi 3..

webnautes.tistory.com

 

            https://m.blog.naver.com/beaqon/221412580559

 

라즈베리파이 안드로이드 블루투스 데이터 송수신 방법 (+예제)

마감일이 얼마 남지 않았다. 라즈베리파이3를 사용하고 싶지만 교수의 허락이 필요한 관계로 라즈베리파이0...

blog.naver.com

 

 

MainActivity.java

package com.example.bluetoothtestapplication;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity
{
    private final int REQUEST_BLUETOOTH_ENABLE = 100;
    private ActivityResultLauncher<Intent> resultLauncher;

    private TextView mConnectionStatus;
    private EditText mInputEditText;

    ConnectedTask mConnectedTask = null;
    static BluetoothAdapter mBluetoothAdapter;
    private String mConnectedDeviceName = null;
    private ArrayAdapter<String> mConversationArrayAdapter;
    static boolean isConnectionError = false;
    private static final String TAG = "BluetoothClient";

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /* send 버튼 누르면 sendMessage호출로 메세지를 보냄 */
        Button sendButton = (Button)findViewById(R.id.send_button);
        sendButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v){
                String sendMessage = mInputEditText.getText().toString();
                if ( sendMessage.length() > 0 ) {
                    sendMessage(sendMessage);
                }
            }
        });
        
        mConnectionStatus = (TextView)findViewById(R.id.connection_status_textview);
        mInputEditText = (EditText)findViewById(R.id.input_string_edittext);
        ListView mMessageListview = (ListView) findViewById(R.id.message_listview);

        mConversationArrayAdapter = new ArrayAdapter<>( this,
                android.R.layout.simple_list_item_1 );
        mMessageListview.setAdapter(mConversationArrayAdapter);

        Log.d( TAG, "Initalizing Bluetooth adapter...");


        /* 기기가 블루투스를 지원하지 않으면 종료 */
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) {
            showErrorDialog("This device is not implement Bluetooth.");
            return;
        }

        /* 블루투스 연결을 확인하고 showPairedDevicesListDialog()를 호출해 기기리스트 호출 */
        if (!mBluetoothAdapter.isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            intent.putExtra("Call", REQUEST_BLUETOOTH_ENABLE);
            resultLauncher.launch(intent);
        }
        else {
            Log.d(TAG, "Initialisation successful.");

            showPairedDevicesListDialog();
        }
        
        resultLauncher = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        if(result.getResultCode() == RESULT_OK){ //블루투스가 활성화되었음
                            Intent intent = result.getData();
                            int CallType = intent.getIntExtra("CallType", 0);
                            if(CallType == 0){
                                //실행될 코드
                            }else if(CallType == 1){
                                //실행될 코드
                            }else if(CallType == 2){
                                //실행될 코드
                            }
                        }
                    }
                });
    }

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

        if ( mConnectedTask != null ) {

            mConnectedTask.cancel(true);
        }
    }


    /* 디바이스 연결 후 송수신을 위한 소켓 생성 */
    private class ConnectTask extends AsyncTask<Void, Void, Boolean> {

        private BluetoothSocket mBluetoothSocket = null;
        private BluetoothDevice mBluetoothDevice = null;

        //시리얼 통신(SPP)을 하기위한 RFCOMM 블루투스 소켓 생성
       ConnectTask(BluetoothDevice bluetoothDevice) {
            mBluetoothDevice = bluetoothDevice;
            mConnectedDeviceName = bluetoothDevice.getName();

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

            try {
                mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
               //Log.~~ 로그캣에 찍기위해 사용
                Log.d( TAG, "create socket for "+mConnectedDeviceName);

            } catch (IOException e) {
                Log.e( TAG, "socket create failed " + e.getMessage());
            }

            mConnectionStatus.setText("connecting...");
        }


        @Override
        protected Boolean doInBackground(Void... params) {
            
            // 디바이스 검색 중지. 느려짐 방지
            mBluetoothAdapter.cancelDiscovery(); 

            // 연결
            try {
                mBluetoothSocket.connect();
            } catch (IOException e) {
                // 오류 시 종료
                try {
                    mBluetoothSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, "unable to close() " +
                            " socket during connection failure", e2);
                }

                return false;
            }

            return true;
        }


        /* 소켓 생성 성공시 connectedTask AsyncTask 실행*/
        @Override
        protected void onPostExecute(Boolean isSucess) {

            if ( isSucess ) {
                connected(mBluetoothSocket);
            }
            else{

                isConnectionError = true;
                Log.d( TAG,  "Unable to connect device");
                showErrorDialog("Unable to connect device");
            }
        }
    }


    /* 소켓 실행 */
    public void connected( BluetoothSocket socket ) {
        mConnectedTask = new ConnectedTask(socket);
        mConnectedTask.execute();
    }


    /* 실제 데이터 주고 받기 */
    private class ConnectedTask extends AsyncTask<Void, String, Boolean> {

        private InputStream mInputStream = null;
        private OutputStream mOutputStream = null;
        private BluetoothSocket mBluetoothSocket = null;

        ConnectedTask(BluetoothSocket socket){

            mBluetoothSocket = socket;
            try {
                mInputStream = mBluetoothSocket.getInputStream();
                mOutputStream = mBluetoothSocket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "socket not created", e );
            }

            Log.d( TAG, "connected to "+mConnectedDeviceName);
            mConnectionStatus.setText( "connected to "+mConnectedDeviceName);
        }


        /* 이 메소드에서 대기하며 수신되는 문자열이 있을 시 버퍼에 저장 */
        @Override
        protected Boolean doInBackground(Void... params) {

            byte [] readBuffer = new byte[1024];
            int readBufferPosition = 0;


            while (true) {

                if ( isCancelled() ) return false;

                try {

                    int bytesAvailable = mInputStream.available();

                    if(bytesAvailable > 0) {

                        byte[] packetBytes = new byte[bytesAvailable];

                        mInputStream.read(packetBytes);

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

                            byte b = packetBytes[i];
                            if(b == '\n')
                            {
                                byte[] encodedBytes = new byte[readBufferPosition];
                                System.arraycopy(readBuffer, 0, encodedBytes, 0,
                                        encodedBytes.length);
                                String recvMessage = new String(encodedBytes, "UTF-8");

                                readBufferPosition = 0;

                                Log.d(TAG, "recv message: " + recvMessage);
                                publishProgress(recvMessage);
                            }
                            else
                            {
                                readBuffer[readBufferPosition++] = b;
                            }
                        }
                    }
                } catch (IOException e) {

                    Log.e(TAG, "disconnected", e);
                    return false;
                }
            }

        }

        /* 수신된 문자 리스트에 업데이트 */
        /* 다른곳에 업데이트 하고자 하면 아래 메소드 수정 */
        @Override 
        protected void onProgressUpdate(String... recvMessage) {

            mConversationArrayAdapter.insert(mConnectedDeviceName + ": " + recvMessage[0], 0);
        }

        @Override 
        protected void onPostExecute(Boolean isSucess) {
            super.onPostExecute(isSucess);

            if ( !isSucess ) {
                closeSocket();
                Log.d(TAG, "Device connection was lost");
                isConnectionError = true;
                showErrorDialog("Device connection was lost");
            }
        }
 
        /* 소켓 닫기 */
        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);
            closeSocket();
        }

        void closeSocket(){
            try {
                mBluetoothSocket.close();
                Log.d(TAG, "close socket()");
            } catch (IOException e2) {
                Log.e(TAG, "unable to close() " +
                        " socket during connection failure", e2);
            }
        }

        /* 받은 메세지 출력 */
        void write(String msg){

            msg += "\n";

            try {
                mOutputStream.write(msg.getBytes());
                mOutputStream.flush();
            } catch (IOException e) {
                Log.e(TAG, "Exception during send", e );
            }

            mInputEditText.setText(" ");
        }
    }


    /* 페어링 되어있는 블루투스 장치들의 목록확인.*/
    public void showPairedDevicesListDialog()
    {
    Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
    final BluetoothDevice[] pairedDevices = devices.toArray(new BluetoothDevice[0]);

    if ( pairedDevices.length == 0 ){ //연결 할 디바이스 없을 때
        showQuitDialog( "No devices have been paired.\n"
                +"You must pair it with another device.");
        return;
    }

    String[] items;
    items = new String[pairedDevices.length];
    for (int i=0;i<pairedDevices.length;i++) {
        items[i] = pairedDevices[i].getName();
    }

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Select device");
    builder.setCancelable(false);
    builder.setItems(items,new DialogInterface.OnClickListener() {
        //디바이스 선택하면 기기 선택 종료
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();

            //다비이스 연결
            ConnectTask task = new ConnectTask(pairedDevices[which]);
            task.execute();
        }
    });
    builder.create().show();
    }

    public void showErrorDialog(String message)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Quit");
        builder.setCancelable(false);
        builder.setMessage(message);
        builder.setPositiveButton("OK",new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                if ( isConnectionError  ) {
                    isConnectionError = false;
                    finish();
                }
            }
        });
        builder.create().show();
    }

    public void showQuitDialog(String message)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Quit");
        builder.setCancelable(false);
        builder.setMessage(message);
        builder.setPositiveButton("OK",  new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                finish();
            }
        });
        builder.create().show();
    }

    /* 메세지 보내기 */
    void sendMessage(String msg){

        if ( mConnectedTask != null ) {
            mConnectedTask.write(msg);
            Log.d(TAG, "send message: " + msg);
            mConversationArrayAdapter.insert("Me:  " + msg, 0);
        }
    }

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

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_BLUETOOTH_ENABLE) {
            if (resultCode == RESULT_OK) {
                //BlueTooth is now Enabled
                showPairedDevicesListDialog();
            }
            if (resultCode == RESULT_CANCELED) {
                showQuitDialog("You need to enable bluetooth");
            }
        }
    }


}

 

 activity_main_xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="connection state : "/>

        <TextView
            android:id="@+id/connection_status_textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text=""/>

    </LinearLayout>

    <LinearLayout
        android:weightSum="1"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/input_string_edittext"
            android:hint="input text here"
            android:layout_weight="0.8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/send_button"
            android:layout_weight="0.2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Send" />
    </LinearLayout>

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/message_listview"/>

</LinearLayout>