Ir al contenido principal

Administración de módulos GSM en sistemas reactivos

Generalmente, un módulo GSM se administra por medio de comandos AT, lo cual implica enviar un comando y esperar la recepción de una respuesta. Hasta aquí, el esquema obedece a un simple modelo del tipo cliente-servidor, en el cual el módulo externo o dispositivo se comporta como servidor de comandos, procesando las solicitudes y enviando en consecuencia el resultado obtenido al cliente conectado. En este caso, el cliente es aquel que envía el comando y espera indefectiblemente una respuesta como resultado. De forma tal, que una próxima solicitud deberá esperar la respuesta del comando previo, ya que los módulos GSM tradicionales no permiten recibir un comando mientras se encuentre en procesamiento, por lo tanto, desde el punto de vista del cliente, la respuesta implica que el módulo está nuevamente listo para recibir comandos. Si así no fuera, puede que el comando se descarte o bien el procesamiento en curso se aborte. Otra de las problemáticas surge con las respuestas no solicitadas o URC (Unsolicited Result Code), las cuales las envía el módulo externo ante la ocurrencia de un evento determinado, sin necesidad de comandos previos.
Cabe aclarar, que lo expuesto se aplica a cualquier tipo de módulo que utiliza comandos AT, ya sea GSM, GPS, dial-up, etc.



Solución propuesta

El software de una aplicación tradicional que administra un módulo externo manejado por comandos AT (mmgr or module manager), debería ser tal que:
  • Envíe comandos AT, y en función de estos espere un conjunto de respuestas posibles.
  • Notifique la recepción de respuestas no solicitadas.
  • Durante la espera de respuesta no se apropie de la CPU.
  • Notifique si no recibe respuesta alguna ante un comando enviado.
  • Secuencie el envío de comandos, al ritmo que imponga el módulo en cuestión. 
  • Incorpore las temporizaciones requeridas por el módulo relacionadas con los comandos.
Para cumplir con las premisas expuestas de manera clara y consistente, se propone utilizar un modelo que utilice parte del concepto del modelo cliente-servidor, en donde el mmgr, actúa como servidor, encapsulando el comportamiento específico del dispositivo, mientras que el resto de la aplicación adopta el rol de cliente, enviando comandos y esperando resultados (respuestas). Este modelo permite la conexión simúltanea de más de un cliente .
La Fig. 1 muestra el diagrama del modelo propuesto, presentando los actores que intervienen y la relación entre estos.

Figura 1 - Diagrama cliente-servidor de comandos

De la figura anterior se observa que:
  1. No existe conexión lógica entre el dispositivo y los clientes que requieren enviar comandos. El vínculo entre estos es mmgr. Esto permite encapsular el comportamiento del módulo, y restringir así el acceso concurrente al mismo.
  2. Cuando un cliente, en este caso clientA, requiere enviar un comando al módulo, lo hace enviando el evento apropiado a mmgr.
  3. El comando representado por la cadena de caracteres lo envía exclusivamente mmgr al módulo externo.
  4. El mmgr posee una cola de comandos, la cual permite recibir nuevos comandos aún cuando el módulo externo se encuentre en procesamiento. Además, evita bloquear la ejecución de aquellos clientes que esperan respuestas y restringe el acceso concurrente al dispositivo. Hasta aquí, la solución se basa en el patrón de diseño objeto activo[1].
  5. Cada comando que reside en la cola del mmgr posee un cliente asociado.
  6. La ISR envía al mmgr una notificación indicando la recepción de la respuesta. La misma se recibe caracter por caracter desde ISR (generalmente UART).
  7. La notificación de la respuesta se redirige al cliente correspondiente.

Aplicando el paradigma de la programación dirigida por eventos

La solución que aplica el artículo se basa en los principios de la programación dirigida por eventos [1,2,4], la cual permite la construcción de software claro, flexible,  y robusto. Esto implica aplicar los conceptos de máquina de estados y objetos activos para diseñar el software, mientras que la implementación se basará en el framework RKH. El comportamiento de mmgr se representa mediante el diagrama de estados de la Fig. 2, implementando las acciones en pseudo-código.

Figura 2 - Máquina de estados mmgr

en donde:

   (1-2) Inicializa el temporizador tcmd y la cola de comandos diferidos qcdef. Aún cuando mmgr espera una respuesta del módulo o simplemente se encuentra ocupado, puede recibir nuevos comandos, esto se logra diferiéndolos para ser enviados más tarde, precisamente cuando mmgr vuelva a su estado de reposo idle. La constante MMGR_SIZEOF_QCDEF define la cantidad de comandos que pueden diferirse mientras mmgr no se encuentre disponible.
   (3) En estado de reposo recibe una solicitud para enviar un comando, las solicitudes son eventos con parámetros, estos determinan: la cadena de caracteres que representa el comando AT, por ejemplo: "at+i0", el objeto activo (cliente) que envía el comando (el cual recibirá la respuesta), el tiempo de espera de respuesta, el retardo luego de enviado el comando, una cadena de caracteres que representa los argumentos del comando AT y su longitud.
   (4) Dado que el evento tiene parámetros asociados y estos son variables se utiliza el concepto de "eventos dinámico", con lo cual para que RKH [4] no libere automáticamente la memoria ocupada, se le indica que el evento "está en reserva".
   (5) El evento se mantiene referenciado localmente.
   (6) Envía el comando.
   (7) Dispara el temporizador tcmd con el tiempo de respuesta máximo.
   (8-9) En caso de no recibir una respuesta válida desde el módulo externo, tcmd alcanza su cuenta final y por lo tanto, el objeto activo cliente es notificado.
   (10) Las transiciones desde los estados in_progress y wintercmd hacia idle, requiere que se "recuerde" el más antiguo de los comandos diferidos para ser procesado.
   (11) El evento que representa el comando actual ya no es utilizado, con lo cual se le indica a RKH [4] que "recicle" el evento, con lo cual la memoria ocupada queda disponible para otro uso.
   (12) Suponiendo que el módulo se conecta mediante una UART; cuando la ISR de recepción determina que una cadena de respuesta válida se encuentra disponible lo notifica a mmgr mediante el evento MRESP, el cual posee un parámetro asociado fwde. Este permite independizar a mmgr de los comandos y respuestas específicas del módulo externo, profundizando aún más el desacoplamiento de mmgr del resto de la aplicación.
   (13) Detiene la cuenta del temporizador tcmd, ya que ha sido recibida una respuesta válida.
   (14) De acuerdo a (12) el evento es redirigido al objeto activo cliente correspondiente.
   (15-16) El pseudo-estado condicional tiene una condición que debe ser evaluada, la cual determina si el comando enviado requiere agregar un retardo al posible próximo comando. Si es así, el estado final de la transición será wintercmd (wait inter-command time), de lo contrario,
   (17-19) se transita al estado de reposo ejecutando (10) y (11).
   (20-22) Si el comando requiere retardo inter-comando, una vez alcanzada la cuenta final del temporizador ticmd se transita al estado de resposo ejecutando (10) y (11).
   (23) Como bien se enfatizó en los ítems anteriores, si mmgr recibe una solicitud de nuevo comando, estando en progreso (estado in_progress) o esperando el retardo inter-comando (estado wintercmd) este es diferido momentáneamente.
   (24) Si en cualquiera de los estados de mmgr se recibe un URC, este es redirigido al objeto activo lnmgr, siendo este responsable de su tratamiento.

Envío de comandos

Utilizando el framework RKH [4] y de acuerdo con el diagrama de la Fig. 2, se muestra a continuación la definición y forma del evento para el envío de comandos.

typedef struct
{
 RKHEVT_T e;
 const char *cmd;   /** pointer to AT command string */
 RKHSMA_T *aodest;  /** pointer to AO client */
 RKH_TNT_T ntick;   /** timout waiting for modem response */
 RKH_TNT_T delay;   /** intercommand delay */
 rkhui8_t *data;    /** pointer to associated data of AT command */
 rkhui8_t ndata;    /** length of associated data of AT command */
} MCMD_EVT_T;
Listing 1 - Evento para comandos

Ejemplos sencillos en la preparación del comando a enviar a mmgr.

static char cmdstr[ MMGR_MAX_SIZEOF_CMDSTR ]; /* AT command string */

/* cmd = AT */
e_cmd.cmd = "AT";
e_cmd.aodest = clientA;  /* target AO */
e_cmd.ntick = 500;   /* [in ms] */
e_cmd.delay = 0;   /* without delay time */

/* cmd = ATD */
e_cmd.cmd = cmdstr;
sprintf( cmdstr, "ATDT,%s;", dstnr );
e_cmd.aodest = clientA;
e_cmd.ntick = 10000;
e_cmd.delay = 0;

/* cmd = AT:R */
e_cmd.cmd = cmdstr;
sprintf( cmdstr, "AT:R%X", reg );
e_cmd.aodest = clientA;
e_cmd.ntick = 500;
e_cmd.delay = 0;

/* cmd = AT:U, */
e_cmd.cmd = cmdstr;
sprintf( cmdstr, "AT:U%X,%X\r\n", reg, value );
e_cmd.aodest = clientA;
e_cmd.ntick = 500;
e_cmd.delay = 0;

/* cmd = AT+FTM=201 */
e_cmd.cmd = "AT+FTM=201";
e_cmd.aodest = clientA;
e_cmd.ntick = 5000;
e_cmd.delay = 0;
e_cmd.data = frame.data; /* associated data with FTM command */
e_cmd.ndata = frame.ndata;

/* cmd = AT+FRM=200 */
e_cmd.cmd = "AT+FRM=200";
e_cmd.aodest = clientA;
e_cmd.ntick = 20000;
e_cmd.delay = 0;
Listing 2 - Comandos

De acuerdo con los ejemplos anteriores, podría generalizarse el envío de comandos centralizándolos en una tabla.

/* Beautifiers macros */
#define MDM_CMD_TBL( name ) \
    const MCMD_ST name[] = {

#define MDM_CMD( cmd, tout, delay ) \
    { (cmd), RKH_TIM_MS(tout), RKH_TIM_MS(delay) },

#define MDM_CMD_ENDTBL \
    { NULL, 0, 0 } }


/* Type of command table */

typedef struct
{
 const char *cmd;
 RKH_TNT_T ntick;
 RKH_TNT_T delay;
} MCMD_T;

/* Command table entries */

enum
{
 MCMD_SYNC_IX,
 MCMD_RREG_IX,
 MCMD_DIAL_IX,
 MCMD_ESC_IX,
 MCMD_ANSWER_IX,
 MCMD_HANGUP_IX,
 MCMD_RXFRM_IX,
 MCMD_TXFRM_IX,
 MCMD_GEN_DTMF_IX,
 NUM_MDM_CMDS
} CMD_IXS;

/* AT command table */

MDM_CMD_TBL( si2493_cmds )
 MDM_CMD( "AT",    500, 0 )
 MDM_CMD( "AT:R%X",    500, 0 )
 MDM_CMD( "ATDT,%s;",  10000, 0 )
 MDM_CMD( "+++",    500, 0 )
 MDM_CMD( "ATDT;",   1000, 0 )
 MDM_CMD( "ATH0",   1000, 200 )
 MDM_CMD( "AT+FRM=200",  20000, 0 )
 MDM_CMD( "AT+FTM=201",  5000, 0 )
 MDM_CMD( "AT+VTS=[%s,20]", 2000, 0 )
MDM_CMD_ENDTBL;

/* Useful macros */

#define MGET_CMD_STR( ix )  sim900_cmds[(ix)].cmd
#define MGET_CMD_NTICK( ix ) sim900_cmds[(ix)].ntick
#define MGET_CMD_DELAY( ix ) sim900_cmds[(ix)].delay
Listing 3 - Tabla de comandos

Ahora, se preparan y envían los comandos por medio de la siguiente función.
void
mmgr_send_cmd( rkhui8_t cmd_ix, void *arg )
{
 e_cmd = RKH_ALLOC_EVENT( MCMD_EVT_T, MCMD );
 e_cmd->aodest = clientA;
 if( arg != NULL )
 {
  sprintf( cmdstr, MGET_CMD_STR(cmd_ix), arg );
  e_cmd->cmd = cmdstr;
 }
 else
  e_cmd->cmd = MGET_CMD_STR((cmd_ix));

 e_cmd->ntick = MGET_CMD_NTICK((cmd_ix));
 e_cmd->delay = MGET_CMD_DELAY((cmd_ix));
 rkh_sma_post_fifo( mmgr, CE(e_cmd) );
}
Listing 4 - Generalización de envío de comandos

Uso.
#define MMGR_DIAL( num ) \
   mmgr_send_cmd( MCMD_DIAL_IX, (void*)(num) )

void
open_session( RKHEVT_T *pe )
{
 rkh_tim_stop( &tlnmgr );
 MMGR_DIAL( COS(pe)->pn );
}
Listing 5 - Envío de comandos

Recepción de respuesta

Nuevamnte, utilizando el framework RKH [4] y de acuerdo con el diagrama de la Fig. 2, se muestra a continuación la definición y forma del evento para la notificación de recepción de respuesta válida desde ISR.

typedef struct
{
   RKHEVT_T e;
   RKHE_T fwde;   /** signal for forwarding the arrived event */
} MRESP_T;
Listing 6 - Evento para respuestas

Ejemplos sencillos en la preparación y envío de eventos de notificación de respuesta.

/* Declare an event as static one */
#define MMGR_DCLR_MR_STATIC_EVENT( e, s, fs ) \
           MRESP_T e = { {(s),0,0}, (fs) }

/* received response = OK */
static MMGR_DCLR_MR_STATIC_EVENT( e_ok, MRESP, MR_OK );
rkh_sma_post_fifo( mmgr, CE( &e_ok ) );

/* received response = ERROR */
static MMGR_DCLR_MR_STATIC_EVENT( e_error, MRESP, MR_ERROR );
rkh_sma_post_fifo( mmgr, CE( &e_error ) );

/* received URC = RING */
static MMGR_DCLR_MR_STATIC_EVENT( e_ring, MRESP, MR_RING );
rkh_sma_post_fifo( mmgr, CE( &e_ring ) );

Listing 7 - Respuestas estáticas sin datos asociados

Ahora, preparando y enviando respuestas con datos asociados.

/* Useful macro */
#define CMRESP( p )    (MRESP_T*)(p)

/*
 *  Response to AT:R, receives a 16-bit value.
 */

typedef struct
{
   MRESP_T e;
   rkhui16_t value;
} MR_REG_EVT_T;

e_rdr = RKH_ALLOC_EVENT( MR_REG_EVT_T, MRESP );  /* dynamic event */
CMRESP( e_rdr )->fwde = MR_RD_REG;
rkh_sma_post_fifo( mmgr, CE( e_rdr ) );

/* 
 *  Response to ATI0, receives a string.
 */

typedef struct
{
   MRESP_T e;
   char id[ MMGR_MAX_SIZEOF_ID ];
} MR_ID_EVT_T;

e_id = RKH_ALLOC_EVENT( MR_ID_EVT_T, MRESP );
CMRESP( e_id )->fwde = MR_ID;
rkh_sma_post_fifo( mmgr, CE( e_id ) );

/* 
 *  Response to AT+FRM=200, receives a stream of data.
 */

typedef struct
{
   MRESP_T e;
   rkhui8_t frame[ MMGR_MAX_SIZEOF_RFRAME ];
   rkhui8_t nframe;
} MR_FRM_EVT_T;

e_frm = RKH_ALLOC_EVENT( MR_RFRM_EVT_T, MRESP );
CMRESP( e_frm )->fwde = MR_FRM;
rkh_sma_post_fifo( mmgr, CE( e_frm ) );

Listing 8 - Respuestas con datos asociados

Determinando la respuesta válida

Sabiendo que la respuesta a un comando es una cadena de caracteres y que la misma se recibe asincrónicamente caracter por caracter desde interrupción (ISR), el determinar si esta corresponde con alguna de las respuestas esperadas, puede ser resposabilidad tanto de la ISR como de mmgr.
  • Responsabilidad de mmgr: la ISR determina el final de la cadena de respuesta y envía una notificación a mmgr para que este posteriormente verifique, en función del comando enviado, si la misma corresponde con alguna de las esperadas, enviando la notificación al cliente que corresponda. 
  • Responsabilidad de la ISR: en función del comando enviado esta debe conocer que cadenas de respuesta esperar y de que manera enviar la notificación a mmgr. Luego, este último envía la notificación al cliente que corresponda.
  • Responsabilidad compartida, ambas entidades colaboran para determinar la respuesta recibida.
Como es de imaginarse, existen infinitas soluciones. La manera en que esto puede lograrse está fuera del alcance de este artículo, sin embargo, se propone en posteriores publicaciones mostrar al menos una posible solución, sin hacer uso de extensos bucles y comparaciones innecesarias.

¿Qué ocurre con los URC en este modelo?

Desde el punto de vista de mmgr, las URC podrían recibirse durante la espera de una respuesta como en su estado de reposo. El destinatario del URC depende de las necesidades de la aplicación, podría requerirse que se notifiquen a un objeto activo particular, o que el objeto activo se determine de acuerdo con la URC recibida, entre otras posibilidades. En la Fig. 2 el destinatario de los URC es un único, lnmgr.

Conclusiones

La administración y control de un módulo externo GSM, dial-up, GPS, u otro, operado por comandos AT, puede implementarse eficientemente de manera no bloqueante, respetando las restricciones que imponen estos dispositivos, haciendo uso del encapsulamiento de comportamiento y restringiendo así el acceso concurrente al dispositivo. Los principios de la programación dirigida por eventos [1,2,4], las máquinas de estados (en este caso Statecharts [1,2,3,5,6,7]), y los objetos activos basados en el framework RKH [4] de licencia GPLv3 permiten la construcción de un software claro, robusto y flexible.

Adicionalmente, el artículo Identificación de respuestas a comandos AT en ISR presenta una posible solución dentro de ISR para recibir e identificar eficientemente respuestas y URC, sin hacer uso de extensos bucles y comparaciones innecesarias.

Referencias

[1] M. Samek, “Practical UML Statecharts in C/C++, Second Edition: Event-Driven Programming for Embedded Systems,” Elsevier, October 1, 2008.
[2] D. Harel, "Statecharts: A Visual Formalism for Complex Systems", Sci. Comput. Programming 8 (1987), pp. 231–274.
[3] D. Harel and H. Kugler - "The Rhapsody Semantics of Statecharts", Lecture Notes in Computer Science, Vol. 3147, Springer-Verlag, 2004, pp. 325-354.
[4] RKH, “RKH Sourceforge download site,”, http://sourceforge.net/projects/rkh-reactivesys/, August 7, 2010.
[5] Object Management Group, “Unified Modeling Language: Superstructure version 2.1.1,” formal/2007-02-05, Febrary 2007.
[6] B. P. Douglass, “State machines and Statecharts,”
[7] B. P. Douglass, “UML and Statecharts,”, Embedded.com, June 17, 2003
[8] RKH, “Reference Manual,”, http://rkh-reactivesys.sourceforge.net/
[9] QP and tools, http://www.state-machine.com/

Comentarios