2009년 6월 17일 수요일

맥, 리눅스, 심비안용 파이썬 블루투스 프로그래밍 인터페이스(Bluetooth API using Python in Mac OS X, Linux and Symbian)

이전 포스트인 블루투스 시계 기능을 확장해주는 소프트웨어 smartWatchM (Expand Bluetooth watch's feature by smartWatchM) 는 사실 원래부터 알고 있던 소프트웨어를 단지 소개하게 된 게 아니고 원래 블투시계나 블투팔찌를 맥, 아이폰, 또는 아뒤노같은 임베디드 보드에 어떻게 연결해서 사용할 방법을 찾다 보니 알게된 것이었다.



원래 목적대로 구글신에게 좀 더 많은 신탁을 구한 결과 다음의 사항을 알게 되었다.
  • Hands-Free Profile(HFP)를 사용해서 명령과 데이터를 주고받는다.
  • 여기서 컴퓨터 또는 아이폰은 Hands-Free Device(블투시계)에 대해 AG(Audio Gateway)로 동작한다.
  • HFP는 RFCOMM 위에 올라가는 프로파일로 예전 모뎀 제어에 널리 사용되던 AT 커맨드를 사용한다.
예를 들어 전화가 오면 다음과 같은 식으로 동작하게 된다.

 AG  HF device
 RING --> 
 +CLIP 1234567890
-->
 
   전화온거 알려주고
발신자 번호 표시 후
사용자가 전화 받기 버튼을 누름
  <--ATA
 OK-->
 
 음성통화로 연결됨
 

위의 시나리오에서 붉은색 문자가 시리얼 프로파일(RFCOMM)을 사용해 상대방 기기에게 전달되게 된다.

뿐만 아니고 소니 에릭슨 블루투스 장비들의 AT명령어 목록을 담고 있는 PDF파일과 HFP Application Guideline이라는 문서까지 구할 수 있었다. 이에 대한 내용은 좀 더 공부해서 맥/아이폰 또는 아뒤노용의 smartWatchM류의 프로그램 개발에 사용해 볼 예정이다. 이 부분에 대한 상세한 내용은 추후 포스팅에 다시 거론하기로 하겠다.

서론이 너무 길어졌는데 이 목적의 소프트웨어 개발에 가장 기본적이고 필수적인것이 블루투스를 사용해 컴퓨터(또는 아이폰, 아뒤노 등등)이 블투시계/팔찌와 통신을 하는 방법을 찾는 것이다. 일단 사용할 언어는 파이썬으로 생각하고 있기에 파이썬용 블루투스 API를 찾아 본 결과 발견한것이 LightBlue 이다. 파이썬용 크로스 플랫폼 블루투스 API로 맥 OS X, 리눅스 용 뿐 아니고 심비안 시리즈 60 플랫폼도 지원한다. (즉 현재 국내에서 판매중인 노키아 6210s도 Symbian OS 9.3 Series 60 UI를 사용하고 있기 때문에 노키아 6210s에서도 파이썬으로 블루투스 프로그램을 작성할 수 있다는 소리다.)



* 심비안 시리즈 60 플랫폼은 꽤 오래전부터 파이썬을 지원해 왔다. Python for Symbian Series 60

현재 LightBlue에서 지원하는 기능은 다음과 같다.
  • 디바이스 및 서비스 검색
  • RFCOMM과 L2CAP에 대한 표준 소켓 인터페이스
  • OBEX를 통한 파일 전송
  • RFCOMM과 OBEX 서비스 advertising
  • 로컬 디바이스 정보
또한 맥 OS X용은 LightBlue의 커스텀 OBEX를 위해 Objective-C 프레임웍으로 작성한 LightAquaBlue라는 어플리케이션까지 포함되어 있다.




사용법은 매우 간단해서 일단 lightblue를 설치한 다음 예제를 잠시만 보면 매우 쉽게 사용할 수 있다.

무엇보다 먼저 lightblue 모듈을 임포트 해 줘야 한다.

    >>> import lightblue


그 다음 주변에 있는 디바이스와 각 디바이스의 서비스를 검색하는 방법은 다음과 같다.


>>> lightblue.finddevices()
[('00:0E:6D:71:A2:0B', u'My6600', 5243396), ('00:0D:93:19:C8:68',
u'pantherbox', 1057028)]

검색된 디바이스 중 두번째 디바이스가 제공하는 서비스 목록을 확인한다.


>>> lightblue.findservices('00:0D:93:19:C8:68')
[('00:0D:93:19:C8:68', 10, 'OBEX Object Push'), ('00:0D:93:19:C8:68', 15,
'OBEX File Transfer'), ('00:0D:93:19:C8:68', 1, 'Bluetooth-PDA-Sync'),
('00:0D:93:19:C8:68', 3, 'Palm Serial Port')]

GUI를 통해 사용자가 디바이스와 서비스를 선택하게 할 수도 있다.

    >>> lightblue.selectdevice()      # brings up a device-selection GUI 
('00:0E:6D:71:A2:0B', u'My6600', 5243396)

>>> lightblue.selectservice() # brings up a service-selection GUI
('00:0E:6D:71:A2:0B', 2, u'Bluetooth Serial Port')

RFCOMM 소켓을 사용하려면 다음과 같이 하면 된다.

    # client socket
>>> s = lightblue.socket()
>>> s.connect(("00:12:2c:45:8a:7b", 5))
>>> s.send("hello")
5
>>> s.close()

# server socket
>>> s = lightblue.socket()
>>> s.bind(("", 0)) # bind to 0 to bind to dynamically assigned port
>>> s.listen(1)
>>> lightblue.advertise("My RFCOMM Service", s, lightblue.RFCOMM)
>>> conn, addr = s.accept()
>>> print "Connected by", addr
Connected by ('00:0D:93:19:C8:68', 5)
>>> conn.recv(1024)
"hello"
>>> conn.close()
>>> s.close()

위에서 보다시피 표준 소켓 인터페이스를 사용하기 때문에 네트웍 프로그래밍과 별다른 차이가 없다.

OBEX를 사용해 파일을 전송하려면 다음과 같이 하면 된다.


# send a file (can pass file name or file object)
>>> lightblue.obex.sendfile("00:12:2c:45:8a:7b", 10, "MyFile.txt")

# receive a file and save it as MyFile.txt
>>> s = lightblue.socket()
>>> s.bind(("", 0))
>>> lightblue.advertise("My OBEX Service", s, lightblue.OBEX)
>>> lightblue.obex.recvfile(s, "MyFile.txt") # or pass file object instead
>>> s.close()

OBEX 클라이언트 세션을 실행하려면 다음과 같다.


    # send a business card (vCard) to an Object Push service
>>> client = lightblue.obex.OBEXClient("00:12:2c:45:8a:7b", 10)
>>> client.connect()
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
>>> client.put({"name": "MyBusinessCard.vcf"}, file("MyBusinessCard.vcf", "r"))
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>
>>> client.disconnect()
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>

# get a directory listing from a File Transfer service
# (see examples/obex_ftp_client.py for a basic File Transfer client implementation)
>>> client = lightblue.obex.OBEXClient("00:12:2c:45:8a:7b", 15)
>>> ftp_target_uuid = '\xf9\xec{\xc4\x95<\x11\xd2\x98NRT\x00\xdc\x9e\t'
>>> client.connect({"target": ftp_target_uuid})
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={'connection-id': 327258,
'who': '\xf9\xec{\xc4\x95<\x11\xd2\x98NRT\x00\xdc\x9e\t'}>
>>> import StringIO
>>> dirlist = StringIO.StringIO()
>>> client.get({'type': 'x-obex/folder-listing'}, dirlist)
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={'length': 292}>
>>> dirlist.getvalue()
'<?xml version="1.0"?>\n<!DOCTYPE folder-listing SYSTEM
"obex-folder-listing.dtd"\n [ <!ATTLIST folder mem-type CDATA #IMPLIED>\n
<!ATTLIST folder label CDATA #IMPLIED> ]>\n<folder-listing version="1.0">\n
<folder name="C:" user-perm="RW" mem-type="DEV" label="Phone memory"/>\n</folder-listing>'

>>> client.disconnect()
<OBEXResponse reason='OK' code=0x20 (0xa0) headers={}>

로컬 디바이스의 정보를 얻으려면 다음과 같다.


    >>> lightblue.gethostaddr()
'00:0F:3D:5F:20:F0'
>>> lightblue.finddevicename(lightblue.gethostaddr()) # get local device name
u'susebox'
>>> lightblue.gethostclass() # class of device
3670276

* LightBlue를 사용해서 맥을 Lego NXT에 연결시켜주는 프로그램도 나와 있다.

Python/Bluetooth Support for Lego Mindstorms NXT on Mac OS X

* 아뒤노에 블루투스를 연결하기 위해 현재 사용을 고려중인 모듈은 이것이다.

BT2.0+EDR class 2 module
로 시리얼 프로파일을 가지고 있기 때문에 전원과 Rx, Tx만 시리얼 포트에 연결해주면 되기 때문에 아주 쉽게 사용할 수 있다. 또한 저렴한 가격($19.5)이 최대 장점이다.

댓글 1개:

  1. 궁금한게 있습니다 lightblue.obex.sendfile("00:12:2c:45:8a:7b", 10, "MyFile.txt")여기서 포트번호는 10번으로 전부 같은건가요?

    답글삭제