分类: C/C++
2008-04-23 22:07:22
在WinCE5中编程查找并连接周围的蓝牙设备并实现通信
作者:
不好意思,题目有些长,我只是想让更多人能看到这篇文章,提高被搜索引擎搜到几率。
蓝牙(Bluetooth)是一种短距离无线通信技术,利用“蓝牙”技术,能够有效地简化掌上电脑、笔记本电脑和移动电话手机等移动通信终端设备之间的通信,也能够成功地简化以上这些设备与Internet之间的通信,从而使这些现代通信设备与因特网之间的数据传输变得更加迅速高效,为无线通信拓宽道路。说得通俗一点,就是蓝牙技术使得现代一些轻易携带的移动通信设备和电脑设备,不必借助电缆就能联网,并且能够实现无线上因特网,其实际应用范围还可以拓展到各种家电产品、消费电子产品和汽车等信息家电,组成一个巨大的无线通信网络。
利用蓝牙做无线通信的应用非常广泛,甚至有些工业控制都在使用,编程玩家当然也是爱不释手,比如用自己的宝贝手机编一个蓝牙控制程序来控制自己的电脑,看电影的时候可以当做遥控器来使用。
那么如何在手机中编程实现蓝牙通信即是本文将要阐述的内容,本文以Windows Mobile 5.0
为开发平台,简单介绍用蓝牙APIs进行编程的要点,并附上一个源代码供大家参考。
在PC上编写蓝牙应用程序,可以使用 WindowsXP Platform
SDK中的蓝牙APIs,也可以使用WinSock,二者兼可实现蓝牙设备的查找,服务的发现、配对。大部分蓝牙设备都提供虚拟串口的服务,所以真正的通信可以使用系统中虚拟的串口,像操作普通串口一样进行数据通信,当然也可以使用网络编程服务器侦听客户端连接的方式来进行数据通信。PC端蓝牙编程我有一个简单的源代码,可以给初学者一点启发,下载地址是:
在Windows Mobile中我们不能使用Windows XP Platform SDK,像
BluetoothFindFirstDevice()、BluetoothFindNextDevice()等函数都没办法使用,但是我们还可以使用蓝牙SOCKET来编程。
蓝牙通信第一步就是搜索蓝牙设备。这里要用到
WSALookupServiceBegin()、WSALookupServiceNext()、WSALookupServiceEnd()三个API函数。为了纪录搜索到的所有周边蓝牙设备信息,自定义一个结构体和数组变量:
// 远程蓝牙设备详细信息 typedef struct _RemoteBthDevInfo { _RemoteBthDevInfo () { memset ( szName, 0, sizeof(szName) ); memset ( &RemoteAddr, 0, sizeof(BT_ADDR) ); memset ( &LocalAddr, 0, sizeof(BT_ADDR) ); } TCHAR szName[64]; BT_ADDR RemoteAddr; BT_ADDR LocalAddr; } t_RemoteBthDevInfo; typedef CArray搜索周边蓝牙设备的具体代码如下:t_Ary_RemoteBthDevInfo;
// // 用Socket 函数搜索附近的蓝牙设备,成功时返回设备数,否则返回-1 // int CBlueTooth_WM::ScanNearbyBthDev_Direct () { m_Ary_RemoteBthDevInfo.RemoveAll (); SetWaitCursor (); WSAQUERYSET querySet; HANDLE hLookup; DWORD flags = LUP_RETURN_NAME | LUP_RETURN_ADDR; union { CHAR buf[5000]; double __unused; // ensure proper alignment }; LPWSAQUERYSET pwsaResults = (LPWSAQUERYSET) buf; DWORD dwSize = sizeof(buf); BOOL bHaveName; ZeroMemory(&querySet, sizeof(querySet)); querySet.dwSize = sizeof(querySet); querySet.dwNameSpace = NS_BTH; if ( ::WaitForSingleObject ( m_hEvtEndModule, 0 ) == WAIT_OBJECT_0 ) return -1; if (ERROR_SUCCESS != WSALookupServiceBegin (&querySet, LUP_CONTAINERS, &hLookup)) { ResotreCursor (); MsgBoxErr ( _T("WSALookupServiceBegin failed") ); return (-1); } ZeroMemory(pwsaResults, sizeof(WSAQUERYSET)); pwsaResults->dwSize = sizeof(WSAQUERYSET); pwsaResults->dwNameSpace = NS_BTH; pwsaResults->lpBlob = NULL; BOOL bError = FALSE; while ( TRUE ) { if ( ::WaitForSingleObject ( m_hEvtEndModule, 0 ) == WAIT_OBJECT_0 ) break; if ( ERROR_SUCCESS == WSALookupServiceNext (hLookup, flags, &dwSize, pwsaResults) ) { ASSERT (pwsaResults->dwNumberOfCsAddrs == 1); BT_ADDR b = ((SOCKADDR_BTH *)pwsaResults->lpcsaBuffer->RemoteAddr.lpSockaddr)->btAddr; bHaveName = pwsaResults->lpszServiceInstanceName && *(pwsaResults->lpszServiceInstanceName); t_RemoteBthDevInfo RemoteBthDevInfo; if ( bHaveName ) { hwSnprintf ( RemoteBthDevInfo.szName, sizeof(RemoteBthDevInfo.szName), _T("%s"), pwsaResults->lpszServiceInstanceName ); } RemoteBthDevInfo.RemoteAddr = b; CSADDR_INFO* pCSAddr = (CSADDR_INFO *)pwsaResults->lpcsaBuffer; RemoteBthDevInfo.LocalAddr = ((SOCKADDR_BTH *)pCSAddr->LocalAddr.lpSockaddr)->btAddr; TRACE (L"%s ( xx )\n", RemoteBthDevInfo.szName, GET_NAP(b), GET_SAP(b) ); Add_RemoteBthDevInfo ( RemoteBthDevInfo ); } else { if ( WSAGetLastError() != WSA_E_NO_MORE ) { bError = TRUE; ResotreCursor (); MsgBoxErr ( L"Lookup bluetooth device failed" ); } break; } } WSALookupServiceEnd(hLookup); ResotreCursor (); if ( bError ) return (-1); return (int)m_Ary_RemoteBthDevInfo.GetSize(); }搜索周边的蓝牙设备重要的是要得到他们的蓝牙地址,这个地址是通信建立的关键,以上代码将搜索到的地址、设备名称等信息保存在m_Ary_RemoteBthDevInfo数组中了。那接下来就是该建立连接了,我现在测试的是将Windows Mobile手机和一个装有蓝牙芯片的心电仪设备建立连接,该设备的提供的蓝牙名称为“CONTRON”,连接过程其实很简单,做过SOCKET网络编程的人都知道,代码如下:
// // 连接到蓝牙服务器中的某一个服务,成功返回,失败返回错误代码 // int CBlueTooth_WM::ConnectToBlueToothServer ( BT_ADDR ServerAddress, LPCTSTR lpszServiceGUID ) { if ( m_socketClient==INVALID_SOCKET ) { GUID ServerGuid; if ( !StringToGUID(lpszServiceGUID, &ServerGuid) ) return -1; m_socketClient = socket (AF_BT, SOCK_STREAM, BTHPROTO_RFCOMM); if (m_socketClient == INVALID_SOCKET) { return WSAGetLastError(); } SOCKADDR_BTH sa; memset (&sa, 0, sizeof(sa)); sa.addressFamily = AF_BT; sa.serviceClassId = ServerGuid; sa.btAddr = ServerAddress; if (connect (m_socketClient, (SOCKADDR *)&sa, sizeof(sa)) == SOCKET_ERROR) { m_socketClient = INVALID_SOCKET; return WSAGetLastError(); } } return 0; }SOCKET连接一旦建立起来,就能像访问普通的网络通信套接字一样来访问蓝牙设备里的数据了,请看一下数据通信代码:
// // 数据传输(收/发) // 返回成功传输的字节数。失败时返回-1;连接已经断开,返回-2;处理不能立即完成,返回-3 // int CBlueTooth_WM::Transmite ( LPVOID lpData, int nSize, BOOL bSend ) { if ( m_socketClient==INVALID_SOCKET ) return -1; if ( !lpData ) return -1; if ( nSize < 1 ) return 0; int iBytesTransmited = 0; if ( bSend ) iBytesTransmited = send (m_socketClient, (char *)lpData, nSize, 0); else iBytesTransmited = recv (m_socketClient, (char *)lpData, nSize, 0); if ( iBytesTransmited > 0 ) return iBytesTransmited; int nLastError = WSAGetLastError (); if ( nLastError == WSAENETDOWN || nLastError == WSAENOTCONN || nLastError == WSAENOTSOCK || nLastError == WSAESHUTDOWN || nLastError == WSAETIMEDOUT ) { Disconnect (); return -2; } if ( nLastError == WSAEWOULDBLOCK ) return -3; return -1; }其实用虚拟串口的方式也可以实现蓝牙的通信,在Windows Mobile 中使用如下代码就可以虚拟出一个串口设备:
PORTEMUPortParams pp; memset (&pp, 0, sizeof(pp)); pp.channel = 0; pp.flocal = FALSE; pp.device = reinterpret_cast<_SOCKADDR_BTH*>(pwsaResults->lpcsaBuffer->LocalAddr.lpSockaddr)->btAddr; memcpy(&pp.uuidService, &CLSID_NULL, sizeof(GUID)); pp.uiportflags = RFCOMM_PORT_FLAGS_REMOTE_DCB; int port = 4; // 1到9中任何一个数 HANDLE bth = RegisterDevice (L"COM", port, L"btd.dll", (DWORD)&pp));串口创建好以后直接使用 CreateFile()、WriteFile()、ReadFile()、DeviceIoControl()等函数像操作普通串口一样来操作它。例如:
HANDLE hCommPort = CreateFile (L"COM4:", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);附带源代码是一个简单的蓝牙通信程序。程序执行后,出现如下界面:
先选择“Setting …”菜单,打开如下画面:
按下“Scan”按钮,程序开始搜索周边的蓝牙设备,并将所有找到的设备名称添加在下拉列表中,这里选择我的心电仪设备“CONTRON”,并设置好我将要使用的蓝牙服务GUID,然后选择“OK”菜单保存配置信息,回到前面的界面。
选择菜单“Connect Bluetooth Device”,程序将开始连接刚才配置的蓝牙设备,连接成功以后便可选择菜单“Send Test Data”来进行收发数据的测试了。
由于时间和水平的关系,代码和文章写得都不咋的,但愿能起到抛砖引玉的作用。