ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [두번째-재료구성] 꿍스의 아이를 위한 크리스마스 Android 조이스틱 앱 만들기
    RC카 만들기 2024. 1. 3. 15:52

     

     

    HM-10 AT-09 BT-05 BLE 모듈과 통신하는 앱을 만들어 보겠습니다~

    원래 wifi모듈로 통신해볼까 했는데 음.. 밖에서도 가지고 놀려면  ble 가 제격인 것 같아서  ble 통신 앱으로 진행 고고

     

    [조이스틱 UI 참고 소스]

    조이스틱 UI를 제공해준 안드로이드 라이브러리는 아래의 소스이고 사용에 맞게 조금 수정했습니다.

    https://github.com/harry1453/joystick-view

     

    GitHub - harry1453/joystick-view: A simple Andriod Joystick view

    A simple Andriod Joystick view. Contribute to harry1453/joystick-view development by creating an account on GitHub.

    github.com

     

    그럼 안드로이드 앱을 만들어 봅니다. ㅎㅎ

     

     

    1. 조이스틱 라이브러리

     

    안드로이드 스튜디오에서 새프로젝트를 만든 후

    저는 위의 git 프로젝트 모든 파일은 필요없어서 조이스틱UI 에 해당하는 자바 소스를 수정해서 넣어봤습니다.

     

    위소스에서

    https://github.com/harry1453/joystick-view/blob/master/joystickView/src/main/java/com/harrysoft/joystickview/JoystickView.java

    의 자바소스만 MainActivity.java 가 있는 소스 위치에 넣어줍니다.

     

     

     

    아참! 중요한 설정을 해 봅니다.!

    블루투스 관련 퍼미션을 넣어줍니다.

    이것을 넣어줌으로 앱이 시스템 자원인 블루투스 기능을 사용할 수 있게 해 줍니다.

    AndroidManifest.xml 파일의 아래 위치에 user-feature, user-permission 을 넣어줍니다.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    
        <uses-feature
            android:name="android.hardware.bluetooth"
            android:required="false" />
        <uses-feature
            android:name="android.hardware.bluetooth_le"
            android:required="false" />
    
        <uses-permission
            android:name="android.permission.BLUETOOTH"
            android:maxSdkVersion="30" />
        <uses-permission
            android:name="android.permission.BLUETOOTH_ADMIN"
            android:maxSdkVersion="30" />
    
        <!-- SDK 30 이하를 타겟팅 하는 앱 Bluetooth 권한 -->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
        <!-- SDK 31 이상을 타겟팅 하는 앱 Bluetooth 권한 (Android 12+) -->
        <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

     

    2. layout.xml 파일 수정

    프로젝트를 신규 생성하면 res/layout 폴더에 몇개의 xml 파일들이 있는데 이것들 모두 삭제 하고

    activity_main.xml 파일만 놔두고 아래와 같이 작성함.

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        android:orientation="vertical"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context="com.example.rcjoystic.MainActivity">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal">
            
            <!-- 커스텀 조이스틱 뷰 -->
            <com.example.rcjoystic.JoystickView
                android:id="@+id/joystickLeft"
                android:layout_width="wrap_content"
                android:layout_height="321dp"
                android:layout_weight="1" />
                
        </LinearLayout>
    </LinearLayout>

     

    이후 빌드시 res과련 폴더에서 없는 파일들에 관해서 오류가 날 경우 오류나는 파일들은 모두 삭제 하면 됨.

     

    3.MainActivity.java 파일 수정

    1. 퍼미션 요청

    onCreate()메소드안에 넣어주면 앱 설치 후 실행하면 권한 alert 메세지가 나옵니다. 이 때 모두 수락해 주어야 ble가 작동함

    String[] permissions = {
            android.Manifest.permission.BLUETOOTH_SCAN,
            android.Manifest.permission.BLUETOOTH_ADMIN,
            android.Manifest.permission.ACCESS_COARSE_LOCATION,
            android.Manifest.permission.ACCESS_FINE_LOCATION,
            android.Manifest.permission.BLUETOOTH_CONNECT
    };
    
    ActivityCompat.requestPermissions(MainActivity.this, permissions, 1023);

     

    2. ble 초기화

     

    멤버변수를 선언해 줍니다.

    private BluetoothManager bluetoothManager;
    private BluetoothAdapter adapter; 
    private BluetoothLeScanner scanner;
    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic bluetoothGattCharacteristic;
    
    static String BLE_DEVICE_NAME = "MLT-BT05";
    static String BLE_READ_WRITE_CHARACTERISTIC_UUID = "0000ffe1-0000-1000-8000-00805f9b34fb";


    onCreate()변수 내에 ble 관련 변수를 초기화 합니다.

    bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    adapter = bluetoothManager.getAdapter();
    scanner = adapter.getBluetoothLeScanner();

     

    BLE_READ_WRITE_CHARACTERISTIC_UUID 값은 다음 아두이노 펌웨어 작성에서 다루어 보겠습니다.

     

    2. ble 스캔

    앱이 HM-10 모듈을 검색할수 있게 함.

     

    아래의 ScanCallback은 스캔시 스캔 정보를 받아서 처리하는 선언 변수

    private final ScanCallback scanCallback = new ScanCallback() {
    
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                super.onScanResult(callbackType, result);
    
                if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                    Log.d("permission", "not grandted BLUETOOTH_CONNECT");
                    return;
                }
    
    			// HM-10의 모듈 이름 그대로를 사용하여 검색함
                if (result.getDevice().getName() != null && result.getDevice().getName().equals(BLE_DEVICE_NAME)) {
                    
                    /// 연결
                    result.getDevice().connectGatt(MainActivity.this, false, new BluetoothGattCallback() {
                        @Override
                        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
                            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
                        }
    
                        @Override
                        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
                            super.onPhyRead(gatt, txPhy, rxPhy, status);
                        }
    
                        @Override
                        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                            super.onConnectionStateChange(gatt, status, newState);
    
                            mBluetoothGatt = gatt;
    
                            if (newState == BluetoothProfile.STATE_CONNECTED) {
                                Log.d("BluetoothGattCallback", "STATE_CONNECTED");
                                if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)
                                {
                                    return;
                                }
                                
                                /// 서비스 찾기
                                mBluetoothGatt.discoverServices();
                            }
                        }
    
                        @Override
                        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                            super.onServicesDiscovered(gatt, status);
                            Log.d("BluetoothGattCallback", "onServicesDiscovered");
    
                            if (status == BluetoothGatt.GATT_SUCCESS) {
    
                                for (BluetoothGattService s : gatt.getServices()) {
    
                                    for (BluetoothGattCharacteristic c : s.getCharacteristics())
                                    {
                                        if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
                                            return;
                                        }
    
                                        if (c.getUuid().toString().equals(BLE_READ_WRITE_CHARACTERISTIC_UUID)) {
                                            bluetoothGattCharacteristic = c;                                
                                        }
                                        
                                        /// --- > 연결 완료됨! 
                                        
                                        mBluetoothGatt.readCharacteristic(c);
                                        mBluetoothGatt.setCharacteristicNotification(c, true);
                                    }
                                }
    
                            }
                        }
    
                        @Override
                        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                            super.onCharacteristicRead(gatt, characteristic, status);
                            mBluetoothGatt = gatt;
    
                            if (status == BluetoothGatt.GATT_SUCCESS) {
                            
                            	/// HM-10모듈로 부터 보내온 데이타를 읽음 - 본 소스에선 앱에서 데이타를 보내면 그대로 전달받기로 함
                                for (int index = 0; index < characteristic.getValue().length; index++) {
                                    Log.i("read ble data", characteristic.getUuid().toString() + ":" + String.format("0x%20x", characteristic.getValue()[index]));
                                }
                            }
                        }
    
                        @Override
                        public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
                            super.onCharacteristicRead(gatt, characteristic, value, status);
                            Log.d("BluetoothGattCallback", "onCharacteristicRead");
                        }
    
                        @Override
                        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                            super.onCharacteristicWrite(gatt, characteristic, status);
                            Log.d("BluetoothGattCallback", "onCharacteristicWrite");
                        }
    
                        @Override
                        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                            super.onCharacteristicChanged(gatt, characteristic);
                            Log.d("BluetoothGattCallback", "onCharacteristicChanged");
                        }
    
                        @Override
                        public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
                            super.onCharacteristicChanged(gatt, characteristic, value);
                            Log.d("BluetoothGattCallback", "onCharacteristicChanged");
                        }
    
                        @Override
                        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                            super.onDescriptorRead(gatt, descriptor, status);
                            Log.d("BluetoothGattCallback", "onDescriptorRead");
                        }
    
                        @Override
                        public void onDescriptorRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
                            super.onDescriptorRead(gatt, descriptor, status, value);
                            Log.d("BluetoothGattCallback", "onDescriptorRead");
                        }
    
                        @Override
                        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
                            super.onDescriptorWrite(gatt, descriptor, status);
                            Log.d("BluetoothGattCallback", "onDescriptorWrite");
                        }
    
                        @Override
                        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
                            super.onReliableWriteCompleted(gatt, status);
                            Log.d("BluetoothGattCallback", "onReliableWriteCompleted");
                        }
    
                        @Override
                        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                            super.onReadRemoteRssi(gatt, rssi, status);
                            Log.d("BluetoothGattCallback", "onReadRemoteRssi");
                        }
    
                        @Override
                        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
                            super.onMtuChanged(gatt, mtu, status);
                            Log.d("BluetoothGattCallback", "onMtuChanged");
                        }
    
                        @Override
                        public void onServiceChanged(@NonNull BluetoothGatt gatt) {
                            super.onServiceChanged(gatt);
                            Log.d("BluetoothGattCallback", "onServiceChanged");
    
                        }
                    });
    
                }
            }
        };

     

    위의 ScanCallback으로 스캔 실행

    if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED) {
        scanner.startScan(scanCallback);
    } else {
        Log.d("permission-start-scan", "Not permited");
    }

     

    mBluetoothGatt;
    bluetoothGattCharacteristic;

    위의 변수는 메인 쓰레드에서 실행되기 위하여 선언된 변수들이고 ScanCallback 에서 전달받는 인자들로 대입함.

    그리고 최종 rx(수신 서비스), tx(전송 서비스) uuid 까지 찾은 코드는  onServiceDiscovery 콜백에서 볼수 있음.

     

     

    2. HM-10모듈에게 데이타 전달

    try{
        bluetoothGattCharacteristic.setValue(bytes);
        if (ActivityCompat.checkSelfPermission(MainActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)
        {
            return;
        }
        mBluetoothGatt.writeCharacteristic(bluetoothGattCharacteristic);
    
    }catch (Exception e){
    
    }
    

     

     

     

    이상 관련코드를 살펴보았다

    1. ble모듈 스캔
    2. 연결
    3. 서비스 찾기
    4. 데이타 전송

    이렇게 정리할수 있음.

     

     

    다음은 펌웨어 쪽 코드를 살펴봅니다.!

Designed by Tistory.