본문 바로가기

개발자는 오늘도 달립니다.

[라즈베리파이] Raspberry Pi 3 블루투스 세팅 및 설정 방법 본문

라즈베리파이

[라즈베리파이] Raspberry Pi 3 블루투스 세팅 및 설정 방법

✍21시간 2020. 12. 4. 13:36

기본적으로 RPi 3는 bluetooth 모듈을 내장하고 있습니다.

RPi 3 에서 블루투스 프로그래밍을 하거나 기능적으로 활용하는 방법을 블로깅하기로 했습니다.

 

 

 

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

 

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

 

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

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

 

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

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

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

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

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

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

 

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

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

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

 

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

 

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

 

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

agent ondefault-agent

 

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

 

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

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

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

 

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

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

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

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

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

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

 

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

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

paired-devices

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

 

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

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

 

 

Bluetooth 서버 예제 테스트 

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

 

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

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

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install libbluetooth-dev

 

gcc bluetooth_server.c -o bluetooth_server -lbluetooth  -lpthread

sudo apt-get install libbluetooth-dev

 

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

 

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

 

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

sudo nano /etc/systemd/system/dbus-org.bluez.service

 

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

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

 

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

 

sudo reboot

 

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

 

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

sudo chmod 777 /var/run/sdp

 

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

 

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

 

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

 

블루투스 서버 소스

#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;    

 

Comments