Documente Academic
Documente Profesional
Documente Cultură
(pag6)
(Pag 7)
Se incluye tres servicios verticales: el task manager, la seguridad, y el power
manager. Estos servicios están disponibles para el aplicación de usuario, pero
también pueden ser utilizados por las capas más bajas.
El Task Manager, que es el scheduler de la pila, gestiona el uso del MCU por los
componentes internos de la pila y por la applicación de usuario. El Task
Manager implementa un scheduler cooperativo basado en prioridad
especifícamente diseñado para el tipo de pila multicapa y las demandas de
tiempo crítico de los protocolos de red.
Las rutinas del “power manager “ son las responsables del apagado de todos
los componentes de la pila y el salvado del estado del sistema cuando éste se
echa a dormir y de sus restaurado cuando éste se levanta
El HAL incluye una set completo de API's para el uso de los recursos (EEPROM,
sleep, y watchdogs) asi como de los drivers...
(Pag 8)
(Pag 10)
typedef struct
{
ZDO_StartNetworkConf_t confParams;
void (*ZDO_StartNetworkConf)(ZDO_StartNetworkConf_t *conf);
} ZDO_StartNetworkReq_t;
por lo tanto la petición debe ser precedida de una asignación del puntero de
callback:
networkParams.ZDO_StartNetworkConf = ZDO_StartNetworkConf;
Este ejemplo ilustra una instancia particular del uso del mecanismo petición/confirmación, pero
todos usos siguen el mismo proceder.
Aparte de los pares petición/confirmación, hay casos en los que la aplicación necesita ser avisada de
la existencia de un evento externo. Estos casos no son una replica a una petición especifica. Para
estas situaciones, existen la posibilidad de crear callbacks definidas por usuario con un nombre
especifico que son invocadas por la pila de forma asícrona. Entre estas se encuetran los eventos para
indicar la perdida de conectividad, la disposición de una subcapa para echarse a dormir, o la
notificación de que el sistema está ya en pie.
(Pag 11)
Regla 1: todas las aplicaciones de usuario se organizan en un conjunto de callbacks que se ejecutan
al termino de una solicitud a la capa subyacente.
Regla 2: La aplicación del usuario es la responsable de declarar las callbacks que maneja los
eventos no solicitados del sistema que interesen.
En contraste con S.O. con desalojo en los cuales es más adecuado un manejo múltiple de
aplicaciones que sin embargo requiere una mayor sobrecarga de éstos, los sistemas multitarea
cooperativos son más livianos. Sin embargo, requieren una cooperación entre la aplicación y el
sistema operativo. Los sistemas con desalojo dividen el tiempo de CPU entre las diferentes
aplicaciones (multiplexando por lo tanto transparentemente la CPU) de modo que a los
desarrolladores les parezca que sus aplicaciones tienen el control exclusivo de la CPU. En cambio
una aplicación corriendo en un sistema multitarea cooperativo debe ser cuidadosa con la necesidad
de ceder los recursos de los que dispone (principalmente, la CPU) a la capa subyacencte. Otro
beneficio es que solo se necesita una capa. Lo que se traduce en un ahorro considerable de
memoria.
SYS_PostTask(APL_TASK_ID);
(Pag 12)
En otras palabras, el manejador de tareas de la aplicación corre solo cuando todas las capas de
mayor prioridad se han completado. Esto permite tiempos de ejecución mayores en el contexto del
manejador de tareas, que en el contexto de la callback.
Regla 5: El manejador de tareas de la aplicación corre solo cuando todas las capas de mayor
prioridad se han completado
Otras ID de tareas interesantes son HAL_TASK_ID y BSP_TASK_ID, que hacen referencia a las
tareas pertenecientes a la capa de abstración hardware y al paquete de soporte de la placa. Cuando la
aplicación de usuario conlleva modificaciones en las capas HAL o BSP, en la ejecución aplazada de
callbacks de HAL o BSP debe utilizarse estos ID.
Hasta ahora, las tres formas de que el control de flujo se introdujera en el codigo de aplicación eran:
(1) a través del manejador de tareas a continuación de una invocación de SYS_PostTask, (2) a
través de la callback de confirmación invocada por la capa subyacente tras la ejecución de una
solicitud, y (3) a través de notificaciones de eventos asíncronos invocados por el SO. Hay que tener
en cuenta que para ninguno de tres metodos existe un momento en el cual se sabe va a ser
ejecutado. Una forma de asegurarse la ejecución de una callback definida por el usuario en un
momento específico del futuro es el uso de timers.
La pila provee de un interfaz de alto nivel para timers, que hace uso de un timer hardware de bajo
nivel. Dicha interfaz es parte del HAL y su uso esta ilustrado en muchas aplicaciones de usuario
( BlinkApp.c). La idea general es que una estructura HAL_AppTimer_t contiene el intervalo del
timer, (ya sea un timer cíclico o que se dispara una sola vez) y la función de callback a ejecutar
cuando el timer se dispara. La estructura se debe pasar a HAL_StartAppTimer y a
HALStopAppTimer para iniciar y parar el timer.
3.5 Concurrencia e interrupciones
El concurrencia es el término que se utiliza para referirse a los distintos threads independendientes
que se ejecutan a la vez. En un sistema que utiliza desalojo con multiplexado en el tiempo de la
CPU y en el cual corren multiples treads, la ejecución de una función puede ser interrumpida por el
planificador del sistema en un punto aleatorio, dando el control a otro thread en ejecución que
potencialmente puede ejecutar una función diferente de la misma aplicación. Debido a la
imposiblidad de predecir una interrupción y al hecho de que dos funciones pueden compartir datos,
el desarrollador debe asegurarse que el acceso a datos compartidos sea totalmente atómico.
Como se explicó previamente, en una aplicación BitCloud un único thread se comparte para
ejecutar la aplicación y el S.O. Cuando este thread realiza una tarea en una capa de la pila, éste
adquiere la prioridad de la capa, pero éste es siempre el mismo. Simplemente está ejecutando un
secuencia de funciones (no intercaladas entre ellas) de las diferentes capas de la pila y la aplicación.
Por lo tanto el control del flujo de ejecución no puede encontrarse en más de un callback de usuario
en un mismo tiempo. En general, la ejecución de multiples callbacks no puede ser intercalada, cada
callback se ejecuta como un bloque de código atómico.
(Pag 13)
A pesar de que el multiplexado de CPU no se usa, hay condiciones especiales donde se puede crear
otro thread de control. Ocurre cuando emerge una interrupción hardware, que puede interrumpir la
ejecución en un punto arbitrario ( el principal thread de control debe estar ejecutando codigo de la
pila o de la aplicación cuando esto ocurre ) para gestionar un evento físico proveniente de un
periferico del MCU. ( p.e. La USART o el canal SPI). Esto es análogo al manejo de interrupciones
hardware en otro tipo de sistemas.
En la anterior figura se ilustra un ejemplo de interacción entre la aplicación, la pila, el planificador
de tareas, y las interrupciones hardware. En un principio el planificador ejecuta una tarea mediante
la llamada a APL_TaskHandler. Cuando la aplicación usuario se está ejecutando, ésta es
interrumpida por un evento hardware ( en gris). A la función que se ejecuta con la llegada de la
interrupción se la denomina servicio de interrupción. Tras la ejecución del servicio de interrupción,
se devuelve el control a la aplicación de usuario, y una vez que ésta última termina, el control es
devuelto a planificador de tareas. Éste selecciona una tarea de la capa MAC para ser ejecutada a
continuación. Mientras la función MAC_TaskHandler corre, invoca una callback de confirmación
en la capa ZDO que es interrumpida por otro evento hard. Notese que la capa MAC invoca otra
callback en ZDO que a su vez invoca otra callback registrada por la aplicación de usuario. Por lo
tanto este último callback se ejecuta con la prioridad de la capa MAC o MAC_TASK_ID.
Las aplicaciones BitCloud puede crear rutinas de servicio de interrupción que se ejecuten con la
llegada de un evento hardware. Esto se usa, típicamente, para la gestión de perifericos adicionales
cuyos “drivers” no son parte del SDK … y son, en cambio, añadidos por el usuario. La llamada para
definir un servicio de interrupción es
(Pag 14)
El acceso atómico se puede asegurar dividiendo cada acceso individual con las macros
ATOMIC_SECTION_ENTER y ATOMIC_SECTION_LEAVE. Estas macros inician y terminan los
que se conoce como una sección crítica, un bloque de código que ininterrumpible. Estas macros
operan desabilitando las interrupciones hardware. Las secciones debe ser lo mas cortas posibles
para reducir el tiempo que las interrupciones hardware son enmascaradas. En los
microcontroladores AVR mediante el uso de flags, las interrupciones enmascaradas son salvadas
para gestionarlas más adelante.
1 Cada aplicación define un unico gestor de tareas de aplicación, que contiene el total del
código de la aplicación de usuario ( incluido el código accesible a través llamadas a
subfunciones)
2 Toda aplicación define un numero de callbacks que se ejecutan cuando se completa una
petición asíncrona de la capa subyacente.
3 Toda aplicación define un numero de callbacks de nombres conocidos que se ejecutan con
el procesamiento de un evento por la pila
4 Toda aplicación mantiene un estado global que es compartido ente las callbacks y el
manejador de tareas de usuario.
La función main está embebida dentro de la pila. Una vez la pila se inicializa, el control es
transferido de main a planificador de pila, que comienza a invocar, los distintos manejadores de
tarea en un order de prioridad ( de mas alto a más bajo), invocando eventualmente al
APL_TaskHandler, que es el punto de entrada a la aplicación. Tras la llamada inicial al manejador
de tareas de la aplicación, el control es transferido continuamente entre la pila y las callbacks como
se ha mostrado en la figura anterior.
Una aplicación puede ser dividida en un conjunto de archivos escritos en C. Para simplificar, aquí
se considera la existencia de un único archivo.
/*******************************
#include directives
*******************************/
...
#include <taskManager.h>
#include <zdo.h>
#include <configServer.h>
#include <aps.h>
/*******************************
FUNCTION PROTOTYPES
*******************************/
...
/*******************************
GLOBAL VARIABLES
*******************************/
AppState_t appState = APP_INITING_STATE;
...
/*******************************
IMPLEMENTATION
*******************************/
/*******************************
Application task handler
*******************************/
void APL_TaskHandler()
{
switch (appState)
{
case APP_IN_NETWORK_STATE:
...
break;
case APP_INITING_STATE: //node has initial state
...
break;
case APP_STARTING_NETWORK_STATE:
...
break;
}
}
/*******************************
Confirm callbacks
*******************************/
static void ZDO_StartNetworkConf(ZDO_StartNetworkConf_t *confirmInfo)
{
...
if (ZDO_SUCCESS_STATUS == confirmInfo->status)
{
appState = APP_IN_NETWORK_STATE;
...
SYS_PostTask(APL_TASK_ID);
}
}
/*******************************
Indication callbacks
*******************************/
void ZDO_WakeUp_Ind(void)
{
...
if (APP_IN_NETWORK_STATE == appState)
{
appState = APP_STARTING_NETWORK_STATE;
...
SYS_PostTask(APL_TASK_ID);
}
}
(Pag 16)
El anterior esqueleto de código es representativo de la mayoria de aplicaciones.
Invariablemente, hay un estado global, representado en este caso por la variable appState al cual
acceden desde las callbacks y desde el manejador de tareas. En aplicaciones del mundo real, el
estado se representa por un determinado número de variables. Adviertase que un típico manejador
de tareas cambia el estado de las variables al ejecutar código para un estado particular, que es una
plantilla de programación compartida por todas las aplicaciones ejemplo incluidas en el SDK. El
desarrollador debe primero considerar su aplicación en términos de máquina de estados. El
mapeado de esa máquina de estados es el método natural de programación basada en eventos.
Tengase en cuenta también el uso de la función del gestor SYS_PostTask. p.e. la aplicación aplaza
a propósito el procesamiento de la indicación de pérdida de conectividad.(:::). La callback
simplemente cambia el valor de la variable de estado appState y devuelve el control a la capa desde
la que fue invocada (ZDO). Este estilo de programación es totalmente consistente con un sistema
multitarea cooperativo, y permite a la pila manejar tareas de una prioridad más alta antes de
devolver el control a la tarea aplazada
(Pag 17)
4 La interfaz ConfigServer
la pila de BitCloud provee de un extenso set de parámetros de configuración que determinan los
diferentes aspectos del comportamiento de la red y sus nodos. Estos parámetros son accesibles a
través de la interaz ConfigServer (abreviado CS). Este capitulo introduce ligeramente la interfaz
CS. Un completo listado y descripción se puede encontrar en la documentación de la API.
Todos los parámetros CS se pueden dividir en dos categorias: persistentes y no persistentes. Los
persistentes se almacenan en la memoria EEPROM y sus valores son accessibles por la aplicación y
la pila tras el reset HW del nodo. Los parámetros no persistentes son almacenados en la memoria
RAM y tras un reset HW son reinicializados a sus valores por defecto. El fichero ConfigServer.h
incluye un comentario seguido de cada ID de parámetro indicando si éste es persistente o no.
+= -DCS_RF_TX_POWER=3
En las aplicaciones BitCloud las variables de entorno CFLAGS son automaticamente inicializadas
durante el proceso de compilación del proyecto de modo que el simple hecho de añadir la línea de
arriba es suficiente para asignar el valor deseado al parámetro.
A pesar de que el método es simple, solo permite la configuración del parámetro en tiempo de
compilación y no permite modificaciones en tiempo de ejecución.
5 Un vistazo a la red
Como se vio con anterioridad la pila de BitCloud sigue la estructura de la especificación de Zigbee
PRO que permite a las aplicaciones interactuar directamente solo con las componentes APS y ZDO
del núcleo de la pila. Este enfoque simplifica significativamente el desarrollo de aplicaciones a la
vez que garantiza que la aplicación no interfiere en el protocolo de red y por lo tanto el
comportamiento siempre cumple la especificación Zigbee PRO.
A pesar del uso del termino “inicio de red” desde el punto de vista de la aplicación no hay
diferencia entre los procedimiento que se sigue para crear un red y el que se sigue para unirse a ésta.
La aplicación BitCloud deberá ejecutar el procedimiento de red en los tres pasos que se describen
con detalle en esta sección:
Adviertase que todos los parámetros CS mencionados en la sección 5.1 no se podrá modificar si el
dispositivo ya está unido a la red. Sin embargo los parámetros no mencionados en la sección 7.1 se
pueden modificar una vez el nodo ha abandonado la red.
La especificación Zigbee diferencia entre tres tipos de dispositivos que pueden estar presentes en
una red: coordinador, router y dispositivo final.
• la principal responsablilidad del nodo coordinador (C) es la de formar la red con las
características deseadas. Tras la formación de la red otros nodos se pueden unir a ella vía el
coordinador o los routers ya presentes en la red. El nodo coordinador puede ejecutar
funciones de enrutado. Debido a que la dirección estática asociada al coordinador es
0x0000, solo un único dispositivo de este tipo es permitido en la red.
• Un nodo router (R) permite el envio transparente de datos a través de él desde otros nodos a
la dirección de destino. Pero por supuesto el también puede enviar su propia información. Al
igual que el coordinador el nodo router puede ser capaz de añadir nuevos nodos a la red.
• Un nodo final (ED) solo puede enviar y recibir datos que han sido reenviados hacia o por el
nodo destino a través de los nodos padres del nodo final al que actualmente esta asociado.
Solo los nodos finales tienen la capacidad de echarse a dormir.
(Pag 20)
El tipo de dispositivo se define por el parámetro CS_DEVICE_TYPE de tipo DeviceType_t. Este
parámetro se puede fijar a uno de los siguientes valores: DEVICE_TYPE_COORDINATOR (o
0x00) para el coordinador, DEVICE_TYPE_ROUTER (o 0x01) y DEVICE_TYPE_END_DEVICE
(o 0x02) para el router y el dispositivo final respectivamente.
Adicionalmente el parámetro booleano CS_RX_ON_WHEN_IDLE se debe fijar a true en los nodos
coordinador y router mientras que en los dispositivos finales que pueden echarse a dormir, este se
deberá fijar a falso para indicar la recepción indirecta mediante peticiones sonda como se describe
en la sección 5.3.4.1 El código que sigue a continuación es un ejemplo de como configurar un
dispositivo como nodo final:
CS_WriteParameter(CS_DEVICE_TYPE_ID, &deviceType;
Cuando se une a una red Zigbee, cada dispositivo es identificado por lo que se conoce como
dirección corta (16 bits), también denominada como dirección de red (NWK). Esta es la dirección
usada en las cabeceras de cada frame durante el intercambio de datos.
En BitCloud las direcciones cortas puede ser seleccionadas aleatoriamente por la pila durante la
unión a la red o asignadas por la aplicación de usuario a un valor deseado antes de proceder a unirse
a una red. Es crítico asegurarse de que todos los nodos de la red usan el mismos mecanismo de
adquisición de dirección.
(Pag 21)
En el esquema de asignación automática aleatoria de dirección un mecanismo de resolución de
conflictos detecta y resuelve situaciones en las que la dirección escogida aleatoriamente parece no
ser única, como por ejemplo cuando se une a la red un nodo con las misma dirección ya presente en
la red. Despues de que la dirección de red es actualizada, la aplicación del nodo correspondiente es
informada vía ZDO_MgmtNwkUpdateNotf() con el status ZDO_NWK_UPDATE_STATUS
(0X8E) y el nuevo valor de dirección en el argumento. En redes de direccionamiento manual la
aplicación de usuario es la responsable de resolver dichos conflictos. Aún así, Bitcloud ofrece
asistencia en estos casos mediante la llamada a la función ZDO_MgmtNwkUpdateNotf() con el
status ZDO_STATIC_ADDRESS_CONFLICT_STATUS (0x95) en el nodo que ha detectado el
conflicto ( que puede diferrir del nodo que tiene el conflicto de dirección)
Adviertase que la organización de la red (topología) que se muestra en la figura 5-1a) casi siempre
difiere del que se usa en realidad de enlaces de transmisión directa que se utiliza para enrutar los
datos a través de la red.. En la figura 5-1b se muestra un modelo de enlaces de transmisión directa
en la que todos los nodos se encuentran en el rango de señal de cada otro.
Además de los parámetros de nodo y de red de destino descritos en este capítulo es fundamental que
antes de unirse a una red, el nodo configure los parámetros que tienen un impacto sobre la
organización de la red y por lo tanto pueden ser utilizados para lograr el comportamiento y
rendimiento adecuando en la red.
Los nodos coordinadores y enrutadores pueden limitar el número de hijos directos que les pueden
enlazar configurando el parámetro CS_MAX_CHILDREN_AMOUNT que define el número de
hijos totales (enrutadores + nodos finales) que puede tener.
CS_MAX_ROUTER_CHILDREN_AMOUNT define el máximo de enrutadores entre los hijos, y
por lo tanto, la diferencia entre estos dos parámetros especifica el número máximo de dispositivos
finales que se pueden directamente conectar a esto nodo al mismo tiempo. Adviertase que la
siguiente relación debe cumplirse siempre en nodos coordinadores y routers:
CS_NEIB_TABLE_SIZE > CS_MAX_CHILDREN_AMOUNT >= CS_MAX_ROUTER_CHILDREN
(Pag 22)
La profundidad máxima de la red se define mediante CS_MAX_NETWORK_DEPTH, que
especifica el máximo número de saltos posibles en la red, desde un nodo perteneciente al
coordinador. Si este límite es alcanzado en la unión de un router a la red, este no será capaz de
enlazar ningún hijo, aunque cualquiera de los anteriores parámetros se lo permitiera.
CS_MAX_NETWORK_DEPTH tienen un impacto directo en muchos de los intervalos de tiempo
de espera dentro de la pila, por lo tanto para que el correcto funcionamiento de la red esté
garantizado éste debe tener el mismo valor en todos los nodos de la red.
Antes de proceder a iniciar una red, el nodo es responsable de fijar los parámetros que deciden si
este nodo desea formar la red (coordinador) o si el nodo desea unirse a la red. Estos parámetros son:
CS_CHANNEL_PAGE define el tipo de modulación a ser usado por el dispositivo. Este parámetro
se ignora para frecuencias de 2.4GHz, mientras que en la banda 868/915 Mhz solo se aceptan los
valores 0 y 2. Para la banda de 780 Mhz debe ser siempre 5.
(Pag 23)
La tabla 5-1 muestra la distribución de canales a través de toda la banda de frecuencias de la
IEE802.15.4, y muestra la velocidad de transmisión del medio físico para las distintas páginas del
canal.
CS_EXT_PAIN_ID es el identificador extendido de 64-bit de la red que es verificado durante el
proceso de asociación. Por lo tanto los dispositivos que desean unirse a cierta red deben configurar
su PAN ID igual al del coordinador de la red. Si CS_EXT_PAN_ID es fijado a 0x0 en un router o
dispositivo final, entonces el nodo se unirá a la primera red detectada en el aire.
Durante la formación de la red el coordinador selecciona otro identificador de red: este nuevo
PAN_ID de red de 16 bit es usa en las cabeceras de las tramas durante el proceso de intercambio de
datos en vez del de 64 bit que es mas pesado. Por defecto el PAN_ID de red (16-bit) se genera de
forma aleatoria, y si un coordinador durante la formación de la red detecta otra red con el mismo
PAN_ID extendido (64-bit) entonces selecciona de forma automática un PAN_ID de red (16-bit)
diferente para evitar conflictos durante la transmisión de datos.
(Pag 24)
Sin embargo este mecanismo conlleva un comportamiento indeseado en ciertas ocasiones: si se
resetea el nodo coordinador, y este reinicia la red con el mismo PANID extendido y en el mismo
canal, puede encontrarse con un router de la anterior red y por lo tanto formará una red con un
PANID de red diferente. Sin embargo se requiere que el coordinador se una a la misma red donde
anteriormente actuaba y que participe en el intercambio de datos. Por lo tanto, para forzar a que el
coordinador se una a la antigua red, se recomienda que el PANID se predefina en el nivel de
aplicación antes de iniciar la red tal y como se muestra en el siguiente ejemplo:
bool predefPANID=true;
uint16_t nwkPANID=0x1111;
CS_WriteParameter(CS_NKW_PREDEFINED_PAN_ID, &predefPANID);
CS_WriteParameter(CS_NWK_PANID_ID, &nwkPANID);
Si el PANID de red se predefine en un nodo no coordinador, solo será posible el ingresar en una red
con el mismo PANID extendido y de red configurado en el nodo.
(Pag 25)
– Perdida del padre. Como en una topología de malla, un router no tiene nodos padres
dedicados, este causa solo se dá en nodos finales
– Se solicita el abandono de la red. Esta solicitud puede ser emitida tanto (a) por la aplicación
que corre en el nodo como (b) por un nodo remoto
La primera razón (a) es válida para todo tipo de dispositivos. Mientras que la segunda (b) se aplica a
routers y dispositivos finales unicamente.
La principal dificultad del seguimiento de eventos de p´rrdida de hjos viene del hecho de que un
nodo final es propenso a tener periodos de inactividad (sleep) y por lo tanto no se realizan
intercambio de información durante intervalos largos de tiempo incluso si el nodo final se encuentra
en la red y preparado para enviar información en cuanto finalize su periodo de inactividad.
(Pag 26)
Por lo tanto, para asegurar que un nodo hijo está fuera de la red y simplemente no se ha echado a
dormir, el nodo padre debe conocer la duración de los intervalos de inactividad del nodo hijo ( p.e.
Tener el mismo CS_END_DEVICE_SLEEP_PERIOD). Par un nodo padre esto implica que sus
hijos se levanten periodicamente y envien alguna solicitud. Si durante el intervalo de tiempo
calculado como:
CS_NWK_END_DEVICE_MAX_FAILURES * (CS_END_DEVIE_SLEEP_PERIOD + CS_INDIRECT_POLL_RATE)
un hijo no entrega ninguna trama de solicitud, el nodo padre asume que ese hijo ha abandonado la
red y por lo tanto se lanza la callback ZDO_MgmtNwkUpdateNotf() con el status
ZDO_CHILD_REMOVED (0x93). Y en sus argumentos, se indica la dirección extendida del nodo
hijo que abandonó la red
El diagrama de secuencia para el procedimiento de abandono de red se muestra en la figura 5.4 (el
nodo abandona la red de motu propio) y en la figura 5.5 (el nodo abandona la red debido a una
solicitud remota).
(Pag 27)
5.3 Intercambio de información
Obviamente el proposito de establecer una red Zigbee es el realizar un intercambio de información
entre nodos remotos según los solicite la funcionalidad de la aplicación. En esta sección se da un
vistazo a la configuración del nodo, los parárametros de transmisión y las funciones del API
Bitcloud responsbles de la comunicación en el nivel de aplicación.
(Pag 28)
El fragmento de código que sigue ejemplifica como registrar y definir un punto final ep1 con un ID
de perfil 1 y sin ningún tipo de limitación en cuanto a I/O.
// Register endpoint
APS_RegisterEndpointReq(&endpointParams);
Debido a que la pila necesita que el ASDU se guarde en la memoria RAM como un bloque continuo
de datos con unas características especificas, la aplicación deberá construir dicha estructura como se
muestra en la figura 5.6
La maxima longitud permitida para el frame de datos es de 53 bytes en datos cifrados y 84 bytes
para transmisiones no seguras.
(Pag 29)
El siguiente extracto de código ejemplifica como crear una solicitud de envío con la correcta
estructura ASDU:
(Pag 30)
Como el protocolo Zigbee permite una comunicación bidireccional, la aplicación puede solicitar un
reconocimiento de la recepción de la trama en el nivel de aplicación (APS ACK) fijando
acknowledgedTransmission en el campo txOption del la solicitud de envio a 1. En este caso a la
aplicación se la notifica el éxito de la entrega de la trama (APS_SUCCESS_STATUS) mediante la
función callback de confirmación que se registró, solo si despues de que el ACK de la trama
correspondiente haya sido recibido. Si durante el intervalo CS_ACK_TIMEOUT no se recibe ACK
entonces se emite la función callback con APS_NO_ACK_STATUS.
Las transmisiones sin APS ACK's tienen una mayor tasa de envío pero no son seguras, puesto que
no se puede confirmar el envío de una trama. En estos casos si todos los parámetros se fijan
correctamente, se llama a la callback con APS_SUCCESS_STATUS tras la emisión de la trama por
el medio físico (aire).
La instancia de APS_DataReq_t solo puede ser reutilizada tras la confirmación de que la callback a
sido ejecutada.
Además de difundir la trama sobre la red, la aplicación puede configurar la transmisión de modo
que la trama sea entregada a todos los puntos finales registrados en los nodos destino. Esto se puede
realizar fijando el campo dstEndpoint en la solicitud de envío a APS_BROADCAS_ENDPOINT ( ó
0xFF). Esta difusión a nivel de nodo también se puede ejecutar mediante transmisiones punto a
punto.
Al igual que las tramas unicast, el mensaje broadcast puede ser enviado con un limite de saltos
( configurando el campo radio). Si el radio es fijado a 0 el mensaje llegará a todos los nodos de la
red.
Tras la recepción de una trama por el nodo, la pila verifica si el destino indicado en la cabecera de la
trama coincide con los distintos puntos finales registrados en el nodo. En caso de coincidencia, la
correspondiente callback, especificada en el campo APS_DataInd de la solicitud de registro de un
punto final (APS_RegisterEndpointReq_t), se ejecutará con un argumento de tipo APS_DataInd_t.
Este argumento contiene el mensaje de la aplicación (el campo ASDU) así como información sobre
las direcciones fuente y destino, punto final, perfil, etc.
(Pag 31)
(Pag 32)
Tras ser alimentado, un nodo siempre comienza en modo activo y tiene su MCU alimentado y
activo, con el chip RF listo para realizar operaciones Rx/Tx. La aplicación puede llamar a cualquier
procedimiento BitCloud y puede ejecutar callbacks y recibir notificaciones.
En modo suspendido, el chip RF se apaga mientras que el MCU es puesto en un modo especial de
bajo consumo. La aplicación no podrá realizar ningun tipo operación radio, ni comunicar con
periféricos externos.
Para poner los dispositivos finales a dormir, la aplicación debe llamar a ZDO_SleepReq() con un
argumento de tipo ZDO_SleepReq_t. Tras la confirmación, la callback indicara el estado de
ejecución y si se devuelve ZDO_SUCCESS_STATUS el nodo entrara en modo suspendido.
Hay dos formas de levantar el nodo: una planificada y otra a través de una interrupción IRQ
Adviertase que si se llama a IRQ_callback desde un estado de inactividad y el timer que controla el
tiempo de inactividad esta fijado deacuerdo a CS_END_DEVICE_SLEEP_PERIOD, este se parará
y comenzara de nuevo a la siguiente llamada a ZDO_SleepReq()
(Pag 33)
Ambos métodos pueden combinarse en una aplicación para gestionar el consumo de energía. Si
CS_END_DEVICE_SLEEP_PERIOD se fija a cero, solo una interrupción hardware puede levantar
el nodo de un estado suspendido a su estado activo.
5.4.2 Apagar el chip RF
En muchos escenarios se puede requerir que el MCU permanezca operando (p.e. para proposistos
de procesado de datos ) mientras que el chip de RF se apaga para reducir el consumo de energía.
Este caso es contemplado por la pila BitCloud, posibilitando el apagado del mecanismo de polling
en un dispositivo final y por lo tanto el del chip RF. Para apagar la radio, la aplicación deberá llamar
a la función ZDO_StopSyncReq(). Para encenderla de nuevo (y permitir el polling) la aplicación
deberá llamar a ZDO_StartSyncReq(). Cuando se use este mecanismo sera importante tener en
cuenta que el nodo padre espera solicitudes de polling de su nodo hijo, y que si no las recibiera
durante un cierto periodo de tiempo, eliminará el nodo de la red. Tengase en cuenta que este
mecanismo únicamente está disponible en nodos finales.
5.5 Seguridad
BitCloud proporciona dos niveles de seguridad en la red ZigBee; sin seguridad, y nivel de seguridad
standard. Por lo tanto existen dos tipos de librerias localizadas en el directorio lib/: una sin
seguridad y otra que incluye soporte a seguridad. La elección de una librería en particular se debe
realizar en el fichero Makefile cuando se compila la aplicación. En el ejemplo de aplicación de
BitCloud, esta elección se realiza basandose en los valores del parámetro SECURITY_MODE.
Si se requiere soporte a seguridad, ademas de incluir las librerías adecuadas, los parámetros
CS_ZDO_SECURITY_STATUS y CS_NETWORK_KEY deben ser configurados para permitir la
adecuada operación de la red.
Este capítulo arroja un poco de luz sobre las principales interfaces HW soportadas en BitCloud.
La pila BitCloud proporciona un API para soporte de la interface serie Univerval Synchronous
/Asynchronous Receiver/Transmitter (USART).
Para permitir la comunicación mediante interfaz USART la aplicación primero debe configurar el
correspondiente puerto USART usando una variable global estática de tipo HAL_UsartDescriptor_t.
Se requiere fijar todos los parámetros USART más comunes tales como modo sincrono/asincrono,
baudrate, flow control, modo de paridad, etc. Adviertase que a pesar de que el nombre USART es
generalmente usado en este capitulo para hacer referencia al API. es lo mismo que para las
interfaces asincronas o sincronas (UART / USRT). Para modo de operación asincrono, el descriptor
debe ser fijado a USART_MODE_ASYNC.
La recepción y transmisión de datos a través de la USART se puede configurar para trabajar tanto
en modo callback o en modo polling como se describe más abajo.
Los ajustes de la USART se deben realizar usando la función HAL_OpenUsart() con un argumento
apuntando una la variable global de tipo HAL_UsartDescriptor_t con la configuración deseada del
puerto. El valor devuelto indica si el puerto se ha sido abierto de manera satisfactoria y puede ser
usado para el intercambio de datos.
Cuando no haya necesidad de mantener activo el puerto USART, la aplicación deberá cerrarlo
usando la función HAL_CloseUsart().
HAL_UsartDescriptor_t appUsartDescriptor;
static uint8_t usartRxBuffer[100]; // any size maybe present
…
appUsartDescriptor.rxBuffer = usartRxBuffer; // enable Rx
appUsartDescriptor.rxBufferLength = sizeof(usartRxBuffer);
appUsartDescriptor.txBuffer = NULL; // use callback mode
appUsartDescriptor.txBufferLength = 0;
appUsartDescriptor.rxCallback = rxCallback;
appUsartDescriptor.txCallback = txCallback;
…
HAL_OpenUsart(&appUsartDescriptor);
…
La siguiente figura 6.1 ilustra el correspondiente diagrama de secuencia para el uso de la USART en
modo callback
Para la transmisión de datos se debe llamar a la función HAL_WriteUsart() con un puntero hacia el
buffer de datos a ser transmitido y con la longitud del buffer como argumentos. Si se devuelve un
valor mayor de cero, la función registrada como txCallback en el descriptor de la USART se
ejecutará despues de notificar a la aplicación que la transmisión ha finalizado.
La USART es capaz de recibir datos si los campos rxBuffer y rxBufferLength del correspondiente
descriptor de la USART no son NULL y cero respectivamente. Para el modo callback el campo
rxCallback deberá apuntar a la función que se ejecutará siempre que llegen datos al rxBuffer de la
USAR con el número de bytes recibidos como argumento. Conocido este número, la aplicación
debe reenviar los datos recibidos desde el rxBuffer de la USART hacia el buffer de la aplicación
usando la función HAL_ReadUsart()
La figura 6.2 ilustra el diagrama de secuencia para Tx7Rx en modo polling. La principal diferencia
en la operación Tx entre los modos callback y polling reside en que en esta última tras la llamada a
HAL_WriteUsart() todos los datos utiizados en la transmisión como argumento, son movidos
cíclicamente del txBuffer al descriptor de la USART. Y por lo tanto la aplicación puede reusar la
memoria ocupada por los datos. La función HAL_IsTxEmpty() se usa para verificar tanto que hay
suficiente espacio en el txBuffer como para verificar cuantos bytes han sido realmente transferidos.
En el caso del overflow de los txBuffer o rxBuffer, el resto de los datos se pierde. Para evitar la
pérdida de datos, la aplicación deberá controla el número de bytes escritos que devuelve
HAL_WriteUsart() y si es posible usar HW flow control.
Todos los paquetes “dirección” transmitidos por el bus TWI son de 9 bits de
longitud, y consisten en 7 bits de dirección, un bit de control READ/WRITE, y el
bit ACK. Si el bit READ/WRITE está alto, indica operación de lectura, en otro
caso se ejecutará una operación de escritura. Cuando el esclavo reconoce que
está siendo direccionado, deberá realizar el reconocimiento, poniendo un nivel
bajo el la línea SDA en el noveno ciclo de SCL (el ACK). Si la esclavo
direccionado estuviera ocupado, o por alguna otra razón no puede responder a
la solicitud del master, la línea SDA debe permanecer alta en el ciclo de reloj de
ACK. El Master puede entonces transmitir la condición de STOP o la de
REPEATED START para iniciar una nueva transmisión. Un paquete de dirección
consistente en la dirección del esclavo mas el bit READ/WRITE se denomina
SLA+R o SLA+W respectivamente.
Toda dirección con el formato 1111 xxxx se reserva para futuros propósitos.
Todos los paquetes de datos transmitidos por el bus TWI son de 9 bits de
longitud, y consisten en un byte de datos y un bit ACK, durante la transmisión
de datos, el Master controla la línea de reloj y las condiciones START y STOP,
mientras que el receptor es responsable de la generación del ACK.
Como se dijo con anterioridad, el ACK se genera poniendo a nivel bajo la señal
ACK en el noveno ciclo de reloj SCL Si el receptor deja la línea SDA en alto, se
genera NACK. Cuando al receptor le llega el último byte, o por alguna razón no
puede recibir más bytes, deberá informar al transmisor enviando un NACK tras
el último byte.
6.3Bus SPI
Dependiendo de la plataforma MCU, el componente HAL de la pila BitCloud
puede implementar el protocolo SPI sobre el bus USART (plataformas AVR) o un
bus SPI auténtico cuando éste este disponible. Sin embargo las
correspondientes llamadas a la API definida en el fichero spi.h son
independientes de la plataforma subyacente y la única diferencia aparece en la
configuración SPI, más concretamente en los campos de la variable estática de
tipo HAL_SpiDescriptor_t.
Por último, si no se espera más intercambio de datos, el bus SPI se debe cerrar
para liberar los recursos ocupados.
Las nombres de las funciones y macros relacionadas con las GPIO se definen en el fichero gpio.h
del componente HAL_HWD. Las llamadas a función tienen la forma
“GPIO_#pin_name#_#function_name#()”.
6.5 ADC
Bitcloud provee de una API independiente de la plataforma para el convertidor analógico-digital.
Como en otras interfaces, el ADC se debe configurar primero antes de ser usado mediante la
función HAL_OpenAdc() pasando un puntero a una variable global HAL_AdcParams_t que
contenga los parámetros de configuración. Dicha función devuelve el status del ADC indicando si la
operación de apertura se ha realizado correctamente. La operación puede fallar debido a alguna de
las siguientes razones:
Despues de inicializar el ADC con éxito, la lectura del canal que se desee muestrear se puede
realizar usando la función HAL_ReadAdc(). El resultado se devuelve mediante una función
callback a un buffer especificado como registro en la función HAL_OpenAdc().
El componente HAL de la pila BitCloud también provee soporte a las siguientes interfaces:
– 1-Wire
– IRQ
– Aplicaciones timer, tiempo del sistema
– Watchdog
La documentación de la pila contiene la información sobre las funciones API que se deben usar para
la gestión de las interfaces mencionadas.
A pesar de estar disponibles en el componente HAL, las siguientes funciones son únicamente
usadas para su ejecución interna dentro de la pila por lo que las aplicaciones de usuario deben
evitar en la medida de lo posible su uso:
– HAL_ReadEeprom/HAL_WriteEeprom
– Sleep Timer
Regla del sistema 8: No se debe pasar ninguna estructura a la pila, como por ejemplo el paso de
estructuras como argumentos de funciones. Pasa un puntero en su lugar que apunte a esta.
Regla del sistema 9: Se deben usar variables globales en vez de variables locales, siempre y
cuando sea posible.
El uso de callbacks definidos por el usuario aseguran que las estructuras se pasen mediante puntero.
El usuario debe verificar que esto también se cumbple en las funciones locales que este defina.
En su conjunto, la demanda de RAM por la pila se debe consensuar con la demanda de la aplicación
de usuario. Afortunadamente, el consumo total de RAM usada para mantenimiento de datos de la
pila es un parámetro de usuario configurable. El ConfigServer (o CS) se distribulle con el código
fuente, para permitir al usuario afinar los parámetros runtime de la pila. Mediante estos parámetros
se puede manejal el tamaño total de los datos que la pila almacena en la RAM para tablas. La
siguiente lista muestra estos parámetros y proporciona una fórmula simple para calcular el consumo
de RAM basado en el valor del parámetro.
La suma total de RAM consumida por la pila se puede calcular sumando la última columna a