행복한 하루
아두이노 나노(Arduino Nano 33 IoT) BLE 값을 송수신 가능한 BLE 안드로이드 앱 만들기 본문
아두이노 나노(Arduino Nano 33 IoT) BLE 값을 송수신 가능한 BLE 안드로이드 앱 만들기
변화의 물결 2021. 9. 26. 15:00
안녕하세요.
이전 아두이노 나노 IoT에서 자이로 센서 값을 RFConn 프로그램으로 받는 것을 실험해보았습니다. 그러나 실제로 RFConn 프로그램은 우리가 원하는 BLE 앱으로 배포할 수 없습니다. 그래서 아두이노 나노 33 IoT를 센서장치나 제어 장치로 할 경우 사용자가 만든 BLE 앱으로 만들어야 할 것입니다.
그렇기 하기 위해서 BLE 동작하는 내용과 최소 송수신 기능으로 만드는 것을 목표로 해보겠습니다. 그러나 안정성을 높이려면 추가적으로 소스를 수정해야 합니다. 여기서는 신호를 보내고 받는 것 중점으로 테스트한 버전입니다.
아두이노 나노 33 IoT 버전에 BLE 코드를 올리고 안드로이드 앱으로 접속해서 제어하는 기능을 만들어 보겠습니다. 아두이노 나노 33 IoT 버전 소스는 아래쪽 링크를 참고하시기 바랍니다.
- 기본 소스는 구조와 UI는 오픈소스 BlueLamp를 참조하여 오류 나는 부분들 수정하고 오류 나는 부분들 수정했습니다.
https://github.com/FdevTech/blueLamp
1. 안드로이드 앱 프로젝트 생성
- ArduinoIoT_BLE로 프로젝트를 생성합니다.
1) androidManifest.xml 블루투스 권한 설정
- 블루투스에 접속할 권한을 설정합니다. 이후에 안드로이드 버전에 따라 이것만으로 되지 않고 소스상에서 권한 요청이 필요할 수 있습니다.
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
2) MainActivity.java
(1) 변수 선언
- 화면 설정과 버튼, 메뉴 등에 필요한 변수들을 선언해줍니다.
- BLE 장비를 이름을 직접 Demo Gyroscope로 입력해서 고정하였습니다.
BluetoothManager btManager;
BluetoothAdapter btAdapter=null;
BluetoothLeScanner btScanner;
BluetoothGatt btGatt;
BluetoothGattService btGattservice;
BluetoothGattCharacteristic SWITCHCharacteristic_write;
BluetoothGattCharacteristic SWITCHCharacteristic_read;
Button btnSwitchOn;
Button btnSwitchOff;
Button btnTimer;
Button btnRead;
TextView tvStatus;
TextView tvValue;
ImageSwitcher imageSwitcher;
SeekBar seekBar;
String strAddress;
String strDevicename;
// BLE.setLocalName("Demo Gyroscope") of Arduino Nano 33 IoT source Code
private final static String TEST_BLE_DEVICE_NAME = "Demo Gyroscope";
private final static int REQUEST_ENABLE_BT = 1;
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 1;
MenuItem scanitem;
MenuItem stopscanitem;
MenuItem connecteitem;
MenuItem deconnectitem;
(2) onCreate() 함수 내용 추가
- 화면에 필요한 UI 객체와 변수와 이벤트를 연결시켜줍니다.
btnTimer = (Button)findViewById(R.id.btnTimer);
seekBar=(SeekBar)findViewById(R.id.seek);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
... // 소스 내용이 길이 첨부 원본 소스 참고
}
- BlueLamp 원래 원본 소스에는
@Override
public void onScanResult(int callbackType, ScanResult result) {...}
부분에서 버튼을 활성화시켜놓았는데 서비스가 활성화되기 전에 버튼을 누를 수 있기 때문에
ValueCharacteristic_write = service.getCharacteristic(UUID_VALUE_WRITE);
...
서비스와 보낼 수 있는 변수가 활성화되기 전에 버튼이 활성화되기 때문에 에러가 발생합니다. 그래서 최종적으로 버튼이 활성화할 시점을 BT 서비스가 최종 활성화되었을 때 값을 보내고 받으면 문제가 없습니다.
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) { ... }
그러나 여기서 문제가 또 하나가 발생하는데 버튼을 활성화될 시점에 버튼 활성화시켜주면 함수가 호출 하가다 중지되는 현상이 발생함. 그래서 UI 스레드 처리로 설정해서 UI가 별도로 진행될 수 있도록 처리해야 합니다.
(MainActivity.this).runOnUiThread(new Runnable(){
@Override
public void run() {
btnAllEnable();
}
});
3. activity_main.xml
- 화면 구성은 Arduino Nano 33 IoT에 Lamp가 붙어 있다는 가정 해서 On/Off 버튼과 자이로 센서로 전달되는 X 값을 읽을 수 있는 버튼, 프로그래스 바로 구성되어 있습니다.
4. menu_main.xml (res/menu)
- 오른쪽 상단에 나타날 햄버거 버튼 메뉴를 생성합니다.
- UUID 값으로 서비스를 UUID 각 변수들을 구분할 수 있는 UUID로 지정해줍니다. (여기서 UUID는 임의로 생성한 값입니다.) 센싱 값 X, Y, Z로 구분해 놓았지만, 테스트로 하기 때문에 X값 하나만 받아보겠습니다. 그리고 Write 하기 위한 변수를 하나 더 만들었습니다.
그리고 BLE 규칙에 맞게 UUID를 생성하려면 BLUETOOTH 사이트를 참고해서 생성하면 됩니다. 여기서는 임의의 생성된 UUID키값으로 넣었습니다.
private static final UUID UUID_Service = UUID.fromString("66df5109-edde-4f8a-a5e1-02e02a69cbd5");
private static final UUID UUID_VALUE_WRITE = UUID.fromString("AC9926CA-941E-4CE7-83A4-BBE77C726975");
private static final UUID UUID_VALUE_READ = UUID.fromString("741c12b9-e13c-4992-8a5e-fce46dec0bff");
참고)
- ServiceUUID와 Write UUID가 동일하면 에러 발생
- Read와 Write 하기 전에 서비스가 등록되는 시간이 필요합니다. 그래서 onServicesDiscovered 가 완료된 후에 Read와 Write를 실행하기를 바랍니다.
5. 에러 발생상황
1) 블루투스 함수마다 버전 맞지 않다고 에러가 발생
예를 들어 @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
해결방법)
build.gradle(app)에서 minSdkVersion을 최소 23 이상으로 하고 sync now 에러 나는 것은 사라 집니다.
2) 권한 문제
컴파일하면 다음과 같은 에러가 나오는 경우가 있습니다. xml에 권한을 추가해주었지만, 사용자에게 허가를 받아야 하기 때문에 보안 문제입니다. 그래서
java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results
해결방법)
onCreate() 시작 부분에 사용자에게 권한 요청하는 창을 띄워주는 코드를 넣어줍니다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("This app needs location access");
builder.setMessage("Please grant location access so this app can detect beacons.");
builder.setPositiveButton(android.R.string.ok, null);
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSION_REQUEST_COARSE_LOCATION);
}
});
builder.show();
}
}
위치 허가할지에 대한 다이얼 그 창이 나타나게 된다. 허용하게 되면 이제 에러 없이 블루투스 스캔이 가능해준다.
참고)
String strData = characteristic.getStringValue(2); //offset은 글자를 몇 개 건너뛰고 표시해줄지 값을 지정 2이면 2개 지나서부터 문자열 입력 받음
6. 실행 결과
- 우측 상단에 메뉴 버튼을 눌러 연결 scan을 실행합니다. 그러면 “Demo Gyroscope” BLE 장치를 찾고 연결까지 진행합니다. (아두이노 나노 iot 33 버전을 다른 BLE 다른 이름으로 했다면 검색되지 않습니다.)
- 검색과 연결이 되었다면 버튼이 활성화되고 하단에 문구도 찾았다고 나타납니다.
- ON/OFF 버튼을 누르면 나노 IoT에 장착된 주황색 불이 깜박이는 것을 확인할 수 있습니다. 그리고 전구에 불이 들어온 이미지로 바뀌게 됩니다.
- 실제 장지 LED 제어된 결과
- Read 버튼을 누르면 X, Y, Z 값 중 X 값을 가져옵니다.
- 아두이노 나노에서 센싱 되고 있는 값
- 값을 읽어온 결과
- 우측 상단 버튼을 눌러 BLE 연결을 끊습니다. 그러면 다시 버튼이 비활성화가 됩니다.
다시 말씀드리지만, 소스 자체가 완벽하지 않아서 배포해서 사용할 경우 안정성을 위해서 블루투스 연결, 연결 끊기 등 수정을 해서 사용해야 합니다.
감사합니다.
<참고사이트>
1. blueLamp
https://medium.com/fdevtech/bluetooth-low-energy-on-android-de vice-central-side-5838dd9b1123
https://github.com/FdevTech/blueLamp
2. BLEProofCentral
https://github.com/alexanderlavrushko/BLEProof-collection
3. BLE 통신 개념 + 통신 과정 + Notification 설정
4. BLE(Bluetooth Low Energy)의 이해와 UUID목록
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=geniusus&logNo=221761337501
5. Android Bluetooth Low Energy(샘플 동작 불가)
https://doohans.github.io/android_ble/
6. runOnUiThread 사용하기
https://codetravel.tistory.com/12
'Android > 안드로이드 프로그래밍' 카테고리의 다른 글
com.google.firebase.FirebaseException: An internal error has occurred. [ API key not valid. Please pass a valid API key. ] 해결방법(Solved) (0) | 2020.10.11 |
---|---|
안드로이드 PreferenceScreen 사용 방법(설정 창) (0) | 2020.09.28 |
안드로이드 JSON response 가 배열로 바로 넘어오는 경우 (0) | 2020.09.27 |
[앱 업데이트] DailyEng v1.3.9 – 날짜이동 기능 추가 (20.09.20) (0) | 2020.09.20 |
플래그먼트 갱신,새로고침 하는 방법 (fragment refresh) (0) | 2020.09.02 |