4 분 소요

목표

MYiR MYC-Y6ULX/MYD-Y6ULX(i.MX6ULL, XFCE4 데스크탑) 보드에 USB Realtek 블루투스 동글을 꽂고, Windows PC와 블루투스 시리얼 포트(SPP, Serial Port Profile)로 데이터를 주고받는 것이 목표였다.

결론부터 말하면 된다. 다만 Windows가 먼저 접속하는 방향(송신)은 끝까지 실패했고, Linux가 먼저 접속하는 방향(수신)으로 뒤집으니 바로 해결됐다.

환경

항목 내용
보드 MYiR MYC-Y6ULX (i.MX6ULL), XFCE4 데스크탑
블루투스 어댑터 USB Realtek 동글 (hci1, 48:8F:4C:32:B1:24)
상대 기기 Windows 11 PC (DESKTOP-JVPEEP8, 44:38:E8:8C:7E:30)
스택 BlueZ (bluetoothd)

1. 어댑터 확인부터

hciconfig -a

USB로 꽂은 Realtek 동글이 hci1UP RUNNING PSCAN 상태로 잡혀있는 걸 확인했다. 여기까진 순조로웠다.

2. 첫 번째 함정 — 컨트롤러가 2개였다

bluetoothctl을 켜보니 이렇게 떴다.

[NEW] Controller 9C:B8:B4:B2:90:2B localhost.localdomain #1 [default]
[NEW] Controller 48:8F:4C:32:B1:24 localhost.localdomain

보드에 내장 어댑터와 USB 동글, 컨트롤러가 2개였고 bluetoothctl의 기본(default) 컨트롤러는 우리가 쓰려는 USB 동글이 아니었다. 그동안 pairable on, discoverable on 같은 명령이 전부 엉뚱한 어댑터에 적용되고 있었던 것이다.

bluetoothctl
list
select 48:8F:4C:32:B1:24
show

select로 원하는 컨트롤러를 명시적으로 지정하고 나서야 제대로 진행됐다.

여러 블루투스 컨트롤러가 잡히는 보드에서는 select를 생략하면 아무 에러 없이 엉뚱한 어댑터를 만지게 된다. 가장 먼저 의심해야 할 부분이다.

3. SPP 서비스 등록 — compat 모드가 필요하다

BlueZ 5.x는 기본적으로 sdptool 같은 레거시 SDP 등록 방식을 지원하지 않는다. bluetoothd--compat(-C) 옵션으로 떠있어야 한다.

ps aux | grep bluetoothd
# /usr/lib/bluetooth/bluetoothd -C   ← 이렇게 떠있어야 함

옵션이 없다면 systemd 유닛을 수정한다.

sudo systemctl edit bluetooth.service
[Service]
ExecStart=
ExecStart=/usr/lib/bluetooth/bluetoothd --compat
sudo systemctl daemon-reload
sudo systemctl restart bluetooth

이후 서비스를 등록한다.

sudo sdptool add SP
sdptool browse local   # "Serial Port" 서비스, Channel: 1 확인

4. 두 번째 함정 — “Address already in use”가 반복됨

sudo rfcomm listen /dev/rfcomm0 1

를 실행할 때마다 다음 에러가 반복됐다.

Can't bind RFCOMM socket: Address already in use

rfcomm release 0을 해도, 심지어 커널 모듈을 내렸다 올려도(rmmod rfcommmodprobe rfcomm) 똑같은 에러가 반복됐다. 원인은 어이없게도 예전에 실행해둔 rfcomm listen 백그라운드 job이 계속 살아있었던 것이었다.

jobs
# [1]+  Running    sudo rfcomm listen /dev/rfcomm0 1 &
kill %1

죽이고 나니 그제서야 정상적으로 Waiting for connection on channel 1이 떴다.

터미널 하나로 여러 번 반복 테스트할 땐 jobs로 백그라운드에 뭐가 살아있는지 항상 먼저 확인하자.

5. 세 번째 함정 — /dev/rfcomm0이 안 생긴다

rfcomm listen이 정상적으로 대기 중이어도 /dev/rfcomm0 파일 자체가 안 보였다. 처음엔 udev/mdev가 장치 노드를 안 만들어주는 임베디드 환경 특성이라 생각해서 수동 생성을 시도했다.

cat /proc/devices | grep -i rfcomm   # 216 rfcomm
sudo mknod /dev/rfcomm0 c 216 0
sudo chmod 666 /dev/rfcomm0

그런데 cat /dev/rfcomm0을 하면 No such device or address 에러가 났다. 나중에 알고 보니 이건 정상이었다. rfcomm listen은 실제 연결이 성사되는 순간에야 /dev/rfcommN을 커널 레벨에서 진짜로 바인딩한다. “대기 중” 상태에서는 아직 아무것도 없는 게 맞는 동작이었다.

/dev/rfcommN은 연결이 실제로 붙어야 생기는 게 정상 동작이다. listen 대기 중에 파일이 없다고 당황할 필요 없다.

6. 네 번째 함정 (진짜 벽) — Windows가 접속을 못 함

여기까지 세팅은 다 맞았다. sdptool browse local에 Serial Port 서비스도 잘 등록돼 있고, rfcomm listen도 정상 대기 중이었다. Windows에서 Bluetooth 설정 → COM 포트 → 추가 → 송신(PC에서 연결 시작)으로 COM 포트를 만들고 PuTTY로 열려고 하면 계속 이 에러였다.

Unable to open connection to COM14
Opening '\\.\COM14': Error 121: 세마포 제한 시간이 만료되었습니다.

페어링, compat 모드, SDP 등록, rfcomm listen — 전부 정상인데도 Windows가 여는 순간 타임아웃이 났다. dmesg를 봐도 Linux 쪽엔 연결 시도 흔적조차 없었다. 즉 Windows→Linux 방향의 접속 자체가 무선 레벨에서 안 붙는 상황이었다.

컨트롤러를 재선택하고, 페어링을 지우고 처음부터 다시 하고, 보드와 PC를 둘 다 재부팅해봐도 Windows가 먼저 접속(송신)하는 방향은 끝까지 안 됐다.

7. 해결 — 방향을 뒤집었다

Windows가 클라이언트가 되는 대신, Linux가 클라이언트가 되어 Windows로 먼저 접속하는 방식으로 바꿨다.

Windows: “수신” COM 포트로 설정

Bluetooth 설정 → COM 포트 → 추가 → 수신(장치에서 연결 시작)을 선택하면 Windows는 서버(대기) 역할이 된다. 이 방식은 미리 채널 번호가 바로 할당된다 (예: 채널 5).

Linux: Windows가 광고 중인 채널 확인

sudo sdptool browse 44:38:E8:8C:7E:30 | grep -A5 "Serial Port"
"Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 5

Linux에서 그 채널로 직접 접속

sudo rfcomm connect /dev/rfcomm0 44:38:E8:8C:7E:30 5 &
Connected /dev/rfcomm0 to 44:38:E8:8C:7E:30 on channel 5
Press CTRL-C for hangup

한 번에 붙었다. /dev/rfcomm0도 바로 생성됐다.

crw-rw---- 1 root dialout 216, 0 Jul 23 08:45 /dev/rfcomm0

Windows가 클라이언트로 접속하는 “송신” COM 포트 방식이 특정 USB 블루투스 동글/드라이버 조합에서는 계속 타임아웃날 수 있다. 이럴 땐 역할을 뒤집어서(Linux를 클라이언트로) 시도해보는 게 실질적인 우회책이다.

8. 데이터 송수신 테스트

# 수신 대기
sudo cat /dev/rfcomm0 &

# 송신
echo "hello from board" | sudo tee /dev/rfcomm0 > /dev/null

Windows PuTTY 창과 Linux 터미널 양쪽에서 양방향 데이터가 정상적으로 오가는 것을 확인했다.


재현용 체크리스트

  1. bluetoothctl에서 컨트롤러가 여러 개면 원하는 것을 반드시 select
  2. bluetoothd-C(compat) 옵션으로 떠있는지 확인
  3. sudo sdptool add SPsdptool browse local로 Serial Port 등록 확인
  4. Windows에는 “수신” COM 포트를 만든다 (송신은 이 조합에서 계속 실패했음)
  5. Linux에서 sdptool browse <Windows MAC>으로 채널 번호 확인
  6. sudo rfcomm connect /dev/rfcomm0 <Windows MAC> <채널번호> &로 Linux가 먼저 접속
  7. cat / echo … | tee로 데이터 송수신 테스트

배운 점

  • 여러 블루투스 컨트롤러가 있는 시스템에서는 select를 생략하면 엉뚱한 어댑터를 만지고 있어도 아무 에러 없이 진행된다.
  • rfcomm listen의 “Address already in use”는 커널/모듈 문제보다 백그라운드에 죽지 않고 남아있는 이전 프로세스가 원인인 경우가 많다.
  • /dev/rfcommN은 연결이 실제로 붙어야 생기는 게 정상 동작이다.
  • Windows가 클라이언트로 접속하는 방식이 안 되면, 역할을 뒤집어서 Linux를 클라이언트로 만들어보자.

댓글남기기