http://www.developer.com/ws/other/article.php/3637741/Experiencing-This-Mysterious-Bluetooth-Stack-Programming.htm
If you ever were involved in any Bluetooth-related development at application level, you admit that it is one big headache from portability point of view. Most mobile devices have either Widcomm BT stack or Microsoft's one. There are also some others, but those two are very popular. Putting aside an issue that they are totally incompatible, developers have had another big trouble: Microsoft's BT SDK was free, while Widcomm BT Stack and SDK were commercial products and therefore were quite (if such a word is appropriate for a price of about USD1500 per developer license) expensive. There were also some third-party products created as wrappers around original SDK, but they usually were not good enough to address all requirements. Recently times have changed, and now, after Broadcomm acquired Widcomm, this SDK has changed its status too—you can download it from here. As I said above, Microsoft and Widcomm BT Stacks are quite different. For instance, while Microsoft follows its regular C style, Widcomm SDK is implemented as a set of C++ classes. Besides, the latter SDK doesn't have sockets support, but does expose virtual COM ports. As a result, you might need to maintain separate wrapper classes for every BT stack. You can go even further and use standard Microsoft's structures to keep various information about BT devices and so forth to minimize maintenance effort. This article will discuss Widcomm Bluetooth SDK. Now, when we are ready to put our hands on something useful, let's consider some simple application which enumerates BT devices lurked around. For every found entity, you will request a list of available services and some other information. So, your magic door to Widcomm SDK is called BtSdkCE.h. Tbe first thing to do is to list exported classes: As you see, you have wide range of API to perform all required operations with BT stack: device discovery interface, few communication clients and servers, virtual serial ports, different connection type interfaces and so forth. In addition, you have a number of OBEX classes available. A class of our main interest is CBtIf, which you will usually inherit and implement few virtual functions to get notified about; for example, discovered device: A couple of them are pure virtual; in other words, you must provide your own implementation. Now, take a look at typical tasks you might want to get solved. What you really should keep in mind is that the Widcomm Bluetooth stack is designed as multiuser, thus all operations are asynchronous. This design influences all your code. To add to this nice but complicating feature, you have to keep in mind that all your derived functions run in a separate thread and therefore must be thread-safe to avoid sporadical and weird behavior. This specifically relates to MFC classes if you ever use them. If the answer is "Yes," you should avoid sharing such objects across the thread boundaries and rather make heap-based copies of data passed via parameters. The stack itself provides few callback functions, but you will probably use the Windows messaging mechanism to propagate the BT events occurred, either with single or multiple messages. An alternative method is to use user-defined callback functions. Having said this, you start from your own BT class derived from CBtIf: You can find in the SDK documentation that OnDeviceResponded is called upon every response from a found BT device. You may easily receive multiple responses from the same device several times during the given inquiry operation. You are responsible for filtering out such duplications. OnDeviceResponded's parameters provide you with all you need to know about a BT device: BT address, class, and name as well as its connection status. You may use it to decide whether this given device is of any interest for your application—for example, if it is a printer or mobile phone. The second pre-virtual function, OnDiscoveryComplete, is called on a query completion when your application requests services list from given device. Thus, your typical solution may look like the following code snippet: Here, I would like to note that DEV_CLASS is 3-byte mask, and BD_NAME is represented in ASCII, so you may need to convert it to Unicode. Also, you can stop the device enumeration at any time calling m_BTStack.StopInquiry(). And, to complete this section, Widcomm SDK has a special function to shut down the stack: that might be quite useful when you want to perform some kind of reset when the device stopped functioning properly for some reason. Well, once your application has trapped some BT device, why not discover which services it actually does support? With the Widcomm SDK, it's a rather trivial task to accomplish. All you're supposed to do is to call CBtIf::StartDiscovery(...) to initiate this process. If you know exactly which service you are going to discover, you may specify its GUID as a second parameter to reduce unwanted notifications. A full list of standard service GUIDs may be found in BtIfClasses.h. Again, it is an asynchronous operation, so when it finishes, a derived virtual function CYourStackClass::OnDiscoveryComplete will be launched. You can obtain the data through CBtIf::ReadDiscoveryRecords(...) call it, for example, like this: CSdpDiscoveryRec class has a number of member functions to gather any required information about given service: service name, RFComm channel, and so forth. You can easily use it as you please. The Widcomm SDK has a number of communications client classes you may use in a quite straightforward way. I won't discuss them here because the SDK samples give you an excellent explanation on various areas: FTP client, OBEX, and so on. In many cases, you can easily adjust them to your own purposes. To make your code more portable between different BT stacks (and Widcomm and Microsoft aren't the only ones), you might go one step further and implement a bit more flexible approach that allows you to select a suitable SDK at runtime. Getting access to the classes exported from some DLL is a big headache in C++, so I won't go this way. My approach employs the same idea as Microsoft ActiveSync with its plug-ins. First, you can declare some common base class with desired functionality that suits your needs: Then, an actual implementation will be some kind of wrapping over given BT SDK—the Widcomm SDK in the current case. You can place this implementation into a DLL and load it dynamically. The only thing left to make it work is to declare a couple of functions with predefined names for creation/deletion of a base class object: Thus, you have some outer interface and an actual implementation hidden inside. You even might call it a "COM Interface" if you want because it is actually the same, but doesn't require any additional registration. From the other side, it may be out of the needs of your application, so it's up to you to decide whether or not to use it. You have learned about the one of available BT Stacks SDKs—Widcomm SDK for Windows Mobile OS. It gives you literally all you need to manage surrounding BT devices (well, everything has some bugs in, and this stack isn't an exception). Sample code snippets in the article illustrate some typical use cases; for more complicated ones, please refer to the SDK documentation. In the next article, you will look at what Microsoft can offer you from their own BT stack. Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. After working almost a decade for an international retail software company as a team leader of the Windows Mobile R department, he has decided to dive into Symbian OS ™ Core development.
Experiencing This Mysterious Bluetooth Stack Programming
October 13, 2006
Widcomm SDK and So Forth
virtual void OnAudioConnected(UINT16 audioHandle){};
virtual void OnAudioDisconnect(UINT16 audioHandle){};
virtual void OnStackStatusChange(CBtIf::STACK_STATUS new_status) {}
virtual void OnInquiryComplete (BOOL success,
short num_responses) {} // {}
virtual void OnDeviceResponded (BD_ADDR bda, DEV_CLASS devClass,
BD_NAME bdName,
BOOL bConnected) {} // = 0;
virtual void OnDiscoveryComplete () {} // = 0;
Device Inquiry
#include <BtSdkCE.h>
...
class CBTWidcommStack : public CBtIf
{
public:
CBTWidcommStack();
virtual ~CBTWidcommStack();
virtual void OnDeviceResponded(BD_ADDR bda, DEV_CLASS devClass,
BD_NAME bdName, BOOL bConnected);
virtual void OnDiscoveryComplete();
virtual void OnInquiryComplete(BOOL bSuccess, short nResponses);
...
};
...
if ( m_BTStack.StartInquiry() )
{
// do something
}
...
void CBTWidcommStack::OnDeviceResponded(BD_ADDR bda,
DEV_CLASS devClass,
BD_NAME bdName,
BOOL bConnected)
{
// Notify the application somehow
}
void CBTWidcommStack::OnInquiryComplete(BOOL bSuccess,
short nResponses)
{
// Notify the application somehow
}
void CBTWidcommStack::OnDiscoveryComplete()
{
// Notify the application somehow
}
WIDCOMMSDK_ShutDown();
Getting the devices' service list
CSdpDiscoveryRec sdpData[16];
// optionally, set it to desired service
GUID guidFilter = GUID_SERVICE_XXX;
int nServiceCount =
m_BTStack.ReadDiscoveryRecords(btAddr,
sizeof(sdpData)/
sizeof(CSdpDiscoveryRec),
sdpData, &guidFilter);
for (int i = 0; i < nServiceCount; i++)
{
// process data
}
Using communications client classes
One Additional Possible Wrapper
class CBTInterfaceBase
{
public:
CBTInterfaceBase();
virtual ~CBTInterfaceBase();
virtual int EnumDevices(.../*you can specify all you needs here*/);
...
};
YOURAPI CBTInterfaceBase* CreateInterface()
{
CBaseInterfaceBase *pItfce = new CSpecificClassHere;
return pItfce;
}
YOURAPI void DestroyInterface(CBaseInterfaceBase*& pItfce)
{
delete pItfce;
pItfce = NULL;
}
Conclusion
About the Author