ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Raspberry Pi 3 블루투스 페어링 방법(bluetoothctl) 및 C언어 블루투스 서버 예제
    Raspberry Pi/Bluetooth 2019.09.08 08:32



    Bluetooth 모듈이 내장되어 있는 Raspberry Pi 3와  윈도우가 설치된 PC 간에 페어링 및 메시지를 주고 받는 방법을 설명합니다.


    블루투스 통신 테스트는 C언어로 작성된 Bluetooth 서버 예제를 Raspberry PI 3에서 컴파일 및 실행시켜놓고  PC에서 터미널(클라이언트)을 통해 문자열 전송 테스트를 진행 했습니다.




    1. bluetoothctl 명령을 사용하여 페어링하는 방법


    2. Bluetooth 서버 예제 테스트


    3. 블루투스 에코 서버 소스코드




    관련 포스팅


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

    http://webnautes.tistory.com/995  



    최초작성 - 2017.05.22

    최종작성 - 2019. 9. 8




    bluetoothctl 명령을 사용하여 페어링하는 방법

    라즈베리파이와 윈도우 사이에 페어링하는 방법을 설명합니다.



    1. 윈도우의 작업 표시줄 오른쪽 끝에 있는 블루투스 아이콘을 더블클릭합니다.





    2. 라즈베리파이에서 윈도우가 설치된 PC를 검색할 수 있도록 Bluetooh으로 두고 창을 닫지 않고 유지합니다.


    아래 화면처럼 다른 블루투스 디바이스에서 어떤 이름(여기에선 WEBNAUTES-PC, 윈도우에서 지정한 컴퓨터 이름입니다.)으로 검색되는지 알려줍니다. 





    3. Raspberry Pi 3의 터미널에서 윈도우 설치된 컴퓨터와 페어링을 하기 위해  bluetoothctl  명령을 실행합니다. 


    pi@raspberrypi:~ $ bluetoothctl



    현재 디폴트로 설정되어 있는 Raspberry Pi 3에 내장된 Bluetooth 모듈의 맥어드레스를 출력해줍니다.

    [bluetooth] # 옆에 커서가 깜박이면서  명령 입력 대기모드가 됩니다.


    pi@raspberrypi:~ $ bluetoothctl
    [NEW] Controller B8:27:EB:DA:FA:66 raspberrypi [default]
    [bluetooth]#



     bluetoothctl는 블루투스 디바이스를 검색하고 페어링 및 연결 등의 목적으로 사용할 수 있는 유틸리티입니다.


    대화형 명령 방식이라서  명령을 입력하면 바로 결과를 보여줍니다.




    4. show를 입력하면 Raspberry Pi 3에서 사용중인 모듈의 정보를 보여줍니다.


    블루투스 모듈의 맥어드레스는 B8:27:E8:DA:FA:66입니다.

    Powered: yes 현재 모듈의 전원이 켜져 있습니다.

    Discoverable: no 다른 블루투스 장치에서 검색할 수 없도록 설정되어 있습니다.

    Pairable: yes 페어링이 가능하도록 설정되어 있습니다.

    Discovering: no 현재 다른 블루투스 장치를 검색하고 있지 않습니다.

    UUID 목록은 라즈베리파이에서 제공하는 블루투스 서비스입니다. 


    [bluetooth]# show
    Controller B8:27:EB:DA:FA:66
            Name: raspberrypi
            Alias: raspberrypi
            Class: 0x6c0000
            Powered: yes
            Discoverable: no
            Pairable: yes
            UUID: Headset AG                (00001112-0000-1000-8000-00805f9b34fb)
            UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
            UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
            UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
            UUID: Audio Sink                (0000110b-0000-1000-8000-00805f9b34fb)
            UUID: Audio Source              (0000110a-0000-1000-8000-00805f9b34fb)
            UUID: Handsfree                 (0000111e-0000-1000-8000-00805f9b34fb)
            UUID: Handsfree Audio Gateway   (0000111f-0000-1000-8000-00805f9b34fb)
            UUID: Headset                   (00001108-0000-1000-8000-00805f9b34fb)
            Modalias: usb:v1D6Bp0246d052B
            Discovering: no




    5. 모듈의 전원이 꺼져있다면(Powered: no) 다음 명령으로 모듈의 전원을 켜줍니다.


    [bluetooth]# power on
    [CHG] Controller B8:27:EB:DA:FA:66 Class: 0x6c0000
    Changing power on succeeded
    [CHG] Controller B8:27:EB:DA:FA:66 Powered: yes




    6. 주변에 있는 블루투스 디바이스를 검색합니다. 사용중인 컴퓨터 이름(노란색)이 보일때까지 대기합니다. 


    [bluetooth]# scan on
    Discovery started
    [CHG] Controller B8:27:EB:DA:FA:66 Discovering: yes
    [NEW] Device 5D:82:50:E3:FC:67 5D-82-50-E3-FC-67
    [NEW] Device 1B:B1:D2:D9:D1:75 1B-B1-D2-D9-D1-75



    잠시 기다리면 컴퓨터 이름(WEBNAUTES-PC)이 검색되며 블루투스 모듈 맥어드레스도 같이 출력됩니다.


    [bluetooth]# scan on
    Discovery started
    [CHG] Controller B8:27:EB:DA:FA:66 Discovering: yes
    [NEW] Device 5D:82:50:E3:FC:67 5D-82-50-E3-FC-67
    [NEW] Device 1B:B1:D2:D9:D1:75 1B-B1-D2-D9-D1-75
    [NEW] Device 4F:20:E9:3B:B8:24 4F-20-E9-3B-B8-24
    [NEW] Device 69:E5:68:7A:8F:8E 69-E5-68-7A-8F-8E
    [CHG] Device 4F:20:E9:3B:B8:24 RSSI: -91
    [NEW] Device 74:A2:23:5C:3A:31 74-A2-23-5C-3A-31
    [NEW] Device F8:63:3F:27:10:E0 WEBNAUTES-PC



     WEBNAUTES-PC의 블루투스 모듈  맥어드레스를 info 명령과 함께 입력해주면 블루투스 모듈 상태 정보를 얻을 수 있습니다. scan on 명령이 실행된 상태에서만 가능한 명령입니다. 


    [bluetooth]# info F8:63:3F:27:10:E0
    Device F8:63:3F:27:10:E0
            Name: WEBNAUTES-PC
            Alias: WEBNAUTES-PC
            Class: 0x0a010c
            Icon: computer
            Paired: no
            Trusted: no
            Blocked: no
            Connected: no
            LegacyPairing: no
            UUID: Audio Source              (0000110a-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)
            RSSI: -41
            TxPower: 12




    7. 주변 블루투스 디바이스 검색을 중지합니다.


    [bluetooth]# scan off
    Discovery stopped
    [CHG] Controller B8:27:EB:DA:FA:66 Discovering: no




    8. 에이전트를 활성화하고 기본값으로 설정합니다. 페어링 핀 코드를 요청 및 확인을 하기 위해 필요합니다.


    [bluetooth]# agent on
    Agent registered
    [bluetooth]# default-agent
    Default agent request successful




    9. pair 명령과 앞에서 확인한 컴퓨터 WEBNAUTES-PC의 블루투스 모듈 맥어드레스를 같이 적어주면 페어링이 진행됩니다.


    Raspberry Pi에서 페어링시 확인해야 하는 핀 번호(744950)를 보여주는데 WEBNAUTES-PC에서 보여주는 핀번호와 일치하는지 확인하고  yes를 입력해야 합니다.


    [bluetooth]# pair F8:63:3F:27:10:E0
    Attempting to pair with F8:63:3F:27:10:E0
    [CHG] Device F8:63:3F:27:10:E0 Connected: yes
    Request confirmation
    [agent] Confirm passkey 744950 (yes/no): 




    10. 바로 컴퓨터를 확인하면 오른쪽 아래 시계위에 아래와 같은 메시지가 보입니다.

    탭하여 raspberrypi 설정을 클릭합니다.




    윈도우에서도 핀 번호(744950)를 보여주며 Raspberry Pi의 핀번호와 일치하는지 물어봅니다.

    Raspberry Pi에서 확인한 핀번호와 일치하면를 클릭합니다.





    11. Raspberry Pi에서도 yes를 입력하고 엔터를 누릅니다.


    [bluetooth]# pair F8:63:3F:27:10:E0
    Attempting to pair with F8:63:3F:27:10:E0
    [CHG] Device F8:63:3F:27:10:E0 Connected: yes
    Request confirmation
    [agent] Confirm passkey 744950 (yes/no): yes




    12. 양쪽에서 핀번호가 일치한 걸 확인해야 정상적으로 페어링이 이루어집니다.

    Raspberry Pi와 윈도우에서 각각 페어링이 성공했음을 알려줍니다.


    [bluetooth]# pair F8:63:3F:27:10:E0
    Attempting to pair with F8:63:3F:27:10:E0
    [CHG] Device F8:63:3F:27:10:E0 Connected: yes
    Request confirmation
    [agent] Confirm passkey 744950 (yes/no): yes
    [CHG] Device F8:63:3F:27:10:E0 ServicesResolved: yes
    [CHG] Device F8:63:3F:27:10:E0 Paired: yes
    Pairing successful
    [CHG] Device F8:63:3F:27:10:E0 ServicesResolved: no
    [CHG] Device F8:63:3F:27:10:E0 Connected: no
    [CHG] Device F8:63:3F:27:10:E0 Connected: yes





    잠시 기다리면 윈도우의 블루투스 설정 창에 Raspberry Pi가 페어링됨으로  표시됩니다.

    이제 블루투스 설정 창을 닫아도 됩니다.





    13. Raspberry Pi에서 제공하는 블루투스 서비스를 컴퓨터와 연결할지 물어보려고 다음처럼 보일 수 있습니다.  no를 입력합니다.


    Authorize service
    [agent] Authorize service 0000110d-0000-1000-8000-00805f9b34fb (yes/no): no
    [WEBNAUTES-PC]#




    14. 페어링 완료후, trust 명령을 해줘야 합니다.


    [WEBNAUTES-PC]# trust F8:63:3F:27:10:E0
    [CHG] Device F8:63:3F:27:10:E0 Trusted: yes
    Changing F8:63:3F:27:10:E0 trust succeeded




    15.  info 명령과 블루투스 모듈 맥어드레스를 같이 적어주면 모듈 상태를 보여줍니다.

    6번에서 모듈 상태 확인한 것과 비교해보면 PairedTrusted 항목이 yes로 변경되었습니다.


    [WEBNAUTES-PC]# info F8:63:3F:27:10:E0
    Device F8:63:3F:27:10:E0
            Name: WEBNAUTES-PC
            Alias: WEBNAUTES-PC
            Class: 0x0a010c
            Icon: computer
            Paired: yes
            Trusted: yes
            Blocked: no
            Connected: yes
            LegacyPairing: no
            UUID: Audio Source              (0000110a-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
            UUID: Advanced Audio Distribu.. (0000110d-0000-1000-8000-00805f9b34fb)
            UUID: A/V Remote Control        (0000110e-0000-1000-8000-00805f9b34fb)




    16.  페어링을 취소하는 방법을 잠시 설명하겠습니다.  지금 진행하지는 마세요.

    paired-devices 명령으로 현재 페어링된 디바이스 확인을 할 수 있습니다.


    [WEBNAUTES-PC]# paired-devices
    Device F8:63:3F:27:10:E0 WEBNAUTES-PC



    remove 명령과 WEBNAUTES-PC의 블루투스 모듈 맥어드레스를 같이 적어주면 페어링 설정이 Raspberry Pi 에서 삭제됩니다.


    [WEBNAUTES-PC]# remove F8:63:3F:27:10:E0
    Device has been removed
    [CHG] Device F8:63:3F:27:10:E0 Connected: no
    [DEL] Device F8:63:3F:27:10:E0 WEBNAUTES-PC
    [bluetooth]#



    블루투스 특성상 한쪽에서 페어링을 취소했다고 양쪽 모두에 동시에 반영되지 않습니다.

    윈도우의 블루투스 설정 창에서도 장치 제거를 해줘야 합니다.



    다시 페어링을 하려면 6번부터 진행하시면 됩니다.




    17.  connect 명령과 disconnect 명령으로 블루투스 디바이스에 연결 및 연결 해제를 할 수 있습니다.

    여기에서는 테스트 할 수없어서 생략하겠습니다.




    18. quit 명령으로  bluetoothctl를 종료합니다.


    [bluetooth]# quit
    Agent unregistered
    [DEL] Controller B8:27:EB:DA:FA:66 raspberrypi [default]




    Bluetooth 서버 예제 테스트 

    라즈베리파이에서 블루투스 서버를 실행시켜 두고 윈도우의 터미널을 사용하여 테스트하는 방법을 설명합니다. 


    1. C/C++ 컴파일러와 관련 라이브러리, make 같은 도구들이 포함되어 있는 build-essential 패키지를 설치해줍니다. 

    다음처럼 이미 설치되어 있을 수도 있습니다. 


    pi@raspberrypi:~ $ sudo apt-get install build-essential
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    build-essential is already the newest version (12.3).
    0 upgraded, 0 newly installed, 0 to remove and 73 not upgraded.




    2. 예제 코드를 컴파일 해줍니다. 코드는 포스팅 마지막에 있습니다. 


    pi@raspberrypi:~ $ gcc bluetooth_server.c -o bluetooth_server -lbluetooth  -lpthread



    컴파일시 다음과 같은 에러가 나면  libbluetooth-dev 패키지가 설치 안되어 있어서 입니다.


    pi@raspberrypi:~ $ gcc bluetooth_server.c -o bluetooth_server -lbluetooth  -lpthread
    bluetooth_server.c:6:33: fatal error: bluetooth/bluetooth.h: No such file or directory
    #include <bluetooth/bluetooth.h>
                                    ^
    compilation terminated.



    libbluetooth-dev 패키지에는 블루투스 프로그래밍을 하기 위해 필요한 라이브러리가 포함되어 있습니다. 설치 해주고 나서 ..


    pi@raspberrypi:~ $ sudo apt-get install libbluetooth-dev



    다시 컴파일해보면 문제 없이 완료됩니다.


    pi@raspberrypi:~ $ gcc bluetooth_server.c -o bluetooth_server -lbluetooth  -lpthread
    pi@raspberrypi:~ $




    3. 실행시켜 보면 세그멘테이션 폴트(Segmentation Fault)가 발생합니다. 


    pi@raspberrypi:~ $ ./bluetooth_server
    Registering UUID 00001101-0000-1000-8000-00805f9b34fb
    Segmentation fault




    에러를 해결하기 위해서는 SDP Server를 활성화시켜 줘야합니다.

    dbus-org.bluez.service 파일을 편집기로 열어서 


    pi@raspberrypi:~ $ sudo nano /etc/systemd/system/dbus-org.bluez.service



    아래 부분을 찾아서 빨간색 부분을 추가해줍니다.


    ExecStart=/usr/lib/bluetooth/bluetoothd --compat




    4. 재부팅해줘야 설정이 적용됩니다. (데몬 재시작으로는 반영이 안됩니다.)


    pi@raspberrypi:~ $ sudo reboot




    5. 재부팅 완료 후,  퍼미션 문제를 해결하기 위해 다음 한줄을 실행합니다.

    재부팅할때 마다 실행시켜 주어야 세그멘테이션 폴트가 나지 않습니다. 


    pi@raspberrypi:~ $ sudo chmod 777 /var/run/sdp



    부팅할때 마자 실행되도록   홈디렉토리에 있는 .bashrc 파일 끝에 추가해두어도 됩니다. 


    pi@raspberrypi:~ $ sudo nano ~/.bashrc




    페어링은 한번 해주면  다시 할 필요가 없습니다.




    6. 이제 실행시켜보면 아래처럼 나오면서 대기 상태가 됩니다..

    서버 프로그램 bluetooth_server을 실행시켜 놓고 진행해야 합니다.


    pi@raspberrypi:~ $ ./bluetooth_server




    7. 윈도우의 작업 표시줄 오른쪽 끝에 있는 블루투스 아이콘을 더블클릭합니다.





    8. 블루투스 설정 창에서 추가 Bluetooth 옵션을 선택합니다.





    9. Bluetooth 설정 창의 COM 포트 탭에서 추가를 클릭합니다.





    10. 송신(PC에서 연결 시작)을 선택하고 COM 포트를 사용할 장치 아래에 있는 콤보 박스를 클릭합니다.




    콤보 박스 클릭시 보이는 raspberrypi를 선택합니다.

     Raspberry Pi에서 블루투스 서버 프로그램을 실행시켜놔야 raspberrypi가 보입니다.




    서비스 항목에 자동으로 Armatus Bluetooth server가 입력됩니다. 확인을 클릭합니다.





    11. 잠시 기다리면 송신용으로 포트가 추가됩니다. 수신 포트가 추가되어있다면 제거해주세요.  

    이제 확인을 클릭하여 Bluetooth 설정 창을 닫습니다.





    12. 시리얼 통신을 지원하는 터미널 프로그램을 실행시킵니다.  여기선 MobaXterm를 사용했습니다.


    MobaXterm은 아래 링크에서 다운로드 가능합니다.

    http://mobaxterm.mobatek.net/download-home-edition.html



    MobaXterm을 실행한 후, 왼쪽 위에 있는 Session을 클릭합니다.





    지원하는 연결 서비스 중에 Serial을 클릭합니다.




    별다른 설정없이 바로 OK를 클릭합니다. 




    자동으로 앞에서 추가했던 COM6가 선택됩니다. 


    물리적인 COM 포트가 있는 컴퓨터의 경우 다른 COM 포트가 선택되어 있다면 아래 화면처럼 표준 Bluetooth에서 직렬 링크라고 설명이 되어있는 COM 포트를 선택하세요.

    시리얼 통신을 연결을 하기 위해 OK를 클릭합니다.

     




    13. Raspberry Pi의 터미널을 확인하면 접속했다는 것을 알려줍니다. 





    14. 컴퓨터의 터미널에서 helloworld !라고 입력하면 




    15. Raspberry Pi의 터미널에서 받은 문자들을 보여줍니다.




    16. 한글을 입력하면 다음처럼 에코됩니다.





    또한  컴퓨터에서 Raspberry Pi로의 전송만 가능하도록 구현되어 있습니다.




    블루투스 에코 서버 소스코드

    2017.12.8 - c++에서도 컴파일 가능하도록 수정되었습니다.


    구조체를 #define을 이용해서 정의해놓은 BDADDR_ANY, BDADDR_LOCAL를 그냥 C++코드에서 사용하려하면 다음과 같은 에러가 발생합니다.

    error: taking address of temporary [-fpermissive]


    다음처럼 소스코드 내에서 선언을 해줘서 해결했습니다.(참고 3)

    bdaddr_t bdaddr_any = {0, 0, 0, 0, 0, 0};

    bdaddr_t bdaddr_local = {0, 0, 0, 0xff, 0xff, 0xff};



    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/sdp.h>
    #include <bluetooth/sdp_lib.h>
    #include <bluetooth/rfcomm.h>
    #include <sys/wait.h>
    #include <pthread.h>
    #include <signal.h>


    void *ThreadMain(void *argument);
    bdaddr_t bdaddr_any = {0, 0, 0, 0, 0, 0};
    bdaddr_t bdaddr_local = {0, 0, 0, 0xff, 0xff, 0xff};

    int _str2uuid( const char *uuid_str, uuid_t *uuid ) {
        /* This is from the pybluez stack */

        uint32_t uuid_int[4];
        char *endptr;

        if( strlen( uuid_str ) == 36 ) {
            char buf[9] = { 0 };

            if( uuid_str[8] != '-' && uuid_str[13] != '-' &&
            uuid_str[18] != '-' && uuid_str[23] != '-' ) {
            return 0;
        }
        // first 8-bytes
        strncpy(buf, uuid_str, 8);
        uuid_int[0] = htonl( strtoul( buf, &endptr, 16 ) );
        if( endptr != buf + 8 ) return 0;
            // second 8-bytes
            strncpy(buf, uuid_str+9, 4);
            strncpy(buf+4, uuid_str+14, 4);
            uuid_int[1] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            // third 8-bytes
            strncpy(buf, uuid_str+19, 4);
            strncpy(buf+4, uuid_str+24, 4);
            uuid_int[2] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            // fourth 8-bytes
            strncpy(buf, uuid_str+28, 8);
            uuid_int[3] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            if( uuid != NULL ) sdp_uuid128_create( uuid, uuid_int );
        } else if ( strlen( uuid_str ) == 8 ) {
            // 32-bit reserved UUID
            uint32_t i = strtoul( uuid_str, &endptr, 16 );
            if( endptr != uuid_str + 8 ) return 0;
            if( uuid != NULL ) sdp_uuid32_create( uuid, i );
        } else if( strlen( uuid_str ) == 4 ) {
            // 16-bit reserved UUID
            int i = strtol( uuid_str, &endptr, 16 );
            if( endptr != uuid_str + 4 ) return 0;
            if( uuid != NULL ) sdp_uuid16_create( uuid, i );
        } else {
            return 0;
        }

        return 1;

    }



    sdp_session_t *register_service(uint8_t rfcomm_channel) {

        /* A 128-bit number used to identify this service. The words are ordered from most to least
        * significant, but within each word, the octets are ordered from least to most significant.
        * For example, the UUID represneted by this array is 00001101-0000-1000-8000-00805F9B34FB. (The
        * hyphenation is a convention specified by the Service Discovery Protocol of the Bluetooth Core
        * Specification, but is not particularly important for this program.)
        *
        * This UUID is the Bluetooth Base UUID and is commonly used for simple Bluetooth applications.
        * Regardless of the UUID used, it must match the one that the Armatus Android app is searching
        * for.
        */
        const char *service_name = "Armatus Bluetooth server";
        const char *svc_dsc = "A HERMIT server that interfaces with the Armatus Android app";
        const char *service_prov = "Armatus";

        uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid,
              svc_class_uuid;
        sdp_list_t *l2cap_list = 0,
                    *rfcomm_list = 0,
                    *root_list = 0,
                      *proto_list = 0,
                      *access_proto_list = 0,
                        *svc_class_list = 0,
                        *profile_list = 0;
        sdp_data_t *channel = 0;
        sdp_profile_desc_t profile;
        sdp_record_t record = { 0 };
        sdp_session_t *session = 0;

        // set the general service ID
        //sdp_uuid128_create(&svc_uuid, &svc_uuid_int);
        _str2uuid("00001101-0000-1000-8000-00805F9B34FB",&svc_uuid);
        sdp_set_service_id(&record, svc_uuid);

        char str[256] = "";
        sdp_uuid2strn(&svc_uuid, str, 256);
        printf("Registering UUID %s\n", str);

        // set the service class
        sdp_uuid16_create(&svc_class_uuid, SERIAL_PORT_SVCLASS_ID);
        svc_class_list = sdp_list_append(0, &svc_class_uuid);
        sdp_set_service_classes(&record, svc_class_list);

        // set the Bluetooth profile information
        sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
        profile.version = 0x0100;
        profile_list = sdp_list_append(0, &profile);
        sdp_set_profile_descs(&record, profile_list);

        // make the service record publicly browsable
        sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
        root_list = sdp_list_append(0, &root_uuid);
        sdp_set_browse_groups(&record, root_list);

        // set l2cap information
        sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
        l2cap_list = sdp_list_append(0, &l2cap_uuid);
        proto_list = sdp_list_append(0, l2cap_list);

        // register the RFCOMM channel for RFCOMM sockets
        sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
        channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
        rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
        sdp_list_append(rfcomm_list, channel);
        sdp_list_append(proto_list, rfcomm_list);

        access_proto_list = sdp_list_append(0, proto_list);
        sdp_set_access_protos(&record, access_proto_list);

        // set the name, provider, and description
        sdp_set_info_attr(&record, service_name, service_prov, svc_dsc);

        // connect to the local SDP server, register the service record,
        // and disconnect
        session = sdp_connect(&bdaddr_any, &bdaddr_local, SDP_RETRY_IF_BUSY);
        sdp_record_register(session, &record, 0);

        // cleanup
        sdp_data_free(channel);
        sdp_list_free(l2cap_list, 0);
        sdp_list_free(rfcomm_list, 0);
        sdp_list_free(root_list, 0);
        sdp_list_free(access_proto_list, 0);
        sdp_list_free(svc_class_list, 0);
        sdp_list_free(profile_list, 0);

        return session;
    }



    char input[1024] = { 0 };
    char *read_server(int client) {
        // read data from the client
        int bytes_read;
        bytes_read = read(client, input, sizeof(input));
        if (bytes_read > 0) {
            printf("received [%s]\n", input);
            return input;
        } else {
            return NULL;
        }
    }

    void write_server(int client, char *message) {
        // send data to the client
        char messageArr[1024] = { 0 };
        int bytes_sent;
        strcpy(messageArr, message);

        bytes_sent = write(client, messageArr, strlen(messageArr));
        if (bytes_sent > 0) {
            printf("sent [%s] %d\n", messageArr, bytes_sent);
        }
    }

    int main()
    {

        pthread_t thread_id; 
     
        signal( SIGPIPE, SIG_IGN ); 
       
       
        int port = 3, result, sock, client, bytes_read, bytes_sent;
        struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
        char buffer[1024] = { 0 };
        socklen_t opt = sizeof(rem_addr);

        // local bluetooth adapter
        loc_addr.rc_family = AF_BLUETOOTH;
        loc_addr.rc_bdaddr = bdaddr_any;
        loc_addr.rc_channel = (uint8_t) port;

        // register service
        sdp_session_t *session = register_service(port);
        // allocate socket
        sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
        printf("socket() returned %d\n", sock);

        // bind socket to port 3 of the first available
        result = bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
        printf("bind() on channel %d returned %d\n", port, result);

        // put socket into listening mode
        result = listen(sock, 1);
        printf("listen() returned %d\n", result);

        //sdpRegisterL2cap(port);
       
       
        while(1)
        {
            // accept one connection
            printf("calling accept()\n");
            client = accept(sock, (struct sockaddr *)&rem_addr, &opt);
            printf("accept() returned %d\n", client);
       
            ba2str(&rem_addr.rc_bdaddr, buffer);
            fprintf(stderr, "accepted connection from %s\n", buffer);
            memset(buffer, 0, sizeof(buffer));
           
            pthread_create( &thread_id, NULL, ThreadMain, (void*)client);  
        }
       
    }


    void *ThreadMain(void *argument) 

        char buf[1024]; 
     
        pthread_detach(pthread_self()); 
        int client = (int)argument; 

     
        while(1
        {
            char *recv_message = read_server(client);
            if ( recv_message == NULL ){
                printf("client disconnected\n");
                break;
            }
           
            printf("%s\n", recv_message);
           
            write_server(client, recv_message);
        }
     
        printf("disconnected\n" ); 
        close(client); 
     
        return 0;    
    }  





    2019. 9. 8

    라즈베리파이에서 키보드 입력을 받아 안드로이드로 문자열 전송하도록 수정했습니다.

    안드로이드에서 보내는 문자열은 출력되거나 에코되지 않습니다. 


    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/sdp.h>
    #include <bluetooth/sdp_lib.h>
    #include <bluetooth/rfcomm.h>
    #include <sys/wait.h>
    #include <pthread.h>
    #include <signal.h>


    void *ThreadMain(void *argument);
    bdaddr_t bdaddr_any = {0, 0, 0, 0, 0, 0};
    bdaddr_t bdaddr_local = {0, 0, 0, 0xff, 0xff, 0xff};

    int _str2uuid( const char *uuid_str, uuid_t *uuid ) {
        /* This is from the pybluez stack */

        uint32_t uuid_int[4];
        char *endptr;

        if( strlen( uuid_str ) == 36 ) {
            char buf[9] = { 0 };

            if( uuid_str[8] != '-' && uuid_str[13] != '-' &&
            uuid_str[18] != '-' && uuid_str[23] != '-' ) {
            return 0;
        }
        // first 8-bytes
        strncpy(buf, uuid_str, 8);
        uuid_int[0] = htonl( strtoul( buf, &endptr, 16 ) );
        if( endptr != buf + 8 ) return 0;
            // second 8-bytes
            strncpy(buf, uuid_str+9, 4);
            strncpy(buf+4, uuid_str+14, 4);
            uuid_int[1] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            // third 8-bytes
            strncpy(buf, uuid_str+19, 4);
            strncpy(buf+4, uuid_str+24, 4);
            uuid_int[2] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            // fourth 8-bytes
            strncpy(buf, uuid_str+28, 8);
            uuid_int[3] = htonl( strtoul( buf, &endptr, 16 ) );
            if( endptr != buf + 8 ) return 0;

            if( uuid != NULL ) sdp_uuid128_create( uuid, uuid_int );
        } else if ( strlen( uuid_str ) == 8 ) {
            // 32-bit reserved UUID
            uint32_t i = strtoul( uuid_str, &endptr, 16 );
            if( endptr != uuid_str + 8 ) return 0;
            if( uuid != NULL ) sdp_uuid32_create( uuid, i );
        } else if( strlen( uuid_str ) == 4 ) {
            // 16-bit reserved UUID
            int i = strtol( uuid_str, &endptr, 16 );
            if( endptr != uuid_str + 4 ) return 0;
            if( uuid != NULL ) sdp_uuid16_create( uuid, i );
        } else {
            return 0;
        }

        return 1;

    }



    sdp_session_t *register_service(uint8_t rfcomm_channel) {

        /* A 128-bit number used to identify this service. The words are ordered from most to least
        * significant, but within each word, the octets are ordered from least to most significant.
        * For example, the UUID represneted by this array is 00001101-0000-1000-8000-00805F9B34FB. (The
        * hyphenation is a convention specified by the Service Discovery Protocol of the Bluetooth Core
        * Specification, but is not particularly important for this program.)
        *
        * This UUID is the Bluetooth Base UUID and is commonly used for simple Bluetooth applications.
        * Regardless of the UUID used, it must match the one that the Armatus Android app is searching
        * for.
        */
        const char *service_name = "Armatus Bluetooth server";
        const char *svc_dsc = "A HERMIT server that interfaces with the Armatus Android app";
        const char *service_prov = "Armatus";

        uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid,
              svc_class_uuid;
        sdp_list_t *l2cap_list = 0,
                    *rfcomm_list = 0,
                    *root_list = 0,
                      *proto_list = 0,
                      *access_proto_list = 0,
                        *svc_class_list = 0,
                        *profile_list = 0;
        sdp_data_t *channel = 0;
        sdp_profile_desc_t profile;
        sdp_record_t record = { 0 };
        sdp_session_t *session = 0;

        // set the general service ID
        //sdp_uuid128_create(&svc_uuid, &svc_uuid_int);
        _str2uuid("00001101-0000-1000-8000-00805F9B34FB",&svc_uuid);
        sdp_set_service_id(&record, svc_uuid);

        char str[256] = "";
        sdp_uuid2strn(&svc_uuid, str, 256);
        printf("Registering UUID %s\n", str);

        // set the service class
        sdp_uuid16_create(&svc_class_uuid, SERIAL_PORT_SVCLASS_ID);
        svc_class_list = sdp_list_append(0, &svc_class_uuid);
        sdp_set_service_classes(&record, svc_class_list);

        // set the Bluetooth profile information
        sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
        profile.version = 0x0100;
        profile_list = sdp_list_append(0, &profile);
        sdp_set_profile_descs(&record, profile_list);

        // make the service record publicly browsable
        sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
        root_list = sdp_list_append(0, &root_uuid);
        sdp_set_browse_groups(&record, root_list);

        // set l2cap information
        sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
        l2cap_list = sdp_list_append(0, &l2cap_uuid);
        proto_list = sdp_list_append(0, l2cap_list);

        // register the RFCOMM channel for RFCOMM sockets
        sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
        channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
        rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
        sdp_list_append(rfcomm_list, channel);
        sdp_list_append(proto_list, rfcomm_list);

        access_proto_list = sdp_list_append(0, proto_list);
        sdp_set_access_protos(&record, access_proto_list);

        // set the name, provider, and description
        sdp_set_info_attr(&record, service_name, service_prov, svc_dsc);

        // connect to the local SDP server, register the service record,
        // and disconnect
        session = sdp_connect(&bdaddr_any, &bdaddr_local, SDP_RETRY_IF_BUSY);
        sdp_record_register(session, &record, 0);

        // cleanup
        sdp_data_free(channel);
        sdp_list_free(l2cap_list, 0);
        sdp_list_free(rfcomm_list, 0);
        sdp_list_free(root_list, 0);
        sdp_list_free(access_proto_list, 0);
        sdp_list_free(svc_class_list, 0);
        sdp_list_free(profile_list, 0);

        return session;
    }



    char input[1024] = { 0 };
    char *read_server(int client) {
        // read data from the client
        int bytes_read;
        bytes_read = read(client, input, sizeof(input));
        if (bytes_read > 0) {
            printf("received [%s]\n", input);
            return input;
        } else {
            return NULL;
        }
    }

    void write_server(int client, char *message) {
        // send data to the client
        char messageArr[1024] = { 0 };
        int bytes_sent;
        strcpy(messageArr, message);

        bytes_sent = write(client, messageArr, strlen(messageArr));
        if (bytes_sent > 0) {
            printf("sent [%s] %d\n", messageArr, bytes_sent);
        }
    }

    int main()
    {

        pthread_t thread_id; 
     
        signal( SIGPIPE, SIG_IGN ); 
       
       
        int port = 3, result, sock, client, bytes_read, bytes_sent;
        struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
        char buffer[1024] = { 0 };
        socklen_t opt = sizeof(rem_addr);

        // local bluetooth adapter
        loc_addr.rc_family = AF_BLUETOOTH;
        loc_addr.rc_bdaddr = bdaddr_any;
        loc_addr.rc_channel = (uint8_t) port;

        // register service
        sdp_session_t *session = register_service(port);
        // allocate socket
        sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
        printf("socket() returned %d\n", sock);

        // bind socket to port 3 of the first available
        result = bind(sock, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
        printf("bind() on channel %d returned %d\n", port, result);

        // put socket into listening mode
        result = listen(sock, 1);
        printf("listen() returned %d\n", result);

        //sdpRegisterL2cap(port);
       
       
        while(1)
        {
            // accept one connection
            printf("calling accept()\n");
            client = accept(sock, (struct sockaddr *)&rem_addr, &opt);
            printf("accept() returned %d\n", client);
       
            ba2str(&rem_addr.rc_bdaddr, buffer);
            fprintf(stderr, "accepted connection from %s\n", buffer);
            memset(buffer, 0, sizeof(buffer));
           
            pthread_create( &thread_id, NULL, ThreadMain, (void*)client);  
        }
       
    }


    void *ThreadMain(void *argument) 

        char buf[1024]; 
     
        pthread_detach(pthread_self()); 
        int client = (int)argument; 

     
        while(1
        {
            //char *recv_message = read_server(client);
            //if ( recv_message == NULL ){
            //    printf("client disconnected\n");
            //    break;
            //}
    char buf[1024];
    scanf("%s", buf);
    int size = strlen(buf);
    buf[size] = '\n';
           
            printf("%s\n", buf);
           
            write_server(client, buf);
        }
     
        printf("disconnected\n" ); 
        close(client); 
     
        return 0;    





    참고

    1.https://raspberrypi.stackexchange.com/questions/41776/failed-to-connect-to-sdp-server-on-ffffff000000-no-such-file-or-directory/42262#42262


    2. https://github.com/RyanGlScott/BluetoothTest/blob/master/C%20BlueZ%20Server/bluez_server.c


    3.https://www.experts-exchange.com/questions/27636818/C-C-BlueZ-Bluetooth-BDADDR-ANY-warning-for-address-of-temporary.html




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

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

    유튜브 구 독 하 기
    후 원 하 기


    댓글 103

Designed by Tistory.