Raspberry Pi/Bluetooth

Raspberry Pi 3 블루투스 페어링 방법(bluetoothctl) 및 C언어 블루투스 서버 예제

webnautes 2019. 9. 8. 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

최종작성 - 2020. 05. 22

2021. 05. 17 -  Raspberry Pi 4 에서 동작 확인


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

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



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

 

 

블루투스 아이콘이 안보일 경우, 왼쪽아래에 보이는 시작을 클릭 후, 설정 아이콘을 클릭합니다.

 




장치를 선택합니다. 

 

 

왼쪽 메뉴에서 Bluetooth 및 기타 디바이스를 선택합니다.

 



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

 

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

 



추가 설정이 필요할 수도 있습니다. 

아래쪽에 보이는 추가 Bluetooth 옵션을 클릭합니다. 

 



Bluetooth 장치가 이 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 = ;
bdaddr_t bdaddr_local = ;



#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




반응형

천천히 하지만 꾸준히 공부한 내용을 블로그에 공유하는 것이 제 취미생활입니다.
블로그를 시작하게 된 계기는 내가 알게된 내용을 정리하려고 였는데 이왕이면 다른 사람에게도 공유하자였지요.

블로그 내용을 진행해보다가 문제 발생시 지나치지 말고 댓글로 알려주세요. 그래야 다음에 해당 글을 읽는 분에게 도움이 됩니다.



글이 많이 유익하셨다면 토스아이디로 후원해주세요. 토스아이디



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