2017. 3. 20

Android API 23 (Marshmallow) 이후 버전에서 Bluetooth LE 관련 코드가 동작하려면 다음 두 퍼미션중 하나를  AndroidManifest.xml파일에 추가해야 합니다. 여기서는 퍼미션과 관련된 내용만 있습니다. 나머지 내용은 본 포스팅에서 2016. 6.4일 작성된 부분을 보세요..

1
2
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
cs


추가 안하면 다음과 같은 에러가 납니다.

1
java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results
cs



AndroidManifest.xml파일에 android.permission.ACCESS_COARSE_LOCATION 퍼미션을 추가한 후..

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.webnautes.bluetoothble">
 
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />
cs


 MainActivity.java에  Runtime Permission관련 코드 추가해야 합니다. 우선 다음 코드들을 추가해 준 후..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  static final int PERMISSION_REQUEST_CODE = 1;
    String[] PERMISSIONS  = {"android.permission.ACCESS_COARSE_LOCATION"};
 
    private boolean hasPermissions(String[] permissions) {
        int ret = 0;
        //스트링 배열에 있는 퍼미션들의 허가 상태 여부 확인
        for (String perms : permissions){
            ret = checkCallingOrSelfPermission(perms);
            if (!(ret == PackageManager.PERMISSION_GRANTED)){
                //퍼미션 허가 안된 경우
                return false;
            }
        }
       
        //모든 퍼미션이 허가된 경우
        return true;
    }
 
    private void requestNecessaryPermissions(String[] permissions) {
        //마시멜로( API 23 )이상에서 런타임 퍼미션(Runtime Permission) 요청
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(permissions, PERMISSION_REQUEST_CODE);
        }
    }
 
    @Override
    public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
        switch(permsRequestCode){
 
            case PERMISSION_REQUEST_CODE:
                if (grantResults.length > 0) {
                    boolean Accepted = grantResults[0== PackageManager.PERMISSION_GRANTED;
 
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 
                        if (!Accepted  )
                        {
                            showDialogforPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");
                            return;
                        }else
                        {
                            //이미 사용자에게 퍼미션 허가를 받음.
                        }
                    }
                }
                break;
        }
    }
 
    private void showDialogforPermission(String msg) {
 
        final AlertDialog.Builder myDialog = new AlertDialog.Builder( MainActivity.this);
        myDialog.setTitle("알림");
        myDialog.setMessage(msg);
        myDialog.setCancelable(false);
        myDialog.setPositiveButton("예"new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    requestPermissions(PERMISSIONS, PERMISSION_REQUEST_CODE);
                }
 
            }
        });
        myDialog.setNegativeButton("아니오"new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                finish();
            }
        });
        myDialog.show();
    }
cs


MainActivity의 onCreate 메소드에서 블루투스 관련 코드가 시작되기 전에 넣어줘야 합니다.

1
2
3
4
5
       if (!hasPermissions(PERMISSIONS)) { //퍼미션 허가를 했었는지 여부를 확인
            requestNecessaryPermissions(PERMISSIONS);//퍼미션 허가안되어 있다면 사용자에게 요청
        } else {
            //이미 사용자에게 퍼미션 허가를 받음.
        }
cs



전체 프로젝트 파일을 이곳에 올려둡니다.

BluetoothBLE.z01

BluetoothBLE.zip



2016. 6. 4

안드로이드 폰에서 Bluetooth BLE를 사용하는 Arduino 101를 검색하고 제공하는 서비스의  특정 Characteristic에 값을 기록하거나 읽어오는 것을 해봤습니다.  아래 링크에 있는 안드로이드 코드의 일부를 수정하여 사용했습니다.

https://github.com/youten/BLERW



수정한 부분은 DeviceActivity의 코드에서 아두이노 101에서 제공하는 서비스와 Characteristic를 지정해 주어 동작이 잘되도록 한 것과 일부 UI입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    @Override
    public void onClick(View v) {
        Toast.makeText(getApplicationContext(), "value=",  Toast.LENGTH_LONG);
 
        if (v.getId() == R.id.read_state) {
 
            BluetoothGattService disService = mConnGatt.getService(UUID.fromString("19B10000-E8F2-537E-4F6C-D104768A1214"));
            if (disService == null) {
                Log.d("""Dis service not found!");
                return;
            }
 
 
            BluetoothGattCharacteristic characteristic = disService.getCharacteristic(UUID.fromString("19B10001-E8F2-537E-4F6C-D104768A1214"));
 
            if (characteristic == null) {
                Log.d(""" charateristic not found!");
                return;
            }
 
            boolean result = mConnGatt.readCharacteristic(characteristic);
            if (result == false) {
                Log.d("""reading is failed!");
            }
 
 
 
        } else if (v.getId() == R.id.turn_off_led) {
            BluetoothGattService disService = mConnGatt.getService(UUID.fromString("19B10000-E8F2-537E-4F6C-D104768A1214"));
            if (disService == null) {
                Log.d("""Dis service not found!");
                return;
            }
 
 
            BluetoothGattCharacteristic characteristic = disService.getCharacteristic(UUID.fromString("19B10001-E8F2-537E-4F6C-D104768A1214"));
 
            if (characteristic == null) {
                Log.d("""firmware revison charateristic not found!");
                return;
            }
 
 
            characteristic.setValue(new byte[] { (byte0x00 });
                if (mConnGatt.writeCharacteristic(characteristic)) {
 
                }
 
 
        } else if (v.getId() == R.id.turn_on_led) {
 
            BluetoothGattService disService = mConnGatt.getService(UUID.fromString("19B10000-E8F2-537E-4F6C-D104768A1214"));
            if (disService == null) {
                Log.d("""Dis service not found!");
                return;
            }
 
 
            BluetoothGattCharacteristic characteristic = disService.getCharacteristic(UUID.fromString("19B10001-E8F2-537E-4F6C-D104768A1214"));
 
            if (characteristic == null) {
                Log.d("""charateristic not found!");
                return;
            }
 
 
 
            characteristic.setValue(new byte[] { (byte0x01 });
                if (mConnGatt.writeCharacteristic(characteristic)) {
 
                }
 
        }
    }

cs


 

진행하기 전에 아두이노 101에 LED 예제가 업로드 되어 있어야 합니다. 메뉴에서 파일 - 예제 -  CurieBLE - LED를 선택합니다. 

코드 상단에 Arduino 101에서 제공하는 LED 서비스의 UUID와 LED를 제어에 사용되는 Characteristic의 UUID를 볼 수 있습니다.

1
2
3
4
5
6
7
#include <CurieBLE.h>
 
BLEPeripheral blePeripheral;  // BLE Peripheral Device (the board you're programming)
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
 
// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEUnsignedCharCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
cs


아두이노 101이 Bluetooth LE 장치로 검색되어 질때 노출되는 문자열을 LED로 지정하고 있습니다.

1
2
3
  // set advertised local name and service UUID:
  blePeripheral.setLocalName("LED");
  blePeripheral.setAdvertisedServiceUuid(ledService.uuid());
cs


안드로이드 앱을 실행시킨 후.. SCAN 버튼을  눌러주면  BLE장치의 이름, 맥어드레스 그리고 RSSI값을 출력해줍니다.  이때 STOP 버튼을 눌러주면 스캔이 중단됩니다.  첫번째 줄에 있는 LED 장치가 아두이노 101입니다. 선택해주면..




다른 화면이 보이는데 이때  READ_STATE버튼을 클릭하면 현재 LED의 상태값을 읽어와  화면에 출력해줍니다. 





TURN ON LED버튼을 클릭하면 아두이노 101에 연결된 LED가 켜지고 이때 다시 READ_STATE 버튼을 클릭해보면 1로 값이 변경된 것을 확인할 수 있습니다. 





다시 TURN OFF LED를 클릭하여 LED를 끄고 다시 READ_STATE버튼을 클릭해보면 상태값이 0으로 변한 것을 확인 할 수 있습니다ㅏ. 




포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

  1. 김종철 2016.08.15 16:18

    안녕하세요. 제누이노 블루투스를 통해 센서값을 어플로 받아 보는 방법을 찾다가 들렸습니다. 위 소스를 다운받아 어플설치를 했습니다. 그런데 위 사진 중 Send 했을 경우 다른 블루투스 장치들이 검색되지 않습니다. 혹시 스마트폰 안드로이드 버전과도 상관이 있는건가요? 저의 스마트폰 디바이스 버전은 6.0.1을 사용중입니다. 이전 게시된 BLE LED 예제 사용편에서 사용되는 어플은 잘 되는데 이번 편에서는 잡히지 않아서 문의드립니다. ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2016.08.15 21:37 신고

      6.0부터 생긴 권한 체크때문에 문제 생길수 있습니다. build.gradle에서 Target SDK를 23이 아닌 22로 낮추어보세요

  2. Robot 2017.03.20 10:15

    제가 Robot이랑 Android랑 BLE통신하여 움직임 제어 하는 App을 짜보려고 하는데

    구글링 와중에 들리게 되었습니다.

    로봇측엔 BT-410이란 Bluetooth 모듈을 장착하였습니다.

    Android측에서 BLE통신으로 어떠한 16진수 형태의 패킷 값을 보내면 robot쪽에서 그걸 수신받아 해당 값에 맞는 움직임을 보이면 되는데

    연결은 되는데 값을 못읽는지 움직이질 않네요..

    혹시 그런 쪽 코드를 짜보신적이 있으시면 값 전송관련 도움좀 받았으면 해서 글을 올립니다.

    FF | 55 | Data_L | ~Data_L | Data_H | ~Data_H

    ~는 Inverse(1의 보수)를 뜻합니다.
    Ex) ~0xAA ㅡ> 0x55, ~0xF0 ㅡ> 0x0F

    Ex) DATA : 0x1234
    Packet : 0xFF 0x55 0x34 0xCB 0x12 0xED

    값은 위와 같은 형태로 전송 하고 있습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.03.20 14:18 신고

      로봇쪽에서 어떻게 처리 하느냐에 따라 달라지긴 하지만.. 제가 생각하는 방식을 얘기해보면...


      안드로이드에서 제어 명령을 16진수 문자열 패킷으로 만들어 보내면
      "01020304FF22"

      로봇측에서 이 문자열을 받아 파싱해서 16진수 배열로 만들고
      {0x01, 0x02, 0x03, 0x04, 0xFF, 0x22}

      각 바이트값으로 제어 명령을 파악하는 식으로 동작할 듯 합니다..

      예를 들어 마지막 바이트가 0x22이면 LED를 켜고 0x11이면 LED를 끄는 식이요..


      확인은 끝났는데 정리가 되는데 좀 시간이 거릴듯합니다.
      미리 짐작가는 부분을 알려드리면 DeviceActivity에서 onClick(View v)메소드를 수정해보세요..

      전송할 패킷이 {0x01, 0x02, 0x03, 0x04, 0xFF, 0x11}라면 ...

      아래처럼 16진수 스트링으로 바꿔주고..
      String initData = "01020304FF11";

      byte 배열로 바꿉니다.
      byte[] ByteArray = initData.getBytes();


      //이제 값을 전송하면 됩니다.
      characteristic.setValue(ByteArray);
      if (mConnGatt.writeCharacteristic(characteristic)) {

      }



  3. UsbMaster 2017.04.07 11:32

    혹시 Android와 Arduino간에 USB통신 포스팅 괜찮으신가요 ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.08 08:47 신고

      아두이노로 하려면 Arduino Mega ADK R3 또는 Arduino + 확장보드를 이용해야 합니다..

      지금 해당 보드가 없어서 바로 해보긴 힘들듯하네요..

    • UsbMaster 2017.04.10 17:21

      Android와 Andaroid끼리는 가능하신가요?ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.11 00:18 신고

      안드로이드 간에 USB통신인가요? 아직 못해봤네요..

  4. Android 2017.04.10 17:39

    스마트폰이랑 오드로이드보드랑 연결통신 하고있는데 둘다 os가 안드로이드라

    usbResult = usbDeviceConnection.bulkTransfer(endpointOut, bytesHello, bytesHello.length, 0);

    이걸로 스마트폰에서 오드로이드 보드로 데이터를 줬습니다.

    이제 오드로이드에서 스마트폰에서 보낸 데이러를 받으려구하는데

    스마트폰에서 오드로이드가 부가적으로 연결된거라 오드로이드 쪽에선 스마트폰에 EndPoint를

    못가져옵니다.

    그래서 스마트폰에서 보낼때는 EndPointout을 이용해서 보냈는데 오드로이드 쪽에선

    EndPoint를 못가져오니 EndPointIn을 이용해서 가져올수가 없는데

    무슨 방버이 없을까요??

  5. twl 2017.04.21 15:09

    BLE통신으로 아두이노로 HEX값 배열을 보내고 싶은데 가능한가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.21 15:31 신고

      안드로이드에서 패킷내용을 헥사 문자열로 바꿔서 보내고 아두이노에서 다시 원래대로 바꾸면 됩니다..

    • twl 2017.04.21 16:02

      그래서 그 hex 문자열을 보내는 부분을 알아봤느데

      도통 이해가 되질 않아서...ㅠㅠ

      조언좀 부탁드려도될까요 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.21 17:12 신고

      제가 이 댓글 위쪽에 달아놓은 댓글보세요

    • twl 2017.04.21 17:38

      characteristic.setValue(ByteArray);
      if (mConnGatt.writeCharacteristic(characteristic)) {

      }

      이 부분인가요?

      근데 제가 통신할 대상이 로봇인데
      로봇은 uuid가 없는데..

      BluetoothGattService disService = mConnGatt.getService(UUID.fromString("19B10000-E8F2-537E-4F6C-D104768A1214"));

      위 코드를 못써서
      characteristic이 변수값을 못쓰는데
      어떻게 하죠 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.21 17:55 신고

      로봇에서 사용하는 uuid를 알아보셔야 합니다..블루투스 통신 디바이스는 uuid를 사용해서 서로 통신하기 때문에 꼭 있어야해요.. tcp에서 포트랑 같은 개념입니다 ..

      제가 언급한 댓글은 헥사코드를 문자열로 바꿔서 전송하는 거에요.. 배열에 16진수로 숫자적어서 문자열로 변환하는 내용적은 답글 있습니다..

    • twl 2017.04.24 09:56

      https://github.com/ROBOTIS-GIT/OLLOBOT

      위 코드에서 write부분 수정해서 해보라는데

      도통 모르겠네요.. 거기서는 자세한건 알려줄 수 없다고하고...ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.24 16:57 신고

      알려주신 Github보니..
      시리얼 통신을 무선으로 하기 위한 블루투스 프로파일인 Serial Port Profile(SPP)를 사용하고 있네요..

      어디서 문제인지 파악하기 쉽게 다음처럼 진행해보세요..

      PC와 로봇에서 사용하는 블루투스 디바이스와 페어링해서 시리얼 통신이 가능한지 문의한곳에 물어보시고.. 그렇게 해서 패킷을 조합해서 보내 태스트 가능한지 물어보세요..

      그쪽에 얘기한 앱소스에서 앞에서 얘기한 Serial Port Profile(SPP)를 UUID로 사용하고 있기 때문에 가능해야 합니다.

      된다고 하면 Hercules SETUP utility 같은 패킷을 직접 적어서 보낼 수 있는 프로그램으로 태스트해보세요..

      명령어를 보냈을때 로봇의 반응이라든가 응답값을 확인해보세요..

    • twl 2017.04.24 17:16

      알려주신
      Hercules SETUP utility
      이걸로 하는데 serial연결은 되는데

      ff5501fe00ff
      FF 50 01 FE 00 FF 로 보내는데도
      로봇에는 아무 반응이 없네요 ㅠ

    • twl 2017.04.24 18:13

      Serial연결해서

      http://support.robotis.com/ko/에

      나와있는대로 U버튼 코드값인 1을 전송해 주려구

      0x001을 FF 55 01 FE 00 FF로 보냈는데

      아무반응이 없네요 ㅠㅠ

      다해봤는데 왜..왜..

      4646 3535 3031 4645 3030 4646 을
      프로그램에서 HEX값으로 바꾸니까 FF 55 01 FE 00 FF라서 해보고
      0xFF 0x55 0x01 0xFE 0x00 0xFF,
      ff 55 01 fe 00 ff
      ff5501fe00ff 이렇게 다 보냈는데

      1값이 들어오면 로봇에서 삐~ 하고 소리가 나는데 아무 반응이 없네요 ㅠ

      로보티스에서 제공하는 APP깔아서 해보니까 되던데 ㅠㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.24 20:05 신고

      로봇의 동작을 태스트할 수 있는 HEX값으로 패킷을 로봇 회사에 문의해보세요..

      4646 3535 3031 4645 3030 4646을 변환해서 FF 55 01 FE 00 FF를 얻으셨다는데...

      이상하군요..

      한바이트에 0~255까지 값이 저장 가능한데
      4646은 255를 넘는 값이라서요..

  6. helme 2017.04.24 16:54

    버튼 누를매다 String값을 getbyte()를 통해서 byte로 바꿔줬는데

    근데 Log찍어보니까

    같은 스트링값인데 버튼을 누를때마다 Log에 출력되는값이 다 다른데 .. 원래 이런가요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.04.24 20:06 신고

      위에 답글 달았던게 ..
      지금 다시해보니 문제가 있군요..

      아래 포스팅을 참고해보세요..

      java 예제 - 16진수 문자열(hex string)와 바이트 배열(byte array)간 변환하는 방법
      http://webnautes.tistory.com/1130

    • helme 2017.04.25 09:35

      감사합니다 ㅠㅜㅠㅠㅠㅠ

      정말 감사합니다 ㅠㅠ

+ Recent posts