Sunday, September 11, 2011

Bluetooth Server Programming on Windows

As the world is converging under the roof of augmented reality, most of the devices around us are becoming wireless. Thanks to innovative technologies like WiFi, IR, Bluetooth, ZigBee which enable seamless interaction among various devices manufactured by thousands of vendors all around the world.

Having said that, even I have been working on a project to set up a Bluetooth server on PC which publishes few services to clients. The difficulty in programming this is purely based on selection of programming languages. Java and .Net have a well defined framework for Bluetooth programming and it is reasonably easy to use those APIs. But I planned to do it using raw Windows APIs because this helps in understanding the protocol stack well then using the built-in libraries of high level languages.

But what is significant than coding for Bluetooth is understanding Bluetooth architecture itself, and understanding those procedures to be followed in setting up a Bluetooth server or client. When I started coding for setting up a Bluetooth server, I spent most of the time in understanding these internals, and the major problem is, it was hard for me to find a proper documentation on the same on net. After consulting my colleagues who are working on Bluetooth stack for mobile devices I got a fair idea of the same and could successfully set up Bluetooth server on my PC, so I though of sharing the same with you all.

So let's get your juices going, as we begin..

I hope most of you would have some basic understanding of computer networks and network programming concepts. This is because though Bluetooth architecture was designed from the scratch, it shares lot of similarities with network programming for TCP/IP. No matter whether it is coding for Bluetooth stack or any other network architecture the basic concepts remain the same, as mentioned below.

  •     Selecting a destination device to be connected to, which sometimes involves device discovery.
  •     Agreeing on a protocol to be communicated.
  •     Making an outgoing connection or accepting an incoming connection
  •     Establishing connection for a published service on a  port specified.
  •     Sending and receiving data to be exchanged.

Here is how all the above mentioned steps are achieved in bluetooth context.
Bluetooth Protocol Stack
Bluetooth device addressing
Every Bluetooth chip produced is assigned a unique 48-bit address which is in nature is same as MAC address. This unique address is the means of addressing devices in bluetooth architecture. But the problem here is, it is  quite difficult to deal with this raw 48-bit addresses, hence the architecture allows to have user friendly names as an alias to 48-bit addresses.
Device discovery
The process of searching nearby bluetooh devices is called device discovery, and this process would take as low as 2 seconds to max of 15 seconds. A programmer may need not worry on the internals of this process as it is handled by the hardware itself.

Agreeing on a transport protocol
Once a Bluetooth client could successfully search the device which is hosting Bluetooth server. The next step is that both client application and server application need to agree upon a transport protocol to be used for further communications.
Bluetooth uses two transport protocols as mentioned below.
  • RFCOMM : This protocols provides reliable service as TCP of OSI reference model, hence there is a point to point connection established before the data  transfer. Though RFCOMM shares most of the attributes with TCP, there is a major difference with respect to the number of ports supported. TCP supports 65535 ports but RFCOMM supports only 30 ports.
  • L2CAP : If RFCOMM provides reliable service as TCP, L2CAP supports unreliable service as  like UDP. L2CAP is majorly used in the cases where reliable delivery of packets in not crucial, this helps in avoiding additional overhead of retransmissions and other monitoring requirements of TCP. To put in a nutshell this protocol supports best effort service and the significant advantage here is the level of reliability is configurable. L2CAP can publish services on all the odd numbered ports staring from 1 to 32767.

Issues with fewer number of ports for RFCOMM
In web programming the server applications will be publishing services and receiving incoming connections on the standard well known ports specified at design time for e.g. HTTP runs on port 8080. The issue with this approach is that you can't run two server applications which uses the same port simultaneously. But since due to fact that at a given time there exists large number of unused ports on TCP/IP this has not yet become an issue.

The problem with bluetooth is since only a fewer number of ports are available, it is not a right decision to assign ports at design time, however the ports can't also be assigned dynamically at run time since it can easily result in collision with only 30 ports to be choose from. Bluetooth's solution for this issue is Service Discovery Protocol [SDP].

Service Discovery Protocol
Instead of agreeing upon port numbers at design time as in TCP, Bluetooth uses publish-subscribe model which allows services to be published at run time on a chosen port. The way this is implemented is,
  • Host machine runs a SDP server on the one of the L2CAP ports.
  • All the other server applications will register to this SDP server with description of themselves and the services they provide and gets a port number assigned dynamically.
  • All the client applications trying to connect to a particular service will query SDP server and gets the information about available services and corresponding port numbers.
  • With the server address and port numbers known, client establishes connection with the intended server.
  • The data exchange begins.
Service UUID
The issue with above approach is, how do clients come to know which service that they have to connect to. The solution for this is to assign an unique identifier for each services published. and this is called Service ID or Service UUID, here UUID stands for Universally Unique Identifier.
The UUID of a service is chosen by developer at design time and this is a 128 bit ID. When a client queries SDP for a service, it uses UUID for the particular service to which it is trying to connect to.

Service Class ID
While designing Bluetooth architecture, the designers wanted to distinguish between custom applications with UUID and those classes of applications which all do the same things. To put it in simple terms if two venders provide Bluetooth servers which both provide text exchange service but with different UUID, these applications can be grouped under a unique ID which is a Service Class ID

Finally once the client gets the port number on which the service is pubished, it makes an outgoing connection which is accepted by server and then the data exchange begins.

Here is a simple program written using windows socket APIs  to setup a Bluetooth server on windows
#include "stdafx.h"
#include <WinSock2.h>
#include <ws2bth.h>
#include <bthsdpdef.h>
#include <BluetoothAPIs.h>
using namespace std;

#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "irprops.lib")

int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested = 0x202;
WSADATA m_data;

 if (0 == WSAStartup(wVersionRequested, &m_data))
 {
    SOCKET s = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
    const DWORD lastError = ::GetLastError();

   if (s == INVALID_SOCKET)
   {
     printf("Failed to get bluetooth socket! %s\n",       
     GetLastErrorMessage(lastError));
            exit(1);
   }
   WSAPROTOCOL_INFO protocolInfo;
   int protocolInfoSize = sizeof(protocolInfo);

    if (0 != getsockopt(s, SOL_SOCKET, SO_PROTOCOL_INFO, (char*)&protocolInfo, &protocolInfoSize))
    {
      exit(1);
    }
   SOCKADDR_BTH address;
   address.addressFamily = AF_BTH;
   address.btAddr = 0;
   address.serviceClassId = GUID_NULL;
   address.port = BT_PORT_ANY;
   sockaddr *pAddr = (sockaddr*)&address;

   if (0 != bind(s, pAddr, sizeof(SOCKADDR_BTH)))
   {
      printf("%s\n", GetLastErrorMessage(GetLastError()));
   }
   else
   {
      printf("\nBinding Successful....\n");
      int length = sizeof(SOCKADDR_BTH) ;
      getsockname(s,(sockaddr*)&address,&length);
      wprintf (L"Local Bluetooth device is %04x%08x \nServer channel = %d\n", GET_NAP(address.btAddr), GET_SAP(address.btAddr), address.port);
   }

        int size = sizeof(SOCKADDR_BTH);
        if (0 != getsockname(s, pAddr, &size))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }
        if (0 != listen(s, 10))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }

        WSAQUERYSET service;
        memset(&service, 0, sizeof(service));
        service.dwSize = sizeof(service);
        service.lpszServiceInstanceName = _T("Accelerometer Data...");
        service.lpszComment = _T("Pushing data to PC");

        GUID serviceID = OBEXFileTransferServiceClass_UUID;

        service.lpServiceClassId = &serviceID;
        service.dwNumberOfCsAddrs = 1;
        service.dwNameSpace = NS_BTH;

        CSADDR_INFO csAddr;
        memset(&csAddr, 0, sizeof(csAddr));
        csAddr.LocalAddr.iSockaddrLength = sizeof(SOCKADDR_BTH);
        csAddr.LocalAddr.lpSockaddr = pAddr;
        csAddr.iSocketType = SOCK_STREAM;
        csAddr.iProtocol = BTHPROTO_RFCOMM;
        service.lpcsaBuffer = &csAddr;

        if (0 != WSASetService(&service, RNRSERVICE_REGISTER, 0))
        {
            printf("Service registration failed....");
            printf("%d\n", GetLastErrorMessage(GetLastError()));
        }
        else
        {    
            printf("\nService registration Successful....\n");
        }
        printf("\nBefore accept.........");
        SOCKADDR_BTH sab2;
        int ilen = sizeof(sab2);
        SOCKET s2 = accept (s,(sockaddr*)&sab2, &ilen);
        if (s2 == INVALID_SOCKET)
        {
         wprintf (L"Socket bind, error %d\n", WSAGetLastError ());
        }
        wprintf (L"\nConnection came from %04x%08x to channel %d\n",
        GET_NAP(sab2.btAddr), GET_SAP(sab2.btAddr), sab2.port);
        wprintf (L"\nAfter Accept\n");
 
        char buffer[1024] = {0}; 
        memset(buffer, 0, sizeof(buffer));
        int r = recv(s2,(char*)buffer, sizeof(buffer), 0);
        printf("%s\n",buffer);

         closesocket(s2);
        if (0 != WSASetService(&service, RNRSERVICE_DELETE, 0))
        {
            printf("%s\n", GetLastErrorMessage(GetLastError()));
        }
        closesocket(s);
        WSACleanup();
    }
At this point the server application will be up and running, and you need to write a corresponding client  which connects to this service, I hope in one of my next blog article I'll be writing a corresponding bluetooth client.
That's all for now, comment or mail me if you want to convey your feedback or suggestions on the article.
Happy Coding.. :-) 

Sources : MSDN, Web, Bluetooth.org, Rambling.

3 comments:

  1. Integration of gesture recognition framework with desktop games and utility apps. Demos here.

    http://www.concepttocode.blogspot.in/

    ReplyDelete
    Replies
    1. this program is giving errors when i try to run it on visual c++ 2008

      Delete
  2. This comment has been removed by the author.

    ReplyDelete

ShareThis