El Sitio de los SmartPhones. Todo sobre dispositivos Symbian OS, Series 60, NSeries, ESeries y UIQ. Noticias, reportajes y artículos, configuraciones y foros. Todo lo necesario para poner a punto tu dispositivo.

Programación de aplicaciones cliente/servidor bluetooth en Symbian

Programación de aplicaciones cliente/servidor bluetooth en Symbian

En este tutorial vamos a programar una aplicacion que sea capaz de establecer una conexion a traves de bluetooth, y enviar datos en ambas direcciones. Con este fin se presenta en este tutorial la clase BTManager. Para aquellos que os hayais pegado con los ejemplos de bluetooth que vienen en la SDK habreis visto que si queremos trasladar esas clases a una aplicacion nuestra resulta bastante tedioso, ya que tenemos que añadir una gran cantidad de .cpp y .h a nuestro proyecto, y muchas de ellas no queda bien claro que hacen o con que motivo estan ahi. Pues bien, con la clase que os presento aqui, crear una aplicacion cliente-servidor con bluetooth en nuestras aplicaciones es tan sencillo como incluir en el proyecto dos archivos: BTManager.h y BTManager.cpp. En esta clase encontrareis todo lo necesario para manejar la conexion de una manera intuitiva , es decir, los tipicos metodos: Poner a la escucha el servidor, conectar un cliente, y enviar y recibir datos.
Voy a dividir el tutorial en dos partes. En primer lugar explicare el uso de la clase BTManager , detallando el funcionamiento de cada uno de los miembros publicos de la misma para que podais utilizarla en cualquiera de vuestras aplicaciones.En segundo lugar se explica el funcionamiento de una aplicacion muy simple que utiliza las funcionalidades de esta clase.

1- Uso de la clase BTManager

A la hora de querer usar esta clase en una aplicacion vuestra, lo primero que debereis hacer es linkar las librerias necesarias para que compile bien. Para esto ya sabeis, os vais al .mmp y añadis las siguientes lineas:

LIBRARY bluetooth.lib
LIBRARY btmanclient.lib
LIBRARY BTExtNotifiers.lib
LIBRARY SdpAgent.lib
LIBRARY SdpDatabase.lib
LIBRARY esock.lib

Una vez hecho esto, añadimos a nuestro proyecto los ficheros BTManager.h y BTManager.cpp. Ahora para saber como se usa la clase, echaremos un vistazo a los metodos publicos de BTManager que estan definidos en el .h, y son los siguientes:

BTManager(BTManagerObserver *obs);
~BTManager();
void Listen();
void Connect();
void Send(TBuf8 &data,bool force);
void Close();
bool SendingBusy();

bool AskDevice(TBTDeviceResponseParamsPckg &result);

EBTState state;

Ire comentando cada uno de los metodos y su utilidad.

El primero que nos encontramos es el constructor de la clase.Vemos que como parametro toma un puntero a un objeto de tipo BTManagerObserver, si os habeis pegado con los controles de Avkon ya sabreis mas o menos que quiere decir esto de una clase Observer.Basicamente la clase que intente comunicarse con el BTManager (recibir notificaciones cuando llegue un paquete, o cuando se haya creado una conexion, etc) debe heredar de la clase virtual pura BTManagerObserver y sobrecargar los metodos de la misma para poder recibir estas notificaciones.Esta clase tambien se encuentra en BTManager.h y tiene el siguiente aspecto:

class BTManagerObserver{
public:
virtual void BTIncomingData(TBuf8 &data) = 0;
virtual void BTConnRequest() = 0;
virtual void BTConnAcepted() = 0;
};

Como veis tiene tres metodos que son bastante explicativos de para que van a servir.El metodo BTIncomingData sera llamado cuando lleguen datos a traves de la conexion; BTConnRequest sera llamado cuando a una aplicacion que este actuando como servidor se le haya conectado un cliente; y por ultimo BTConnAcepted sera llamado cuando a una aplicacion que este actuando como cliente y haya efectuado una peticion de conexion se le haya aceptado esa peticion.De esta manera, nuestra aplicacion tendria una forma similar a esto:

class MyApp : public BTManagerObserver {
public:
//Constructor
MyApp();

void BTIncomingData(TBuf8 &data);
void BTConnRequest();
void BTConnAcepted();

//Resto de metodos o atributos de la clase
//....
//....
private:
BTManager *btManager;
};

Y en su implementacion:

MyApp::MyApp(){
//Construimos el objeto BTManager , y le pasamos nuestro puntero por parametro para poder "observar
//las notificaciones que lleguen.

btManager = new BTManager(this);

}

void MyApp::BTIncomingData(TBuf8 &data){
//Acaban de llegar datos, los cuales nos lleguan a traves del parametro data
//Procesarlos o hacer las tareas pertinentes
}

void MyApp::BTConnRequest(){
//Nuestra aplicacion estaba actuando como servidor y acabamos de recibir una conexion.
//Ya podriamos empezar a enviar datos si quisieramos ya que ya hay creado un canal de conexion.
}

void MyApp::BTConnAcepted(){
//Nuestra aplicacion estaba actuando como servidor y nuestra peticion de conexion
//ha sido aceptada.Ya podemos empezar a enviar datos si queremos.
}

Y asi tendriamos implementada la parte de la creacion del objeto BTManager, y de la comunicacion de los eventos entre esta y nuestra aplicacion.Ahora vamos a retomar como funciona cada uno de los metodos de BTManager.

El constructor ya lo hemos visto, asique pasemos al siguiente metodo, que es el metodo Listen().Este metodo lo utilizan las aplicaciones que quieran actuar como servidor.Es decir , en el momento que yo quiera poder recibir una conexion, simplemente llamo al metodo Listen, y nuestra aplicacion ya estara lista para recibir una conexion. Como vimos antes, nos enteraremos de que alguien se nos ha conectado a traves del metodo BTConnRequest.

Con el metodo Connect realizamos la otra parte, es decir realizariamos la peticion de conexion a una aplicacion.Cuando invoquemos este metodo, nos saldra por pantalla el tipico cuadro de dialogo preguntandonos si queremos buscar mas dispositivos o elegir uno de los que tengamos en la lista.Una vez seleccionemos el terminal al que queremos conectarnos , la clase BTManager iniciara todo el proceso de establecimiento de conexion, que no es algo inmediato. Nos enteraremos de cuando ha sido establecida la conexion a traves del metodo BTConnAcepted.

El metodo Send, solo tiene efecto cuando ya se ha establecido una conexion con Listen o con Connect. Como su propio nombre indica , envida datos hacia el otro lado de la conexion. El primero parametro que toma es un TBuf8 donde debe ir codificada byte a byte la informacion que queremos enviar.Por ejemplo, si quisieramos mandar un unsigned short (2bytes) a traves de este metodo, deberiamos hacer lo siguiente:

TBuf8 data;
unsigned short ushort = 90;
data.Append((unsigned char*)&ushort,2);
btManager->Send(data,false);

El segundo parametro tiene un significado un poco especial.Si realizamos dos llamadas a Send muy seguidas (con unos pocos milisegundos de separacion) es posible que cuando ejecutemos la segunda llamada, la primera no haya podido ser llevada a cabo todavia.Con este parametro podemos especificar la manera de actuar en estos casos: Si realizamos una llamada a Send con el segundo parametro a true, y el BTManager esta ocupado aun realizando algun otro envio anterior, este primer envio es eliminado y nuestra llamada tendra prioridad sobre la anterior.Sin embargo si el parametro es false y el BTManager esta ocupado con un envio anterior, nuestro envio sera rechazado y deberemos hacerlo un poco mas tarde (muy poco).

Para comprobar la situacion explicada anteriormente, es decir, si el BTManager esta ocupado con un envio en ese instante, tenemos el metodo SendingBusy, que nos devolvera true si esta ocupado, o false en caso contrario.

Esta situacion de ocupacion del BTManager es bastante raro que se de, pero tuve que tenerla en cuenta para evitar problemas con los Active Objects.Como es muy raro que se de, podeis limitaros a hacer la llamada a Send con el segundo parametro siempre a false como en el ejemplo anterior.

El metodo Close nos permite cerrar una conexion que tengamos activa.

A traves del metodo AskDevice podemos invocar manualmente al cuadro de dialogo que nos busca los dispositivos bluetooth en alcance.Este metodo en principio estaba privado, pero bueno, en algun caso es posible que alguien quisiera utilizar la clase para saber que dispositivos bluetooth estan a su alrededor y obtener el nombre o la direccion bluetooth.

Por ultimo, el atributo publico state nos permite saber en que estado se encuentra en un momento dado nuestro BTManager. Este atributo es del tipo EBTState, definido en BTManager.h del siguiente modo:

enum EBTState{
eDisconnected,
eListening,
eConnecting,
eConnected,
};

Creo que los valores de este tipo enumerado son suficientemente concisos como para saber que significa cada uno de ellos , asi que no voy a profundizar mas en ello.

Ya hemos visto todo el funcionamiento de la clase BTManager, con la cual cualquiera deberia ser capaz de crear una aplicacion bluetooth cliente-servidor y enviar datos entre una y otra.Cualquier persona que no quiera quedarse en la parte superficial, y quiera saber mas sobre la implementacion puede echar un vistazo al archivo BTManager.cpp, ya que me ha parecido mas oportuno comentar un poco el codigo que andar haciendo aqui corta-pega de todo el codigo ya que seria bastante engorrosos , y creo que es mas comodo leerlo directamente ahi.He intentado comentar las partes mas importantes, espero que os sirva de ayuda.

2- TutoBT : Una aplicacion usando BTManager

En los archivos adjuntos a este tutorial encontrareis Tutobt.zip , que es un ejemplo de una aplicacion que usa la clase BTManager. En la carpeta de archivos fuente de esta aplicacion es donde encontrareis los archivos BTManager.h y BTManager.cpp.

El funcionamiento de la aplicacion es muy sencillo. En la pantalla aparecen dos bolitas, nosotros con nuestro movil podemos mover una de ellas y la otra en principio permanece inmovil.Si cargamos la aplicacion en dos moviles, y uno de ellos lo ponemos a la escucha y en otro mandamos conectar al movil que esta la escucha, cada uno de los moviles manejara una de las bolitas.


Como espero que la explicacion de antes os halla servidor para interpretar este codigo, simplemente voy a comentar los aspectos mas relaccionados con la clase BTManager.

Como podeis observar, la clase encargada de manejar el BTManager hereda de BTManagerObserver:

class CTutoBTContainer : public CCoeControl,BTManagerObserver {

Cuando se quiere poner a la escucha o realizar una conexion es tan sencillo como llamar al Listen o Connect de BTManager:

void CTutoBTContainer::Listen(){
btManager->Listen();
}

void CTutoBTContainer::Connect(){
btManager->Connect();
}

En este caso , como es algo bastante trivial , solo se intercambian datos cuando uno de los moviles pulsa una tecla ya que provoca un cambio en el sprite que el maneja. Es por eso que en el metodo de manejo de teclas esta el siguiente fragmento:

if(btManager->state == eConnected){
//Si hay conexion mandar nueva posicion
TBuf8 data;
data.Append((unsigned char *)&x1,2);
data.Append((unsigned char *)&y1,2);
btManager->Send(data,false);
}


Es decir, que si existe ya una conexion creada ( no tiene por que haberla) mandamos nuestra nueva posicion (x1,y1) a traves del BTManager.

Se puede ver como en el BTIncomingData , cuando llegan datos de la posicion del otro terminal se actualizan la X y la Y correspondientes para reflejar el cambio:

if(server){
Mem::Copy(&x2,data.Ptr(),2);
Mem::Copy(&y2,data.Ptr()+2,2);
}else{
if(!initReceived){
Mem::Copy(&x2,data.Ptr(),2);
Mem::Copy(&y2,data.Ptr()+2,2);
Mem::Copy(&x1,data.Ptr()+4,2);
Mem::Copy(&y1,data.Ptr()+6,2);
initReceived = true;
}else{
Mem::Copy(&x2,data.Ptr(),2);
Mem::Copy(&y2,data.Ptr()+2,2);
}
}

Hay que hacer una disticion de si es la primera que se reciben datos (de ahi la variable initReceived) ya que si es la primera vez que el cliente recibe datos, no solo recibe la posicion de la bolita del servidor, si no que ademas el servidor le dice donde tiene que posicionar inicialmente la suya.Esto es para que una vez se inicie la conexion esten sincronizados.

Esta parte se puede observar bien en el metodo BTConnRequest , que es donde el servidor recibe la confirmacion de que ha recibido la conexion , y le manda las coordenadas de ambas bolas al cliente:

void CTutoBTContainer::BTConnRequest(){
//Se nos ha conectado alguien

//Para saber que somos el servidor de esta conexion
server = true;

TBuf8 data;

data.Append((unsigned char *)&x1,2);
data.Append((unsigned char *)&y1,2);
data.Append((unsigned char *)&x2,2);
data.Append((unsigned char *)&y2,2);
btManager->Send(data,false);
}

Un ultimo apunte, cuando vayais a crear el .sis a partir del .pkg, debereis modificarlo con las rutas de donde tengais vosotros instalada la SDK porque si no no os va a compilar.

Aquí podéis descargaros los archivos relacionados con este tuorial, y espero que os sirva de algo en vuestros proyectos, a mí me es de bastante utilidad. Desde luego si creéis que hay alguna manera mejor de hacerlo no dudéis en decirmelo. Dudas, opiniones, sugerencias, y por supuesto criticas a gdesantos@bigfoot.com. También me podéis encontrar a mí y a otros desarrolladores de Symbian en el canal #symbian de irc-hispano.

Autor:
Gustavo de Santos García
OverMind

Si has encontrado algo incorrecto o con lo que no estás de acuerdo puedes ponerte en contacto con el autor o con Todo Symbian. Tu contribución será bienvenida.

Opina en el foro sobre este reportaje.

¿Te ha sido de utilidad? Vota por Todo Symbian en GSMspain TOPSites, tan sólo te tomará un minuto.


[ Volver Atrás ]

Todo Symbian. El Sitio de los SmartPhones.