desarrolloMobile.NET Noticias

domingo, abril 26, 2009

Bluetooth en .NET [Compact] Framework

Hará aproximadamente tres años empecé a desarrollar una libreria en .NET para la gestión de Bluetooth en Windows Mobile a raíz de un artículo que se publicó en dotNetMania num 35 (Marzo 2007).

Durante el tiempo que estuve desarrollando dicha libreria, pude apreciar realmente la dificultad que entraña un desarrollo sobre dicha tecnologia. De hecho, la libreria para Windows Mobile no pude finalizarla –se quedó con algunos Bugs- y la de Windows XP/Vista (unicamente 32 bits) logré que permitiera gestionar la radio Bluetooth, buscar dispositivos cercanos, emparejarse y comunicarse via COM mediante el servicio RFCOMM; quise expandir la libreria mediante el uso de la capa SDP (Service Discovery Protocol) de búsqueda y uso de servicios específicos a través del API de Bthioctl.dll de Windows Vista, pero ahí se quedó por falta de tiempo.

Desde que el material está expuesto en desarrolloMobile.NET, recibo muy a menudo preguntas o curiosidades en mi buzón de correo personal de las que, pese a que algunas desconozco, la mayoria hacen referencia a cómo empezar y de ahí el motivo de este post, para todos aquellos que quieran saber por dónde pueden empezar a implementar soluciones basadas en Bluetooth en sus proyectos.

Aspectos básicos

Incialmente, tanto Windows Mobile como Windows XP/Vista nos ofrece librerias nativas para poder inicializar y obtener la información de la radio bluetooth local y algunas de esas APIs las podemos ver en el siguiente código y cuyas referencias las encontraréis aqui.

   1: public enum BthEstadosRadio : int


   2: { Desactivado, Activado, Detectable };


   3:  


   4: public class BTHRadio : IDisposable


   5: {


   6:     [DllImport("bthutil.dll", SetLastError = true)]


   7:     private static extern int BthGetMode(out BthEstadosRadio mode);


   8:     [DllImport("bthutil.dll", SetLastError = true)]


   9:     private static extern int BthSetMode(BthEstadosRadio mode);


  10:     [DllImport("Btdrt.dll", SetLastError = true)]


  11:     private static extern int BthReadLocalVersion(


  12:     ref byte phci_version, ref ushort phci_revision,


  13:     ref byte plmp_version, ref ushort plmp_subversion,


  14:     ref ushort pmanufacturer, ref byte plmp_features);


  15:     [DllImport("Btdrt.dll", SetLastError = true)]


  16:     private static extern int BthReadCOD(ref CategoriaDispositivo pcod);


  17:     [DllImport("Ws2.dll", SetLastError = true)]


  18:     public static extern int WSACleanup();


  19:     [DllImport("Ws2.dll", SetLastError = true)]


  20:     public static extern int WSAStartup(ushort version, byte[] wsaData);


  21:     //los enumeradores Fabricantes y CategoriaDispositivo son


  22:     //identificadores únicos (http://www.bluetooth.org)


  23:     private Fabricantes _fabricante;


  24:     private short _versionRadio;


  25:     private CategoriaDispositivo _claseDisp;


  26:     //constructor


  27:     public BTHRadio()


  28:     { // inicializamos sockets


  29:         ushort wsv =


  30:         ((ushort)(((byte)(2))  ((ushort)((byte)(2))) << 8));


  31:         byte[] _data = new byte[512];


  32:         WSAStartup(wsv, _data);


  33:         //obt. versión y fabricante


  34:         byte version = 0, lversion = 0, caract = 0;


  35:         ushort revision = 0, lsubrevision = 0, fab = 0;


  36:         BthReadLocalVersion(ref version, ref revision,


  37:         ref lversion, ref lsubrevision, ref fab, ref caract);


  38:         this._fabricante = fab;


  39:         this._versionRadio = (short)lsubrevision;


  40:         //obtenemos categoría disp.


  41:         BthReadCOD(ref this._claseDisp);


  42:     }


  43:     //propiedad lectura/escritura del estado de radio


  44:     public BthEstadosRadio EstadoRadio


  45:     {


  46:         get


  47:         {


  48:             BthEstadosRadio currentMode;


  49:             BthGetMode(out currentMode);


  50:             return currentMode;


  51:         }


  52:         set


  53:         { BthSetMode(currentMode); }


  54:     }


  55:     //liberamos WS2.dll


  56:     public void Dispose()


  57:     { WSACleanup(); }


  58: }





Con esta clase podemos empezar a gestionar nuestro Bluetooth (siempre y cuando se MS Bluetooth Stack) pero en realidad nos ofrecerá muy poca funcionalidad al no ser que la finalidad de la aplicación sea mostrado el estado e información de la radio. La información, en definitiva, que muestra este código es la que podemos ver en la siguiente captura de pantalla:



Inforadio



Aspectos avanzados



Sin embargo, el uso de esta tecnología en nuestros desarrollos requiere unos requisitos previos. En primer lugar es altamente recomendable que se entienda el funcionamiento de Bluetooth. A grandes rasgos, remarcaría dos conceptos muy distintos.



PRIMERO: Debemos conocer el perfil sobre el queremos trabajar. Bluetooth es un protocolo y com tal gestiona el intercambio de mensajes en base a un estándard desarrollado con el fin de comunicar dos dispositivos. No haremos “nada” sólo con Bluetooth, en todo caso sobre Bluetooth y lo haremos a través de los perfiles los cuales definen el comportamiento de los dispositivos y la comunicación. No es lo mismo que una aplicación haga uso de Bluetooth para la gestión de un manos libres que para el intercambio de archivos entre un dispositivo Windows Mobile y Windows Vista, por ejemplo. Los perfiles definen el cómo se lleva a cabo la comunicación. La comunicación con un manos libres utiliza un perfil (HFP), y el intercambio de objectos (archivos, contactos y demás) utiliza otro (OBEX).especificacion



SEGUNDO: En cuanto a la conexión entre dispositivos recordad que el proceso que normalmente realizamos para el emparejamiento manual se debe hacer programaticamente, con todo lo que ello conlleva. Para conectarnos a un dispositivos necesitamos saber la dirección del dispositivo y el Guid del servicio al que queremos conectarnos. El proceso de emparejamiento podría ser algo así:





   1: public class BTHDispositivo


   2: {


   3:     [DllImport("Btdrt.dll", SetLastError = true)]


   4:     private static extern int BthCreateACLConnection(byte[]


   5:     remoteBthAdd,


   6:     ref ushort phandle);


   7:  


   8:     [DllImport("Btdrt.dll", SetLastError = true)]


   9:     private static extern int BthCloseConnection(ushort phandle);


  10:  


  11:     [DllImport("Btdrt.dll", SetLastError = true)]


  12:     private static extern int BthAuthenticate(byte[] remoteBthAdd);


  13:  


  14:     [DllImport("btdrt.dll", SetLastError = true)]


  15:     private static extern int BthSetPIN(byte[] localBthAdd,


  16:     int pinLength, byte[] ppin);


  17:  


  18:     public string _nombre;


  19:     public byte[] _direccion;


  20:  


  21:     //pin.Length => 0 y pin.Length <= 16


  22:     public void EstablecerPIN(string pin)


  23:     {


  24:         byte[] pinbytes = System.Text.Encoding.ASCII.GetBytes(pin);


  25:         int len = pin.Length;


  26:         BthSetPIN(_direccion, len, pinbytes);


  27:     }


  28:     public bool QuitarPin()


  29:     {


  30:         BthRevokePIN(_direccion);


  31:     }


  32:  


  33:     public void Emparejar(string pin)


  34:     {


  35:         ushort handle = 0;


  36:         //conectamos


  37:         BthCreateACLConnection(_direccion, ref handle);


  38:         //autenticamos


  39:         BthAuthenticate(_direccion);


  40:         //desconectamos


  41:         BthCloseConnection(handle);


  42:     }


  43: }





Una vez emparejado toca conectar mediante un servicio común entre ellos cuya conexión se puede realizar mediante Sockets a través de un EndPoint que exponga el servicio o perfil a utilizar (SOCKADDR_BTH) tal y como muestra el siguiente enlace. En modo de ejemplo a continuación se muestra el método de conexión de la libreria de desarrolloMobile.NET para WinXP.





   1: public void Conectar(Servicios.BthServicio servicio)


   2: {


   3:     clienteSck  = new BthSocket();


   4:     


   5:     BthEndPoint endPoint = new BthEndPoint(this, servicio);


   6:  


   7:     try


   8:     {


   9:         clienteSck.Connect(endPoint);


  10:     }


  11:     catch (SocketException se)


  12:     {


  13:         throw;


  14:     }


  15:     catch (Exception ex)


  16:     {


  17:         throw;


  18:     }


  19: }





¿Ahora bien, cómo averiguamos la dirección y el Guid del Servicio?



En cuanto a los servicios o perfiles, todos y cada uno tienen un Guid asociado que es universal (estandarizado). En sitio oficial de Bluetooth podeis encontrar una lista completa (NOTA: Debéis registraros).



En cuanto a la dirección del dispositivo a conectar hay que saber lo siguiente. Hablando desde el punto de vista programático las únicas formas de conocer la dirección destino es a través del descubrimiento de dispositivos cercanos o consultando los dispositivos ya emparejados (previo descubrimiento). Es aqui, en este punto, dónde la mayoria de desarrollos quedan atascados, puesto que el código de desubrimiento es complejo y se lleva a cabo a través de las clases WSALookUpServiceBegin, WSALookUpServiceNext y WSALookUpServiceEnd y cuya forma de proceder la podéis encontrar aquí. Siempre podemos conocer la dirección del dispositivo emparejandolo manualmente y por norma general, MS Bluetooth Stack almacena la dirección de todos los dispositivos previamente emparejados en el registro.



Microsoft tiene un ejemplo de uso para Bluetooth que podéis encontrar aquí. En este ejemplo, además, podéis ver el método de descubrimiento de servicios dado un dispositivo. Además muestra en la clave dónde se almacenan los dispositivos emparejados o de confianza.



Debug



Otros de los quebraderos de cabeza fue el Debug del proyecto. Si éste se realiza sobre una aplicación Windows Desktop no hay nada que decir pero si se realiza sobre Windows Mobile, entonces el emulador de poco me servía. Debía emparejar un dispositivo físico, realizar el despliegue sobre él a través de ActiveSync y depurar la aplicación sobre la misma.



Sin embargo, hace poco me topé con un artículo de codeproject que permitia emular la radio Bluetooth sobre un emulador Windows Mobile. El autor es



Dmitry Klionsky, y el enlace lo podéis encontrar aquí.





Recomendaciones




  • Utiliza, en la medida de lo posible, componentes de gestión de pilas Bluetooth de terceros. No voy a decir ningún proveedor puesto que no he utilizado ninguno en especial. Pero el desarrollo de una libreria o componente propio debería ser la última alternativa.


  • Conoce la pila de tu dispositivo Bluetooth(http://en.wikipedia.org/wiki/Bluetooth_stack). Las mas comunes son Microsoft Bluetooth Stack y Widcomm y la mayoria de componentes de terceros ofrecen servicios para como mínimo ambas pilas. Recuerda que una libreria para Bluetooth desarrollada para Microsoft Bluetooth stack NO es compatible con Widcomm y viceversa. De hecho las librerias que encontrarás en desarrolloMobile.NET no son válidas para dispositivos Widcomm y demás. Encontraras un enlace con el tipo de pilas utilizas por dispositivos (modelo/marca) aqui.


  • Si te apatece profundizar sobre el tema, en desarrolloMobile.NET (sección Bluetooth) encontrarás más recursos. Otros de los sitios que no puedes dejar de visitar es el proyecto 32feet.net.

No hay comentarios: