[raspberri pi] Raspberry pi 와 Window 블루투스 통신하기 (클래식)
카테고리: Server
라즈베리파이 5를 활용하여 블루투스 서버를 열고, Winform 으로 프로그램을 짜서 해당 데이터를 수신하는 과정을 기술하였습니다.
라즈베리파이에서는 python 코드를 가지고 서버를 열었습니다. 또한, 가상환경을 사용하였습니다.
라즈베리파이 세팅하기
1. 블루투스 모듈 설치
블루투스를 사용하기 위한 모듈들을 우선 다운로드 합니다.
$sudo apt-get install bluez libbluetooth-dev pi-bluetooth
2. Python 설치
가상환경을 만들고, Python을 설치 합니다.
파이썬 관련 유틸리티가 설치가 된 상태에서 파이썬을 재빌드해서 설치해야 이후에 블루투스 관련 모듈을 사용할 수 있습니다.
다음의 명령어들을 넣습니다.
$mkdir temp
$cd temp
$wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz
$tar -xvf Python-3.6.5.tgz
$cd Python-3.6.5
$./configure
$sudo make && sudo make install
이렇게하면 자동으로 temp 폴더를 만들고, python을 설치하게 됩니다. 약 10분정도 소요됩니다.
3. 시리얼 포트 등록
통신을 하기 위한 시리얼 포트를 등록 합니다.
$sudo sdptool add SP
4. 블루투스 설정파일 수정
nano 텍스트 편집기를 사용하여 블루투스의 설정파일을 수정해 줍니다.
$sudo nano /lib/systemd/system/bluetooth.service
해당 명령어로 bluetooth.service를 열어보면 다음과 같은 문구가 있습니다.
$ExecStart=/usr/lib/bluetooth/bluetoothd
해당 문구를 아래와 같이 바꿔줍니다.
$ExecStart=/usr/lib/bluetooth/bluetoothd -C
설정파일을 바꾸면, 바로 적용이 안될 수 있기 때문에, 서비스를 재시작 해줍니다.
$sudo systemctl daemon-reload - 설정 수정사항 재로딩
$sudo systemctl restart bluetooth - 블루투스 서비스 재실행
$sudo systemctl enable bluetooth - 재부팅 시 서비스 자동 등록
5. 블루투스를 검색가능하게 만들기
이제, 블루투스를 검색가능하게 만들어서 다른 디바이스에서 찾을 수 있게 해줍니다. bluetoothctl 를 통해서 설정합니다.
bluetoothctl
해당 명령어를 치면 블루투스 설정을 조작할 수 있게 됩니다.
power on
agent on
discoverable on
pairable on
위의 명령어를 입력하여, 블루투스를 사용할수 있게 해줍니다.
6. 파이썬 코드 짜기
이제, 파이썬 코드를 짜야 합니다. 클래식 블루투스통신을 구현할 수 있도록 파이썬 라이브러리를 설치 합니다.
$sudo pip3 install pybluez pybleno
만약 pip 정책의 변경으로 위 코드가 실행이 안된다면, 다음 코드를 입력하시면 됩니다.
pip install git+https://github.com/pybluez/pybluez.git
그러면, 저희가 만든 가상환경 temp 폴더 안에 의존성들이 모두 설치가 되었을 겁니다.
temp 폴더 안에 파이썬 파일을 하나 넣어주고, 스크립트를 짭니다. 여기서 주의해야할 것은 UUID 입니다.
UUID 는 범용 고유 넘버라고 불리며, 일종의 통신을 위한 프로토콜이라고 생각하시면 됩니다. 각 서비스마다 UUID가 다르기 때문에, 주의하여 기입해야 합니다. 본 포스팅에서는 시리얼 포트 UUID를 사용하였습니다.
uuid = "00001101-0000-1000-8000-00805f9b34fb"
다음은, 파이썬 코드 입니다.
from bluetooth import *
import time # Import the time module to use sleep
def receiveMsg():
uuid = "00001101-0000-1000-8000-00805f9b34fb"
# RFCOMM 포트를 통해 데이터 통신을 하기 위한 준비
server_sock = BluetoothSocket(RFCOMM)
server_sock.bind(('', PORT_ANY))
server_sock.listen(1)
port = server_sock.getsockname()[1]
# 블루투스 서비스를 Advertise
advertise_service(server_sock, "BtChat",
service_id=uuid,
service_classes=[uuid, SERIAL_PORT_CLASS],
profiles=[SERIAL_PORT_PROFILE])
print("Waiting for connection: channel %d" % port)
# 클라이언트가 연결될 때까지 대기
client_sock, client_info = server_sock.accept()
print('Accepted connection from', client_info)
try:
while True:
print("start")
# Sending data to the client every 1 second
message = "Hello from server"
client_sock.send(message)
print(f"Sent: {message}")
# Wait for 1 second before sending the next message
time.sleep(1)
except KeyboardInterrupt:
print("Disconnected")
except BluetoothError as e:
print(f"Bluetooth Error {e}")
except OSError as o:
print(f" OSError {o}")
finally:
client_sock.close()
server_sock.close()
print("All done")
receiveMsg()
위 스크립트를 실행시키면 됩니다. 만약, 해당 스크립트를 실행시켰는데, Permission Denied 라는 에러가 뜬다면, sudo 권한으로 실행시켜줘야 합니다. 가상환경이 블루투스모듈에 직접 접근할 권한이 없기 때문에 발생하는 문제 입니다.
sudo python3 /yourDirectory/script.py
실행
위 과정을 모두 하였다면, 스크립트를 실생시킵니다. 스크립트를 실행시키면, 저희가 찍어놓았던 로그대로 Waiting for connection: channel 1 이라는 문구가 뜰 것입니다.
이제, 클라이언트에서 접속을 시도하면, 반응하여 작동할 것입니다.
👉👉👉[참고 블로그]](https://diymaker.tistory.com/124)
클라이언트 세팅하기
블루투스 클래식 서버의 준비를 마쳤고, 이제는 클라이언트를 세팅 합니다. 이번에 맡은 프로젝트는 Winform 으로 작업하고 있기에, C# 클라이언트를 준비 합니다.
라이브러리는 32feet 라이브러리를 사용하였습니다.
using InTheHand.Net.Sockets;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MotionMaestro_ver._1.Forms
{
public partial class BL_Test : Form
{
// Bluetooth 통신 관련 멤버
private BluetoothClient bluetoothClient;
private StreamReader reader;
private StreamWriter writer;
// 수신 루프를 계속 돌릴지 여부
private bool isReading = false;
public BL_Test()
{
InitializeComponent();
}
private void ConnectBluetooth()
{
try
{
// 기존 연결이 열려 있으면 먼저 정리
DisconnectBluetooth();
// 새 클라이언트 생성
bluetoothClient = new BluetoothClient();
// 주변 블루투스 디바이스 검색
var devices = bluetoothClient.DiscoverDevices();
bool found = false;
foreach (var device in devices)
{
if (device.DeviceName == "raspberrypi") // Raspberry Pi의 Bluetooth 이름
{
textBox1.AppendText($"[INFO] Raspberry Pi 찾음: {device.DeviceAddress}\r\n");
// SerialPort 프로필로 연결
Guid guid = new Guid("00001101-0000-1000-8000-00805f9b34fb");
//Guid guid = new Guid("94f39d29-7d6d-437d-973b-fba39e49d4ee");
bluetoothClient.Connect(device.DeviceAddress, guid);
if (bluetoothClient.Connected)
{
textBox1.AppendText("[INFO] 라즈베리파이에 블루투스 연결 성공!\r\n");
// 스트림 얻기
var stream = bluetoothClient.GetStream();
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
// 자동 Flush 설정 (매번 Write 시점에 실제로 전송)
writer.AutoFlush = true;
// 연결 성공하면 비동기로 수신 시작
isReading = true;
ReceiveDataAsync();
found = true;
break; // 원하는 디바이스를 찾았으므로 반복 종료
}
}
}
if (!found)
{
textBox1.AppendText("[WARN] 'raspberrypi' 블루투스 기기를 찾지 못했습니다.\r\n");
}
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}");
}
}
/// <summary>
/// 비동기로 Raspberry Pi로부터 데이터를 계속 수신합니다.
/// </summary>
private async void ReceiveDataAsync()
{
try
{
while (isReading && bluetoothClient != null && bluetoothClient.Connected)
{
// ReadLineAsync()는 '\n' 기준으로 한 줄을 읽어옴
string data = await reader.ReadLineAsync();
// 상대방이 소켓을 끊었거나, null/빈 문자열이 들어오면 종료로 간주
if (string.IsNullOrEmpty(data))
{
textBox1.AppendText("[INFO] 연결이 종료된 것으로 보입니다.\r\n");
break;
}
// UI 스레드에 안전하게 반영
Invoke(new Action(() =>
{
textBox1.AppendText($"[수신] {data}\r\n");
}));
}
}
catch (Exception ex)
{
// 연결이 끊어지거나 소켓이 닫히면 예외 발생
Invoke(new Action(() =>
{
MessageBox.Show($"수신 중 오류가 발생했습니다: {ex.Message}");
}));
}
finally
{
// 루프 종료 시도 시
isReading = false;
}
}
/// <summary>
/// 현재 연결과 자원을 정리합니다.
/// </summary>
private void DisconnectBluetooth()
{
isReading = false;
if (reader != null)
{
reader.Dispose();
reader = null;
}
if (writer != null)
{
writer.Dispose();
writer = null;
}
if (bluetoothClient != null)
{
if (bluetoothClient.Connected)
{
bluetoothClient.Close();
}
bluetoothClient = null;
}
}
/// <summary>
/// "디바이스 찾기" 버튼 클릭 -> ConnectBluetooth()
/// </summary>
private void buttonFindDevice_Click(object sender, EventArgs e)
{
ConnectBluetooth();
}
}
}
댓글 남기기