Entrada destacada
aprendiendoarduino_logo

Análisis: Kits Aprendizaje XBee de Digi

La gente de Digi en Logroño me ha dejado para probar dos kits de aprendizaje para que los pruebe y de paso me sirvan para preparar la parte de comunicación inlambrica del curso Arduino avanzado en http://www.aprendiendoarduino.com/arduino-avanzado-2016/.

Los kits que he probado son:

Se trata de unos kits de aprendizaje de los famosos módulos RF XBee que fabrica Digi para comunicación inalámbrica y que pueden adquirirse en digi-key electronics http://www.digikey.es/

En la caja de ambos kits viene todo el hardware y el enlace a la web donde se encuentran los tutoriales y guías para el uso de los kits.

Veamos por separado cada uno de los kits.

Digi Wireless Connectivity Kit

Aunque el uso que voy a hacer los los módulos va a ser siempre con Arduino, me decidí empezar con este kit que no tiene Arduino ni posibilidad de conectar con un microcontrolador directamente, porque me parecía más sencillo y me quería centrar en aprender la tecnología ZigBee y manejar los módulos XBee de Digi, y no me equivoqué.

Luego con el siguiente kit (XBee Arduino Compatible Coding Platform) y los conocimientos adquiridos, me resultó más fácil manejar los módulos XBee con Arduino.

El hardware de este kit es muy sencillo, se compone de dos módulos XBee serie 1 o XBee 802.15.4 que son unos módulos muy sencillos de Xbee, dos placas de desarrollo para los módulos con conectores grove y dos cables micro USB para conectar las placas de desarrollo al ordenador.

wireless_connectivity_kit_500x316

Este hardware puede comprarse en digi-key en el siguiente enlace: http://www.digikey.es/product-detail/es/digi-international/XKB2-AT-WWC/602-1551-ND/5305247 y tiene un coste de 59$ (aproximadamente 53€).

En la web de digi-key hay un kit nuevo http://www.digikey.es/product-detail/es/digi-international/XKB2-A2T-WWC/602-1902-ND/6010111 que usa los módulos XBee S2C 802.15.4

En mi caso el que he probado es el que lleva los módulos Serie 1:

También hay otros kits disponibles: http://www.digikey.es/en/product-highlight/d/digi-intl/xbee-arduino-coding-platform

Sobre el hardware, decir que es muy sencillo y útil para el aprendizaje, pero luego solo se podría reutilizar los módulos RF, porque las placas de desarrollo no les veo mucha salida salvo para hacer las prácticas propuestas en el kit. A estas placas de desarrollo les añadiría una salida accesible del puerto serie con un selector a 3.3 o 5 V para poder conectarlas a una Raspberry Pi o un Arduino y poder seguir usándolas y aprender la integración con otros dispositivos, ya que sino están limitadas al uso con el ordenador.

Para usar este hardware Digi pone a disposición de los compradores del kit y del resto del mundo un tutorial que va contando paso a paso cómo montar los módulos, como instalar el software necesario para configurar y manejar los Xbee, explica cómo funcionan los módulos y en cada apartado propone ejercicios prácticos para usarlo con el kit adquirido.

Este completo tutorial es accesible desde: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm

PDF del tutorial: http://www.digi.com/resources/documentation/digidocs/pdfs/90001456-13.pdf

Este tutorial es la gran aportación de Digi para aprender a manejar sus módulos RF desde cero y te guia paso a paso como si de un curso online fuera.

El tutorial comienza con ejemplos muy sencillos y hace una guía paso a paso para aprender el manejo los módulos. Los puntos más importantes que se ven son:

Este tutorial es perfecto para aprender a manejar los módulos de una forma muy didáctica, aunque en algunos aspectos se queda corto en la explicación (al menos para los más curiosos) y hay que hacer un acto de fe que con esa configuración funciona, pero en algún caso sin explicar bien porqué. Esa falta de información puede llevar a error en un par de casos a la hora de hacer funcionar la práctica, pero pensando un poco es sencillo resolverlo.

Otro defecto de este tutorial, al menos para mi, es que los ejercicios más interesantes los hace con Java para crear una aplicación en el ordenador que se conecte a los módulos e interactúe con ellos, pero yo añadiría esos mismos ejercicios con algún otro lenguaje como python con .NET.

El objetivo de este kit junto con el manual es aprender a manejar los módulos RF de XBee para la conexión de dispositivos y sensores y lo cumple a la perfección. Además por aprox. 53€ después de aprender a usarlos puedes reutilizar los módulos XBee en cualquier proyecto.

Para mis cursos en www.aprendiendoarduino.com uso como base este tutorial para enseñar como manejar los módulos Xbee.

XBee Arduino Compatible Coding Platform

El segundo kit de aprendizaje de Digi que he probado, comencé a usarlo cuando ya había probando a fondo el anterior (Wireless Connectivity Kit) y conocía bien el uso de los módulos RF de XBee, lo que me facilitó mucho el uso de este kit, puesto que la parte más teórica del funcionamiento de los módulos de XBee no viene en el tutorial de este kit.

El hardware de este kit es muy completo y trae entre otras cosas:

Todo el contenido del kit está en http://docs.digi.com/display/XBeeArduinoCodingPlatform/Kit+contents

wirelessgamekit

Este hardware puede comprarse en digi-key en el siguiente enlace por 99$ (aproximadamente 89,11€): http://www.digikey.es/product-detail/en/digi-international/XKB2-AT-WWG/602-1550-ND/5271212

La verdad es que es un buen precio por el kit teniendo en cuenta que tenemos 3 módulos XBee.

Datasheets:

Sobre el hardware, decir que es muy completo y que todos los materiales que vienen pueden ser reutilizados para otros proyectos.

Para usar este hardware Digi pone a disposición de los compradores del kit y del resto del mundo un tutorial que va contando paso a paso diversos proyectos enfocados al juego, tanto con Arduino como interacción con el ordenador.

El tutorial es accesible desde: http://docs.digi.com/display/XBeeArduinoCodingPlatform/XBee+Arduino+Compatible+Coding+Platform

Este tutorial empieza haciendo una breve descripción del kit y luego explica la instalación del software XCTU y un primer ejemplo. Luego ya entra de lleno en los proyectos.

El kit incluye cinco proyectos con processing para demostrar la interacción con software y otros 5 proyectos con Arduino, para hacer circuitos inalámbricos con los módulos XBee.

Este tutorial se centra en los proyectos que son muy didácticos, pero apenas trata la parte más teórica del funcionamiento de XBee. En algunos proyectos hay enlaces a los aspectos de cómo funcionan los módulos XBee, pero están un poco escondidos y no son accesibles desde el menú lateral.

Los proyectos me gustan, pero de nuevo hay que hacer un acto de fe que las configuraciones que nos dan funcionan, aunque no se explica porque los parámetros que funcionan son esos y no otros.

Al final de cada proyecto hay un apartado llamado “Learn More” y en muchos casos apunta al tutorial del anterior kit (Wireless Connectivity Kit), lo que confirma mi idea que antes de empezar con este kit, es recomendable leer el tutorial del kit anterior si quieres conocer bien el manejo de los módulos XBee.

Después de los proyectos y para finalizar hay varios apartados de información adicional, especialmente interesantes el de troubleshooting y XBee buying guide.

Proyectos Usando Processing

Los proyectos propuestos en este tutorial para interacción de XBee con software, en este caso con processing, son:

Todo el código está disponible en:

Estos proyectos no están actualizados a la última versión 3 de processing, lo que provoca que aparezca algún pequeño error en el código fácilmente solucionable.

Estos 5 proyectos son básicamente iguales y nos enseñan cómo interactuar hardware y software de forma inalámbrica. Nos da la configuración de los dos módulos, uno conectado al ordenador y otro a unos botones, potenciómetros, etc… y nos da el software a ejecutar. Luego simplemente es ver como interactua.

Los dos primeros proyectos demuestra el pin pairing y cómo funciona la librería de XBee en processing y por lo tanto en ese caso el módulo XBee debe estar en modo API. El cuarto proyecto es igual que el segundo pero en lugar de usar un módulo, usa dos módulos. En el tercer proyecto añade un tercer módulo y la entradas analógicas con un potenciómetro y el envío de lecturas cada 100 ms. El último proyecto mezcla lo aprendido en los anteriores y monta un controlador de juegos inalámbrico.

Una mejora que podría incluir el código de processing es sacar por pantalla lo recibido por el módulo XBee, que serviría para hacer debug y aprender un poco más del modo API. Sería sencillo añadir esa funcionalidad por nuestra parte.

También sería interesante añadir a este kit algún ejemplo con lenguajes de programación más usados como python o .NET.

Proyectos Usando Arduino

Los proyectos propuestos en este tutorial para uso de XBee con Arduino son:

Los 4 primeros proyectos son muy parecidos trabajando la comunicación inalámbrica con Arduino, la librería de XBee y los conceptos de cambio de estado de pin y las entradas y salidas de los módulos XBee. El último ejemplo introduce otros conceptos como el de coordinador y RSSI o indicador de fuerza de señal recibida.

Al contrario que tutorial del anterior kit, no se habla casi nada de la parte de cómo funcionan los módulos XBee y cómo interactúan con Arduino. Hay un apartado de trabajando con Arduino http://docs.digi.com/display/XBeeArduinoCodingPlatform/Working+with+Arduino donde se ven unas nociones básicas de Arduino y otra de como instalar la librería xbee-arduino en http://docs.digi.com/display/XBeeArduinoCodingPlatform/Installing+the+xbee-arduino+library, pero no está actualizado a las nuevas versiones del IDE de Arduino, aunque en el enlace al repositorio de github de la librería si lo explica: https://github.com/andrewrapp/xbee-arduino

Un aspecto que sería muy interesante es documentar la librería xbee-arduino explicando que hace cada método de los disponibles, porque sino no nos queda más remedio que ponerse a leer el código de la librería y averiguarlo por tu cuenta.

El código de los ejercicios está disponible en https://github.com/digidotcom/XBeeArduinoCodingPlatform para descargar o hacer fork.

A la hora de hacer los ejercicios, si algo no funciona, es imposible hacer troubleshooting porque no se proporciona una forma de mandar por puerto serie todo lo que le llega de Arduino. Un poco de debug es necesario no solo para ver que puede estar fallando sino para aprender cómo funciona la comunicación entre Arduino y XBee.

Este kit tiene 3 módulos pero sólo es posible hacer ejemplos de comunicación multipunto, pero no es posible hacer esquemas de comunicación mesh, puestos que los módulos del kit no tienen esa funcionalidad.

En este tutorial apenas se ofrece parte teórica, lo que hace que si no hubiera hecho el anterior tutorial me hubiera costado un poco más entender el funcionamiento de los módulos XBee o hacer un acto de fe de que las configuraciones funcionan, pero la parte de la explicación de las conexiones y los proyectos es muy buena.

Los puntos más interesantes del tutorial son:

El objetivo de este kit junto con el manual es aprender más sobre cómo los módulos XBee pueden integrarse fácil y rápidamente con otros elementos (como Arduino o software) para conseguir conectividad inalámbrica y en mi opinión se consigue.

Conclusión

La gran ventaja de uso de los módulos RF XBee frente a otros es la sencillez de uso gracias al potente programa de configuración XCTU. Esto permite aplicar tecnología inalámbrica de forma rápida y sencilla a nuestros proyectos. La desventaja es el precio, son más caros que otros módulos equivalentes como los nRF24.

Con estos kits de aprendizaje se consigue aprender cómo funcionan los módulos XBee y cómo manejarlos. Los tutoriales disponibles en general están muy bien para aprender como si de un curso online se tratara.

Con estos kits he aprendido mucho, pero para los curiosos que nos gusta llegar más al fondo se quedan un poco cortos y he usado este documento http://www.hmangas.com/Electronica/Datasheets/Shield%20XBee%20Arduino/XBee-Guia_Usuario.pdf para profundizar y aclarar algunos conceptos.

Agradecer a Digi Logroño y en especial al Carlos que me hayan prestado este material y poder ampliar mi conocimiento sobre la tecnología XBee y así poder incluirla en mis cursos.

Más información de XBee en mis cursos y talleres de www.aprendiendoarduino.com y en el apartado XBee del curso avanzado de Arduino http://www.aprendiendoarduino.com/arduino-avanzado-2016/

Si quieres saber cuándo publicaré en la web los próximos cursos de XBee y donde los impartiré presencialmente, puedes enterar a través de mi twitter @jecrespom o en la lista de correo de #aprendiendoarduino http://list.aprendiendoarduino.com/mailman/listinfo/aprendiendoarduino.com.noticias

IoT con Arduino

Internet de las cosas (en inglés Internet of things, abreviado IoT) es un concepto que se refiere a la interconexión digital de objetos cotidianos con Internet.

Definición de wikipedia: https://es.wikipedia.org/wiki/Internet_de_las_cosas

Arduino es un elemento que nos permite de forma sencilla y económica conectar cualquier cosa a Internet. Con un Arduino y un sencillo módulo ethernet o wifi podemos conectar a Internet sensores para informar, controlar motores o bombillas desde cualquier parte del mundo o mandar un SMS o email cada vez que se abra la puerta de casa.

Como afecta IoT a nuestro dia a dia: http://socialgeek.co/tecnologia/8-formas-que-the-internet-of-things-impactara-dia-dia

IoT en 5 minutos con Arduino: http://hackaday.com/2016/01/08/internet-of-things-in-five-minutes/

Aplicaciones de IoT: https://temboo.com/iot-applications

Temboo es una plataforma de IoT que nos permite conectar fácilmente mediante una API un Arduino con Internet, mostrar los datos recogidos e interactuar con ellos desde un navegador web.

Una visión del IoT aplicado a la industria es lo denominado como Industria 4.0 o Industria conectada o IIoT que deriva del concepto de M2M (machine to machine) que se refiere al intercambio de información o comunicación en formato de datos entre dos máquinas remotas sin necesidad de conexión a Internet sino que puede ser en una red privada y crear una industria inteligente donde todos los elementos están interconectados y comparten los datos.

Definiciones de wikipedia:

Diferencias entre IoT y M2M: https://www.pubnub.com/blog/2015-01-02-iot-vs-m2m-understanding-difference/

Telefónica y IoT: http://www.thinkingthings.telefonica.com/

El coche autónomo, en el que trabajan grupos como Google, BMW, Volvo o Tesla, es toda una proeza de la robótica.La conducción autónoma se basa en las comunicaciones máquina a máquina (M2M), por las que los vehículos pueden intercomunicarse con las señales, los semáforos y los otros automóviles. Todo esto también tiene mucho que ver con las smart cities.

Elementos que intervienen en el IoT

Explicación gráfica de los elementos necesarios en IoT: http://www.libelium.com/products/meshlium/wsn/

  • Qué quieres medir?
  • Cómo lo quieres conectar?
  • Qué quieres hacer con los datos?

Elementos en IoT:

  • Plataformas Software, para tratar los datos recogidos por nuestros sensores y almacenarlos. Pueden ser plataformas de terceros o plataformas propias desarrolladas por nosotros o simplemente guardar en BBDD propias. Por ejemplo: Carriots, Thingspeak, Temboo, Thinger, etc…
    Además todas estas plataformas SW que están en la nube, deben estar soportadas por un HW de servidores, unas BBDD de gran capacidad y una infraestructura segura que los hospede.
  • Servicios, son los servicios que ofrecen las plataformas como mostrar los datos recogidos, mandar avisos cuando se detecte un evento o la interconexión con otras plataformas o simplemente. Servicios ofrecidos por la plataforma carriots: https://www.carriots.com/que-es-carriots

A modo de resumen, estos son los elementos en el IoT

Sensor — MCU — Comunicación — Protocolo — Plataforma — Servicios

Uno de los retos del IoT es mandar datos de cualquier sensor a través de cualquier protocolo a cualquier plataforma de forma inalámbrica y usando la menor energía posible (baterías) y todo esto mediante una comunicación segura.

IoT con Arduino

Ahora vamos a conectar Arduino a Internet o a cualquier otro tipo de red, es este caso usaremos ethernet y WiFi.

Webserver con Ajax

Mediante Ajax podemos actualizar los datos de la web embebida en Arduino sin necesidad de cargar toda la web, sino solo mandando los datos actualizados, economizando los datos mandados a través de la red.

Ajax:

Ejemplo del webserver anterior que muestra los datos de las entradas analógicas pero con Ajax.

Código: https://github.com/jecrespo/Aprendiendo-Arduino-Proyectos/tree/master/Proyecto_9-Servidor_Web_%20Embebido/EthernetServer-Ajax

Ejemplo avanzado de regulador de encendido con ajax, ejercicio 42: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio42-Ajax

Grabar datos de Arduino en un Ordenador (datalogger)

Con Arduino conectado a una red, se pueden recoger los datos (datalogger) y mandarlos a un servidor (p.e. una Raspberry Pi) y guardarlos en una Base de Datos. Con estos datos almacenados podemos mostrarlos, analizarlos, etc…

Grabar Datos leídos por Arduino en la entrada analógica A0 y grabarlos en una BBDD dentro de una Raspberry Pi o de un servidor público.

Arduino llama a un servicio (p.e. en PHP) pasándole las variables que quiero almacenar y ese servicio se encarga de grabar en la BBDD que puede estar en el mismo servidor.

Métodos POST y GET de HTTP: http://www.w3schools.com/tags/ref_httpmethods.asp

Código: https://github.com/jecrespo/Aprendiendo-Arduino-Proyectos/tree/master/Proyecto_10-Grabar_Raspberry

BBDD: https://qvm602.aprendiendoarduino.com o IP Raspberry Pi

Mandar mensajes de Arduino y visualizarlos en tiempo real

Arduino solicita un nombre y un mensaje que escribimos en el puerto serie y lo manda a un servidor. Desde el servidor vemos los mensajes en tiempo real. Por ejemplo serviría para enviar alarmas a un sistema de monitorización cuando Arduino detecta un evento (pulsar un botón, abrir una puerta, etc…).

Visualizar los mensajes: http://www.aprendiendoarduino.com/servicios/ o IP Raspberry Pi

Código: https://github.com/jecrespo/Aprendiendo-Arduino-Proyectos/tree/master/Proyecto_10-Grabar_Mensajes

Uso de plataformas de IoT con Arduino

Podemos usar de forma gratuita diversas plataformas para conectar nuestro Arduino con ellas y usarlas para mostrar datos, responder a ciertos eventos, realizar acciones, etc…

Algunas plataformas existentes son:

Ejercicios IoT

Plataforma Aprendiendoarduino

Disponemos de una plataforma sencilla de IoT en http://www.aprendiendoarduino.com/servicios/

Desde ella podemos:

El código de arduino para usar cada uno de ellos es:

Todo el código de la plataforma y Arduino en: https://github.com/jecrespo/aprendiendoarduino-servicios

Conexión para enviar datos de temperatura:

Datasheet sonda temperatura: https://www.arduino.cc/en/uploads/Main/TemperatureSensor.pdf

Thingspeak

Mandar datos de temperatura a una plataforma pública como https://thingspeak.com/

Código: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio65-Thingspeak_DHCP

ESP8266

Hacer un sketch para ESP8266 que mande un mensaje de alarma o un SMS cuando se ponga a masa el GPIO2 a masa y mande la recuperación al volver a leer HIGH.

Arduino Sleep Mode

En muchos proyectos donde no es posible tener alimentación eléctrica o de movilidad tenemos que alimentar el microcontrolador mediante batería, para conseguir alimentar un microcontrolador durante mucho tiempo con batería podemos usar el modo sleep donde el consumo se reduce drásticamente.

Ya vimos en el apartado de alimentación Arduino que no tiene sentido alimentar una placa de Arduino con una batería, incluso aunque pongamos la MCU en modo sleep, puesto que los componentes de la placa son altamente ineficientes. Sencillamente porque la eficiencia nunca entró en las especificaciones de diseño, hasta los nuevos modelos.

Normalmente, hay muchos proyectos en los que hay que realizar acciones periódicas de tiempo en tiempo, como tomar lecturas de sensores o enviar mensajes a un servidor central, pero la mayor parte del tiempo nuestro Arduino no está haciendo nada más que un loop continuo.

Sin embargo no hacer nada para un procesador, sigue suponiendo miles de instrucciones por segundo que consumen una energía preciosa, cuando funciona a baterías.

Cuando el microcontrolador entra en modo de suspensión (sleep) por su código, la ejecución del código se detendrá en ese punto. Para reanudar la ejecución de su código, el microcontrolador debe ser despertado desde el modo de suspensión por uno de sus módulos internos de hardware, temporizador expirado, interrupción externa, WDT, etc.

La librería estándar de Arduino, por ahora no permite acceso a las funciones que hacen dormir al MCU (Microcontrolador) y hay que usar la librería AVR directamente o librerías de terceros que nos ayuden a usar el modo sleep.

Para más información ver la documentación del ATmega328p http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf en las página 62

Tenemos en el Atmega328p seis modos de Sleep:

  • Idle
  • ADC Noise Reduction
  • Power-save
  • Power-down
  • Standby
  • Extended Standby

El consumo para cada modo es:

  • SLEEP_MODE_IDLE: 15 mA
  • SLEEP_MODE_ADC: 6.5 mA
  • SLEEP_MODE_PWR_SAVE: 1.62 mA
  • SLEEP_MODE_EXT_STANDBY: 1.62 mA
  • SLEEP_MODE_STANDBY : 0.84 mA
  • SLEEP_MODE_PWR_DOWN : 0.36 mA

Los registros usados para manejar el modo sleep:

  • Sleep Mode Control Register The Sleep Mode Control Register contains control bits for power management. When addressing I/O Registers as data space using LD and ST instructions, the provided offset must be used. When using the I/O specific commands IN and OUT, the offset is reduced by 0x20, resulting in an I/O address offset within 0x00 – 0x3F.
    Name:  SMCR
  • MCU Control Register When addressing I/O Registers as data space using LD and ST instructions, the provided offset must be used. When using the I/O specific commands IN and OUT, the offset is reduced by 0x20, resulting in an I/O address offset within 0x00 – 0x3F.
    Name:  MCUCR
  • Power Reduction Register
    Name:  PRR

Sleep mode select del registro SMCR:

En avr-libc la librería que gestiona el modo sleep es <avr/sleep.h>

Más información:

Librerías Modo Sleep Arduino

La librería más ampliamente usada es la low power:

También hay otras librerías como Enerlib http://playground.arduino.cc/Code/Enerlib o usar las funciones de avr-libc http://www.engblaze.com/hush-little-microprocessor-avr-and-arduino-sleep-mode-basics/

Con la librería lowpower es muy fácil poner en modo sleep a Arduino con una línea como:

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);

Hay tres maneras de despertar un Arduino del modo Sleep:

  • Interrupciones hardware
  • Timers y WDT que es un timer un tanto peculiar
  • UART o puerto serie.

Si por un error de programación dejamos arduino en modo sleep permanente, hay que vaciar la flash de programa y la forma más sencilla es reinstalar reprogramarlo con el blinking led o cualquier otro programa que no incluya instrucciones de dormir.

En este caso estamos usando el Watch Dog Timer y es una interrupción periódica que se dispara, si el contador llega a 0. EL WatchDog Timer acepta diferentes periodos de disparo, de entre una tabla:

  • SLEEP_15MS
  • SLEEP_30MS
  • SLEEP_60MS
  • SLEEP_120MS
  • SLEEP_250MS
  • SLEEP_500MS
  • SLEEP_1S
  • SLEEP_2S
  • SLEEP_4S
  • SLEEP_8S
  • SLEEP_FOREVER

Esta sencilla instrucción (powerdown) es la de menor consumo de energía, pero hay otros modos que pueden mantener encendidos ciertos circuitos del procesador interno del UNO y del Nano y pueden ser útiles:

  • idle
  • adcNoiseReduction   
  • powerDown
  • powerSave
  • powerStandby   
  • powerExtStandby
  • standby

Lista de lo que hace cada modo:

Le pasamos dos parámetros más además del tiempo de despertar, que son:

  • ADC_OFF: Apaga los convertidores Analógico a digital
  • BOD_OFF: Apaga el circuito de Brown Out Detection, que es un circuito para detectar niveles bajos de tensión. Si no fuera por este detector BOD, cuando las pilas se fueran agotando la tensión podría descender de un cierto nivel y dañar los circuitos. Apagarlo ayuda a reducir consumo, pero ciertamente corres otros peligros.

Usar el WatchDog Timer (del que ya hemos hablado) para despertar cada 8 segundos es una solución, sin embargo en un proyecto que requiera tomar lecturas cada media hora, despertar nuestro MCU cada 8 segundos es excesivo, cuando tenemos que estrujar al máximo la capacidad de la batería.

Para solucionar esto hay un modo de dormir nuestro procesador especialmente útil, que es el de duérmete sin fecha de despertar, pero despierta cuando recibas una interrupción, es decir cuando ocurra algo que estamos esperando.

Para ello usamos el ejemplo:

void loop()
   {   	attachInterrupt( 0, ServicioBoton, FALLING);
   	LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
   	detachInterrupt(0);

   	if (n != contador)
      	{     Serial.println(contador);
            	n = contador ;
            	Serial.flush();
      	}
   }

La primera línea del loop() engancha una interrupción llamada ServicioBoton que se dispara cuando usamos el pulsador, y que simplemente incrementa contador, para saber cuántas veces se ha pulsado.

La segunda línea, pone nuestro Arduino a dormir ay va a seguir así hasta que reciba una interrupción. Cuando detecte una interrupción hardware en el pin 2, despierta súbitamente, Incrementa el valor de contador y sigue el ciclo normal del programa, donde se encuentra lo primero con un detachInterrupt, es decir, deshabilitamos las interrupciones hasta que hagamos nuestra tarea, que en este caso son simplemente comprobar si contador (Que se incrementó con la interrupción) es diferente de n (El valor último registrador de contador) y si es así imprime el valor de contador.

Fijaros que no estamos haciendo un debouncing del pulsador. Sería una muy mala idea hacerlo aquí con un delay, porque el tiempo que durase el delay, nuestro procesador está activo y consumiendo y eso es algo que no interesa.

También usamos un flush() al final para asegurarnos de que se vacía la cola.

Ejemplo de reducción de consumo:

Tutorial en 5 partes sobre modo sleep:

También hay otra librería de bajo consumo de JeeLib. Para reducir el consumo al máximo lo mejor es utilizar un microcontrolador solamente y en el skech incluir la librería de bajo consumo JeeLib.

JeeLib es una colección de ficheros de cabecera, clases y sketches de muestra para su uso con el IDE de Arduino. Fue escrito y extendido durante los años para simplificar la experimentación y la exploración del JeeNode y otros productos de JeeLabs.

La clase sleepy es muy sencilla de usar y muy eficaz para hacer sketchs de bajo consumo y su uso está documentada en: http://jeelabs.org/pub/docs/jeelib/classSleepy.html

Más información:

Arduinos Compatibles Bajo Consumo

En el mercado tenemos modelos de Arduino y compatible diseñados para tener bajo consumo y ser alimentados por batería. Combinando este HW con una programación eficiente podemos lograr dispositivos alimentados por batería durante años:

Estas placas están diseñadas para consumir poco y usan elementos eficientes. Como se puede ver son placas que prescinden de elementos innecesarios simplificando mucho las placas.

El arduino UNO es altamente ineficiente puesto que el diodo de protección y el regulador de tensión consumen mucho aunque la MCU esté en modo sleep. Intentar alimentar una placa Arduino UNO con una pila de 9V no es posible si quieres que dure más de un día.

Moteino es una plataforma de desarrollo compatible con Arduino inalámbrico de bajo coste de bajo consumo basada en el popular chip ATmega328p utilizado en Arduinos tradicionales, lo que la hace 100% compatible con el IDE de Arduino. Moteino no incluye un convertidor USB-Serial (como los Arduinos tradicionales), en su lugar es necesario utilizar un adaptador FTDI externo para cargar los sketches, las ventajas son un menor costo y menor tamaño. Sin embargo, hay una variante MoteinoUSB que el convertidor serie integrado. Son dispositivos compatibles con Arduino que utilice el popular transceptor HopeRF RFM69 o el RFM12B más antiguo. Moteino también viene con un chip de memoria flash SPI opcional para programación inalámbrica o registro de datos.

Más información:

Para Arduinos alimentados por batería es interesante usar un dispositivo de medida de voltaje de la batería y saber su capacidad.Esto puede medirse directamente de la batería usando un divisor de tensión:

ZigBee/XBee

ZigBee

IEEE 802.15.4 es un estándar que define el nivel físico y el control de acceso al medio de redes inalámbricas de área personal con tasas bajas de transmisión de datos (low-rate wireless personal area network, LR-WPAN). El grupo de trabajo IEEE 802.15 es el responsable de su desarrollo. También es la base sobre la que se define la especificación de ZigBee, cuyo propósito es ofrecer una solución completa para este tipo de redes construyendo los niveles superiores de la pila de protocolos que el estándar no cubre.

IEEE802.15.4:

El estandar 802.15.4 simplificado: http://catarina.udlap.mx/u_dl_a/tales/documentos/lem/archundia_p_fm/capitulo4.pdf

Bluetooth y RFID se encuentran en el grupo de trabajo 802.15.

ZigBee es el nombre de la especificación de un conjunto de protocolos de alto nivel de comunicación inalámbrica para su utilización con radiodifusión digital de bajo consumo, basada en el estándar IEEE 802.15.4 de redes inalámbricas de área personal (wireless personal area network, WPAN). Su objetivo son las aplicaciones que requieren comunicaciones seguras con baja tasa de envío de datos y maximización de la vida útil de sus baterías.

En principio, el ámbito donde se prevé que esta tecnología cobre más fuerza es en domótica, como puede verse en los documentos de la ZigBee Alliance. La razón de ello son diversas características que lo diferencian de otras tecnologías:

  • Su bajo consumo.
  • Su topología de red en malla.
  • Su fácil integración (se pueden fabricar nodos con muy poca electrónica).

ZigBee utiliza la banda ISM para usos industriales, científicos y médicos; en concreto, 868 MHz en Europa, 915 en Estados Unidos y 2,4 GHz en todo el mundo. Sin embargo, a la hora de diseñar dispositivos, las empresas optarán prácticamente siempre por la banda de 2,4 GHz, por ser libre en todo el mundo. El desarrollo de la tecnología se centra en la sencillez y el bajo costo más que otras redes inalámbricas semejantes de la familia WPAN, como por ejemplo Bluetooth.

Zigbee:

Tabla comparativa de módulos 802.1.4 https://en.wikipedia.org/wiki/Comparison_of_802.15.4_radio_modules

El estándar Zigbee se utiliza primordialmente para aplicaciones domóticas donde es mínima la capacidad de transferencia de información y el costo y consumo tienen un papel fundamental. ZigBee se utiliza para controlar la calefacción, iluminación, sistema de seguridad, etc. de cualquier edificio inteligente. Se espera que ZigBee se aplique para industrias, juguetes, periféricos de PC, componentes electrónicos, sistemas de control automático, medicina, etc. pero en este momento su principal aplicación es en los sistemas de domótica y de automatización.

Zigbee Soporta tres tipos de topologías de red:

  • Star o Estrella: presenta larga vida útil como consecuencia del bajo consumo que requiere.
  • Mesh o Malla: en la cual existen múltiples rutas para alcanzar un destino, obteniéndose alta confiabilidad.
  • Cluster Tree o Racimo de Árbol: es una topología del tipo Mesh-Star que encierra los beneficios de ambas.

Las comunicaciones Zigbee se realizan en la banda libre de 2.4GHz. A diferencia de bluetooth no utiliza FHSS (Frequency hooping), sino que realiza las comunicaciones a través de una única frecuencia, es decir, de un canal. Normalmente puede escogerse un canal de entre 16 posibles. El alcance depende de la potencia de emisión del dispositivo así como el tipo de antenas utilizadas (cerámicas, dipolos, …) El alcance normal con antena dipolo en visión directa suele ser aproximadamente (tomando como ejemplo el caso de MaxStream, en la versión de 1mW de potencia) de 100m y en interiores de unos 30m. La velocidad de transmisión de datos de una red Zigbee es de hasta 256kbps. Por último decir que una red Zigbee la pueden formar, teóricamente, hasta 65535 equipos, es decir, el protocolo está preparado para poder controlar en la misma red esta cantidad enorme de dispositivos. La realidad es menor, siendo, de todas formas, de miles de equipos.

El estándar Zigbee permite la interoperabilidad entre diferentes dispositivos de de diferentes fabricantes. Por ejemplo, nos permitiría controlar luces, sistemas de climatización, riego automático, etc.. de diferentes marcas usando un mismo protocolo con productos certificados Zigbee.

Para lograr esta interoperabilidad entre dispositivos, el protocolo Zigbee está organizado en capas que separan los componentes y las funciones en módulos independientes.

zigbee

Las capas PHY y MAC son las especificadas por el estándar IEEE 802.15.4 y las capa NWK está especificada por el estándar Zigbee que maneja la estructura de red, enrutamiento y seguridad.

La capa de aplicación es la que habilita la interoperabilidad y está compuesta por:

  • APS: ofrece un interfaz entre la red y la capa de aplicación, define los mensajes estandarizados que permite la comunicación entre dispositivos de diferentes fabricantes.
  • Application Framework: es el entorno donde se almacenan las aplicaciones en los dispositivos ZigBee.
  • ZDO (ZigBee Device Object): ofrece funcionalidades de descubrimiento de dispositivos y gestión avanzada de red.

Más información: http://www.electrocomponentes.com/articulos/diciembre06/zigbee.html

XBee

XBee es el nombre comercial del Digi de una familia de módulos de comunicación por radio y están basados en el estándar zigbee, pero digi tiene muchos Xbee y algunos son zigbee estándar y otros son propietarios o modificaciones del estándar. Existen muchos módulos Xbee basados en el estándar IEEE 802.15.4

Los módulos Xbee han sido diseñados para aplicaciones que requieren de un alto tráfico de datos, baja latencia y una sincronización de comunicación predecible. Por lo que básicamente XBee es propiedad de Digi basado en el protocolo Zigbee. En términos simples, los XBee son módulos inalámbricos fáciles de usar.

Xbee es el nombre comercial de la familia de módulos de radio con un factor de forma compatible de Digi International

Los módulos de radio XBee pueden ser usados con un número mínimo de conexiones: Power (3.3V), GND y TX/RX de la UART, junto con otras conexiones recomendables como reset y sleep. La mayoría de los módulos XBee tienen otras conexiones omo flow control, input/output (I/O), analog-to-digital converter (A/D) and indicator

Los módulos XBee funcionan a 3.3V y los pines no son tolerantes a 5V. Desde Arduino podemos alimentar un módulo XBee, pero la comunicación serie en Arduino es a 5V y en el módulo XBee es a 3.3V.

Se puede usar un divisor de tensión como este:

Es posible usar un módulo adaptador de 5V a 3.3V como este https://www.parallax.com/sites/default/files/downloads/32401-XBee-5V-3.3V-Adapter-v1.2.pdf

XBee:

La familia de módulos de radio XBee es:

  • XBee 802.15.4 — The initial point-to-point topology or star topology module running the IEEE 802.15.4 protocol
  • XBee-PRO 802.15.4 — A higher power, longer range version of the XBee 802.15.4
  • XBee DigiMesh 2.4 — A 2.4 GHz XBee module that uses DigiMesh, a sleeping mesh networking protocol developed by Digi International
  • XBee-PRO DigiMesh 2.4 — A higher power, longer range version of the XBee DigiMesh 2.4
  • XBee ZB — An XBee module that incorporates the ZigBee PRO mesh networking protocol
  • XBee-PRO ZB — A higher power, longer range version of the XBee ZB
  • XBee ZB SMT — A surface mount XBee running the ZigBee protocol
  • XBee-PRO ZB SMT — A higher power, longer range version of the XBee ZB SMT
  • XBee SE — An XBee ZB module that incorporates the security cluster for the ZigBee Smart Energy public profile
  • XBee-PRO SE — A higher power, longer range version of the XBee SE
  • XBee-PRO 900HP — A 900 MHz XBee-PRO module with up to 28 mile range with high-gain antenna that supports DigiMesh networking protocol
  • XBee-PRO 900 (Legacy) — A 900 MHz proprietary point-to-point and star topology module, not recommended for new design
  • XBee-PRO XSC (S3B) — A 900 MHz module compatible over the air with the Digi 9XStream radios
  • XBee-PRO DigiMesh 900 (Legacy) — A 900 MHz module that uses DigiMesh, not recommended for new design (see XBee-PRO 900HP for new designs)
  • XBee-PRO 868 — An 868 MHz 500 mW long-range module that supports proprietary point-to-point and star, for use in Europe
  • XBee 865/868LP — An 868 MHz XBee module that uses DigiMesh, available in Surface Mount form-factor (also configurable to 865 MHz for use in India)
  • XBee ZigBee (S2C) — Incorporates an upgrade to the transceiver chip, replacing the Silicon Labs EM250 with the Silicon Labs EM357, effectively adding more RAM, more flash, faster clock speed and lowering the current draw.[11]
  • XBee-PRO ZigBee (S2C) — A higher power, longer range version of the XBee ZigBee (S2C)

Nuevos módulos de XBEE SMD para Europa: http://www.digi.com/pdf/ds_xbee868lp.pdf. Son los 868LP operan en la frecuencia 863-870 MHz utilizando 20 canales para ofrecer más referencia e inmunidad en 868 MHz.

Kits de aprendizaje de XBee de Digi:

Los módulos más sencillos de Xbee son los serie 1 (también llamada 802.15.4) que no soportan mesh e implementan el estándar 802.15.4. Son los más fáciles de usar y los más recomendados para empezar a trabajar. Más información: http://www.digi.com/pdf/ds_xbeemultipointmodules.pdf y ejemplo de uso: http://examples.digi.com/get-started/basic-xbee-802-15-4-chat/

Para usar un módulo XBee con Arduino es necesario un Shield o un adaptador para conectar el puerto serie de XBee con el de Arduino.

Para usar o configurar un módulo XBee con Ordenador es necesario un adaptador que generalmente tiene un chip FTDI que hace de pasarela entre el puerto serie y el USB.

XBee explorer:

Guía de uso de XBee Shield: https://learn.sparkfun.com/tutorials/xbee-shield-hookup-guide

Buena página para saber más de XBee: http://xbee.cl/

Guía de compra de XBee

Digi dispone de una amplia variedad de módulos con diferentes características para diferentes funciones/aplicaciones. A la hora de elegir, esta tabla de comparación puede ser útil: http://www.digi.com/pdf/chart_xbee_rf_features.pdf

Y en este enlace también explica perfectamente como elegir nuestro módulo XBee: http://docs.digi.com/display/XBeeArduinoCodingPlatform/XBee+buying+guide

Los parámetros que afectan a la hora de elegir un módulo en función de la aplicación son:

  • El país de uso, las frecuencias están reguladas en cada país y debe tenerse en cuenta.
  • Alcance de la señal. Para un mayor rango será necesario seleccionar una antena o usar un módulo XBee-PRO
  • Consumo del módulo
  • Topología de la red de nuestra aplicación.

Guia de compra de módulos XBee de sparkfun: https://www.sparkfun.com/pages/xbee_guide

Resellers de Digi para compras on-line:

Para cualquier duda o ayuda, se puede recurrir al foro de digi en http://www.digi.com/support/forum/

Arquitectura Básica de una Red XBee

Una red XBee la forman básicamente 3 tipos de elementos. Un único dispositivo Coordinador, dispositivos Routers y dispositivos finales (end points). Los módulos XBee son versátiles a la hora de establecer diversas topologías de red, dependiendo la serie de XBee que escojamos pueden crearse redes

El Coordinador: Es el nodo de la red que tiene la única función de formar una red. Es el responsable de establecer el canal de comunicaciones (como hablábamos antes) y del PAN ID (identificador de red) para toda la red. Una vez establecidos estos parámetros, el Coordinador puede formar una red, permitiendo unirse a él a dispositivos Routers y End Points. Una vez formada la red, el Coordinador hace las funciones de Router, esto es, participar en el enrutado de paquetes y ser origen y/o destinatario de información.

Los Routers: Es un nodo que crea y mantiene información sobre la red para determinar la mejor ruta para enrutar un paquete de información. Lógicamente un router debe unirse a una red Zigbee antes de poder actuar como Router retransmitiendo paquetes de otros routers o de End points.

End Device: Los dispositivos finales no tienen capacidad de enrutar paquetes. Deben interactuar siempre a través de su nodo padre, ya sea este un Coordinador o un Router, es decir, no puede enviar información directamente a otro end device. Normalmente estos equipos van alimentados a baterías. El consumo es menor al no tener que realizar funciones de enrutamiento.

Los módulos XBee son versátiles a la hora de establecer diversas topologías de red, dependiendo la serie de XBee que escojas puedes crear redes:

  • Punto a punto
  • Estrella
  • Malla
  • Árbol
  • Mixtas

Módulos XBee

Los módulos XBee de digi son pequeños módulos RF (radio frecuencia) que transmiten y reciben datos sobre el aire usando señales de radio. La capacidad inalámbrica es esencial cuando se quieren instalar sensores en lugares donde no hay cables.

Los módulos XBee son altamente configurables y soportan múltiples protocolos para permitir usarlo tanto en un enlace punto a punto como en un diseño complejo con muchos dispositivos en una red mesh.

Algunos ejemplos de uso de los módulos XBee:

  • Controlar un robot remotamente
  • Añadir inteligencia a una casa edificio sin tener que realizar cableados
  • Para aplicaciones industriales, por ejemplo para monitorizar temperatura, presión o máquinas complejas.

Más información en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#concepts/c_introduction_to_xbee_modules.htm%3FTocPath%3D_____3

Las series

XBee Series 1 (también llamados XBee 802.15.4)  – Son la serie más fácil para trabajar, no necesitan ser configurados, pero incluso así se pueden obtener beneficios de estos módulos. Debido a que son fáciles para trabajar, son los más recomendables especialmente si se está empezando. Para comunicaciones Punto-a-Punto, estos módulos trabajan tan bien como los de la Serie 2, pero sin todo el trabajo de pre configuración previo. El hardware de las Series 1 y las Series 2/2.5/ZB NO SON COMPATIBLES. No trates de mezclarlos, no funcionará.

Guia de usuario serie 1:

http://www.hmangas.com/Electronica/Datasheets/Shield%20XBee%20Arduino/XBee-Guia_Usuario.pdf

XBee Znet 2.5 (Formalmente Series 2) Retirado – Los módulos Serie 2 deben ser configurados antes de ser usados. Pueden funcionar en modo Transparente o por medio de comandos API, pero todo esto depende de que firmware se configure en los módulos. También pueden funcionar en una red mesh. Son más difíciles que usar que los de la Serie 1. No existe una forma en que estos módulos sean compatibles con los de la Serie 1. Los módulos Znet 2.5 ya no se venden, pero han sido reemplazados con módulos ZB más compatibles. Datasheet

https://www.sparkfun.com/datasheets/Wireless/Zigbee/XBee-2.5-Manual.pdf

XBee ZB (el actual módulo Series2) – Básicamente es el módulo Znet 2.5, pero con un nuevo firmware. Esto significa que también funcionan en modo transparente o por medio de comandos API. También funcionan en redes mesh. Estos a menudo son llamados módulos de Serie 2, por lo que si escuchas a alguien hablar sobre esta serie, probablemente estén hablando de estos módulos. Puede que no sea el término correcto, pero se hace distinción de estos con los módulos de la Serie 1, los cuales son los más populares.

XBee 2B (el imás actual módulo Series2) – Son nuevos módulos que poseen mejoras en el hardware respecto de los de la Serie 2, básicamente son los mismo que los anteriores pero con un firmware más nuevo, mejorando por ejemplo el uso de la potencia. Funcionan con el Firmware del módulo ZB, pero debido al cambio de hardware, ya no pueden funcionar con el firmware del módulo Znet 2.5. Por lo que ten cuidado si agregas uno de estos módulos a una red ya existente que utilice módulos Znet 2.5. Actualmente algunas tarjetas son 2B y otras son ZB.

Básicamente el problema es mezclarlos.

900 MHz vs 2.4GHz – La mayoría de los módulos XBee operan a 2.4 GHz , pero hay unos pocos que operan a 900 MHz. Básicamente los de 900 MHz pueden llegar muy lejos con una antena de alta ganancia (hasta casi 24 Km). Además a menor frecuencia, la señal posee mayor penetración. Otro punto importante es que los módulos de 900 MHz no están permitidos en algunos países,  Digi tiene versiones de 868 MHz que sí está permitido en la mayoría de los países.

Documentación módulos XBee:

Más información http://xbee.cl/tutorial-xbee/

Regulaciones en paises: http://www.digi.com/resources/certifications

Toda la documentación de digi: http://www.digi.com/resources/documentation/digihelp/default.htm

XCTU

Para configurar y usar los módulos XBee es necesario descargar e instalar XBee Configuration and Test Utility (XCTU) que es un software multiplataforma que permite interactuar con los módulos mediante un interfaz gráfico. Esta aplicación incluye herramientas que hacen muy sencillo configurar y probar los módulos XBee.

XCTU nos sirve para configurar, inicializar, actualizar firmware y testear los módulos XBee, comunicándose por puerto serie a los módulos. Una ventaja de este software es que puedes ver rápidamente un resumen de todos los parámetros del módulo y una descripción de ellos.

Más información y descarga de XCTU: http://www.digi.com/products/xbee-rf-solutions/xctu-software/xctu

XCTU User Guide: http://www.digi.com/resources/documentation/digidocs/90001458-13/default.htm

Drivers para los módulos: http://www.digi.com/support/productdetail?pid=3257&type=drivers

XCTU

Para empezar a usarlo es tan simple como instalar el software XCTU, conectar el módulo RF a la placa de desarrollo, que tiene un módulo FTDI que saca un interfaz USB y luego enchufar el USB a nuestro PC. Para usarlo con USB es necesario poner el jumper en la posición UART en lugar de la de loopback.

Más información de la placa de desarrollo de XBee: https://docs.digi.com/display/XBeeHardware/XBee+Grove+Development+Board

Tutorial XCTU: http://docs.digi.com/display/XBeeArduinoCodingPlatform/XCTU+walkthrough

Cómo configurar los módulos: http://docs.digi.com/display/XCTU/Configure+your+modules

Primera Aplicación. Chat Básico

El primer ejemplo es un chat básico para transmitir en tiempo real mensajes por el aire con los módulos XBee.

Ejercicio completo: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_basic_chat.htm%3FTocPath%3DBasic%2520chat%7C_____0

Para configurar un módulo XBee con el XCTU, lo primero es poner el modo configuración y descubrir los módulos, seleccionando el puerto COM del USB al que he conectado la placa de desarrollo.

Para poder ver el módulo remoto es necesario configurar los parámetros DH y DL de la MAC del módulo remoto.

NOTA: Si se conectan los módulos en las placas de desarrollo y se alimenta mediante batería, los módulos trabajan de forma autónoma, por ejemplo recogiendo datos de un sensor y mandandolo al nodo central

XCTU2

Cómo se comunican los dispositivos XBee

Los dispositivos XBee se comunican entre ellos a través del aire enviando y recibiendo mensajes. Estos dispositivos no pueden gestionar los datos enviados o recibidos, sin embargo pueden comunicarse con otros dispositivos a través del interfaz serie.

Los dispositivos XBee transmiten al aire los datos que llegan del puerto y transmiten al puerto serie cualquier dato que llega por el aire. Los microcontroladores o los PCs pueden controlar que envían los dispositivos XBee y gestionan los mensajes inalámbricos entrantes.

Por lo tanto tenemos dos tipos de comunicación en los dispositivos XBee:

  • Comunicación inalámbrica: es la comunicación entre los módulos XBee, estos módulos deben ser parte de la misma red y usar la misma frecuencia de radio.
  • Comunicación serie: es la comunicación entre el módulo XBee y el microcontrolador o el PC a través de un puerto serie.

En la comunicación inalámbrica los módulos transmiten y reciben información a través de la modulación de las ondas electromagnéticas. Para que se realice la transmisión ambos módulos deben estar en la misma frecuencia y en la misma red. Esto se determina por dos parámetros:

  • Channel (CH) es la frecuencia usada para comunicar, es decir, el canal dentro de la red.
  • Personal Area  Network Identifier (ID) es un identificador único que establece que los módulos están en la misma red.

Un módulo XBee solo recibirá y transmitirá datos a otros XBee dentro de las misma red (mismo ID) y usando el mismo canal (mismo CH).

Direccionamiento

Los módulos de XBee tienen varias direcciones diferentes, cada una con un propósito.

Type Example Unique
64-bit 0013A20012345678 Always
16-bit 1234 Yes, but only within a network
Node identifier Bob’s module Uniqueness not guaranteed

Cada módulo de XBee tiene una dirección única de 64 bits, esta dirección se llama MAC y es análogo a la MAC de las tarjetas de red o wifi. El valor de 64 bits está compuesto por los parámetros Serial Number High (SH) y Serial Number Low (SL), que aparecen impresos en la parte trasera del módulo. El valor SH es generalmente el mismo para todos los módulos XBee (0013A200) e identifica los módulos de Digi. La dirección 000000000000FFFF está reservada para mandar un mensaje de broadcast.

Una dirección de 16 bit puede ser asignada a cada módulo XBee y no es única. Este valor puede leerse o escribirse a través del parámetro MY. Si el valor es FFFF se deshabilita la recepción de paquetes con direcciones de 16 bit.

El identificador de nodo es un string corto que permite identificar fácilmente un módulo con un nombre. Para leer o escribir el node identifier se hace a través del parámetro NI.

Comunicación serie

Un módulo XBee puede funcionar como un módulo independiente o conectado a un microcontrolador o PC. Cuando opera como módulo independiente simplemente manda datos al nodo central de los sensores o dispositivos conectados a los puertos del módulo. Cuando está conectado a un microcontrolador o un PC, el módulo XBee usa la comunicación serie. En el caso de Arduino, este manda a través del puerto serie datos al módulo XBee para que sean transmitidos por el aire.

El módulo XBee hace de interface con el microcontrolador a través de la UART (puerto serie asíncrono). Para más información ver las guías de usuario:

Un arduino o el dispositivo externo conectado a uno módulo XBee mediante puerto serie puede tener varios modos de operación en función de cómo se comunican por el puerto serie. Los módulos XBee soportan dos modos de operación:

  • Modo Transparente (Aplicación transparente). La radio pasa la información tal cual la recibe por el puerto serie. Este modo tiene funcionalidades limitadas pero es la forma más sencilla de comenzar.
  • Modo API (Aplicación de programación). En este caso un protocolo determina la forma en que los datos son intercambiados. Este modo permite hacer una red de comunicaciones más grande.

Ventajas y desventajas de un modo u otro: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#concepts/c_xbee_comparing_at_api_modes.htm%3FTocPath%3DHow%2520XBee%2520devices%2520work%7CWireless%2520communication%7CSerial%2520communication%7C_____2

Más información en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_how_xbees_work.htm%3FTocPath%3DHow%2520XBee%2520devices%2520work%7C_____0

Modo Transparente

En modo transparente el módulo XBee funciona de forma que todo lo recibido por el puerto serie es enviado inmediatamente al aire y todo lo recibido por la radio es se manda tal cual al puerto serie.

En modo transparente para comunicar dos módulos, es necesario configurar la dirección del destino en el módulo que envía datos. Esta dirección se programa en los parámetros:  Destination Address High (DH) y Destination Address Low (DL).

El modo transparente tiene muchas limitaciones, por ejemplo al trabajar con varios módulos es necesario configurar la dirección de destino antes de mandar un mensaje. Sin embargo, el modo transparente es perfecto cuando hay comunicación punto a punto entre solo dos elementos.

En modo transparente podemos usar el modo comando que es un estado en que los caracteres enviados al módulo XBee son interpretados como comandos en lugar de transmitirlos vía radio. Para cambiar a modo comando hay que mandar el string “+++”, cuando el módulo recibe un segundo de silencio seguido del string “+++” (sin /n o /r) y otro segundo de silencio, deja de mandar datos por radio y comienza a aceptar comandos locales. Al entrar en modo comando si transcurren 10 segundo sin recibir datos automáticamente sale de modo comando y vuelve a modo transparente. Para salir del modo configuración usar ATCN.

El propósito del modo comando es leer o escribir la configuración local del módulo XBee. Para ello se usando los comandos AT (https://es.wikipedia.org/wiki/Conjunto_de_comandos_Hayes) . Un comando AT comienza con las letras “AT” seguida de dos caracteres que identifican el comando a ejecutar y algunos otros parámetros opcionales.

Por lo tanto desde un Arduino sería muy sencillo configurar un módulo XBee en lugar de usar el interfaz gráfico XCTU.

Más información en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_xbee_transparent_mode.htm%3FTocPath%3DXBee%2520transparent%2520mode%7C_____0

Modo API

El modo API ofrece un interfaz estructurado donde los datos son comunicados a través del interfaz serie en paquetes organizados y en un determinado orden. Esto permite establecer una comunicación compleja entre módulos sin tener que definir un protocolo propio.

Por defectos los módulos XBee trabajan en modo transparente, pero esto tiene unas limitaciones:

  • Si un módulo necesita transmitir mensajes a módulos diferentes debe cambiar la configuración para establecer el nuevo destino.
  • Para leer y escribir la configuración del módulo hay que entrar primero en modo comando.
  • En modo transparente un módulo XBee no puede distinguir el origen de la comuinicación que recibe.

Para resolver estas limitaciones XBee da la alternativa del modo API (Application Programming Interface).

Ventajas del modo API:

  • Configurar módulos locales y remotos en la red y sin necesidad de entrar en modo comando.
  • Comunicar con uno o varios destinatarios
  • Identificar el módulo que ha mandado la comunicación
  • Recibir el estado de la transmisión de los paquetes
  • Obtener la fuerza de la señal de los paquetes recibidos
  • Hacer gestión y diagnóstico de la red.
  • Hacer funciones avanzadas domo actualización de firmware remota.

Estructura de la Trama

En modo API los datos enviados están estructurados en una trama. Se envían a través de interfaz serie de XBee y contiene el mensaje inalámbrico e información adicional como la calidad de la señal.

La estructura de la trama es:

Start

delimiter

Length Frame data Checksum
Frame

type

Data
1 2 3 4 5 6 7 8 9 n n+1
0x7F MSB LSB API

frame

type

Frame-type-specific data Single

byte

API frame type determina el tipo de trama e indica cómo está organizada la información de los datos enviados. Los tipos de tramas soportadas dependen del tipo de módulo de Xbee que usemos. Más información para lo módulos usados en página 85 de http://ftp1.digi.com/support/documentation/90000982.pdf

Checksum ayuda a comprobar la integridad de los datos mandados. Las tramas enviadas por puerto serie con checksum incorrecto nunca serán procesadas por el módulos y serán ignoradas.

Para configurar el modo AP, se modifica el parámetro AP. Permite seleccionar entre los dos modos API soportados y el modo por defecto que es el transparente.

Mode AP value Description
Transparent 0 API modes are disabled and the module operates in transparent mode
API 1 1 API mode without escaped characters
API 2 2 API mode with escaped characters

La diferencia ente API 1 y API 2 es que las tramas usan caracteres de escape. Puesto que la transmisión por el aire solo es del payload, dos módulos con diferente configuración API 1 y API 2 pueden comunicarse sin problemas.

El modo AP non-escaped (API 1) se basa únicamente en el delimitador de inicio y la longitud de los bytes para diferenciar las tramas, si los bytes en un paquete se pierden, la cuenta de bytes será incorrecta y el siguiente paquete (trama) también se perderá. API escaped (API 2) implica secuencias de caracteres de escape en una trama para mejorar la fiabilidad especialmente en entornos ruidosos.

La estructura de la trama es básicamente la misma en ambos modos API pero en API 2, todos los bytes excepto el delimitador de inicio deben estar escapados si es necesario. Los siguientes bytes de datos deben ser escapados en modpo API 2:

  • 0x7E: Start delimiter
  • 0x7D: Escape character
  • 0x11: XON
  • 0x13: XOFF

El modo API 2 garantiza que todos los bytes 0x7E recibidos son delimitadores de inicio, este carácter no puede ser parte de cualquier otro campo de la trama (longitud, datos o checksum), puesto que debe estar escapado. Para escapar un carácter, insertar 0x7D (carácter de escape) y añadirlo con el byte a ser escapado (XOR con 0x20). En modo API 2 la longitud no incluye los caracteres de escape y el checksum es calculado con los datos no escapados.

Configurar un módulo local XBee en modo API

Para consultar o modificar un valor de configuración de un módulo local (módulo conectado directamente al puerto serie), es necesario usar los comandos AT. Son los mismos comandos usados en modo transparente pero incluyendolo en una trama de tipo comando AT (0x08 o 0x09) y la respuesta vuelve en una trama de tipo respuesta AT (0x88).

Ejemplo: Configurar un módulo local en modo API. http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_sending_frames.htm

Cuando un módulo está en modo API, al usarlo con el SW XCTU y entrar en la consola, la vista es diferente a si está en modo transparente. Además desde consola hay una utilidad para generar una trama con el frame generator.

Configuración Remota de un módulo XBee

Trabajar en modo API nos permite configurar un módulo remoto al que estamos conectados. Cualquier comando AT usado en local se puede usar en un módulo remoto. Para ello se manda una Remote AT Command Request (0x17) al módulo remoto.

La respuesta del módulo remoto al comando AT es Remote AT Command Response (0x97) con el resultado del comando procesado por el módulo remoto. Si el frame ID del  Remote AT Command es0, esta respuesta no es enviada.

Transmitir y recibir datos inalámbricamente en modo API

Esta imagen resume el intercambio de información en los puestos serie de los módulos.

  1. Se manda por el puerto serie una Petición de transmisión (0x10) o un Explicit Addressing Command Frame (0x11) al módulo XBee A.
  2. El módulo XBee A transmite por el aire los datos de la trama al módulo configurado como destino, en este caso el módulo XBee B
  3. El módulo remoto B recibe los datos por el aire y manda por su puerto serie una trama de paquete recibido (0x90) o Explicit Rx Indicator (0x91), dependiendo del valor de API Options (AO). Esta trama contiene los datos recibidos por el aire y la dirección de origen del módulo que la ha transmitido, en este caso XBee A.
  4. El módulo remoto B transmite un paquete de wireless acknowledge con el estado al XBee A
  5. El módulo XBee A manda un paquete Transmit Status (0x8B) a través de su puerto serie con el estado de las transmisión.

Ejemplo: Transmitir y recibir datos en modo API http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_transmit_and_receive_data_with_xctu.htm (El tipo de trama es un Tx Request 0x00 del protocolo 802.15.4 y no como pone en la práctica un Transmit Request 0x10 del protocolo ZigBee)

Más información en http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_api_mode.htm

Librerías Xbee

Para poder manejar el modo API disponemos de librerías que nos ayudan a generar la trama a enviar y a interpretar la trama recibida. Estas librerías nos ayudan a interpretar las tramas de API.

Seguridad y cifrado

Los módulos XBee pueden ser configurados para comunicación segura mediante claves de cifrado.

Los datos son cifrados antes de ser enviados y luego en el receptor son descifrados para poder usarlos. Activar la seguridad en una red XBee puede producir un ligero incremento en latencia y tamaño de los paquetes enviados.

Para habilitar la comunicación segura se deben configurar los siguientes parámetros con el mismo valor en todos los dispositivos de la red:

  • EE = 1: AES Encryption Enable
  • KY: AES Encryption key que debe ser un string de 32 caracteres hexadecimales. Una vez configurado este valor es imposible recuperarlo.

El cifrado es de 128 bits AES.

Ahorro de energía

Los módulos XBee tienen capacidades de ahorro de energía. los módulos se pueden poner en estado sleep y apenas consumir energía, pudiendo llegar a una duración de batería de varios años.

El protocolo 802.15.4 contiene cuatro conductas básicas para el modo sleep que se puede dividor en dos categorías: pin-controlled sleep mode y cyclic sleep mode. Por defectos el modo sleep está siempre deshabilitado.

Los parámetros básicos para configurar en modo sleep son:

  • SM (Sleep Mode)
  • ST (Time before sleep)
  • SP (Cyclic sleep period)

Modo pin-controlled sleep: este modo es controlado por el Sleep_RQ (pin 9) de forma que cuando es puesto a HiGH (3.3 V) entra en modo sleep. (SM = 1)

Modo Cyclic Sleep mode: el módulo se despierta y vuelve a modo sleep en con una programación fija basada en el tiempo. Con SM = 4 se activa el modo y con SM = 5 además de activar el modo cíclico, permite activar a través del pin 9, siendo una mezcla de ambos modos. En estos dos modos se debe configurar los parámetros ST y SP.

Para comunicación bidireccional en modos sleep, los protocolos ZigBee o DigiMesh pueden dar funcionalidades más apropiadas.

Los pines de los módulos relacionados con el modo sleep son el pin 9 que pone el módulo en modo sleep cuando está a HIGH (3.3V) y el pin 13 es una salida que se pone a HIGH cuando está despierto o a LOW cuando está como sleep, este pin se puede conectar a un led o a una entrada del microcontrolador.

Para más información ver guía de usuario del correspondiente módulo: http://ftp1.digi.com/support/documentation/90000982.pdf

Más información en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_explore_low_power.htm

Ejercicio: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#tasks/t_ex_enable_sleep.htm

Comandos AT

Para entrar en modo comando a un XBee hay que poner la cadena “+++” y esperar a que nos devuelva un “OK” el módulo. El propósito es leer o cambiar la configuración del módulo XBee.

Cuando se quiere leer o escribir una configuración, se debe enviar un comando AT con la siguiente estructura:

Comandos AT:

  • AT: comprueba la conexión con el módulo
  • ATCN: se sale del módulo de comandos.
  • ATWR: Escribe la configuración actual a la memoria no volátil y persiste después de iniciar de nuevo el módulo.

Todos los comandos para la serie 1 pueden verse en la guia de usuario: http://ftp1.digi.com/support/documentation/90000982.pdf

Más información en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#concepts/c_at_commands.htm%3FTocPath%3DXBee%2520transparent%2520mode%7CCommand%2520mode%7C_____1

Pin Pairing

Todos los módulos XBee tienen un conjunto de pines que pueden usarse como entradas y salidas sin necesidad de conectar un microcontrolador externo.

Pin pairing se refiere a la comunicación directa de los pines entre dos módulos XBee. Esto enlaza virtualmente uno de los pines del XBee directamente con a los pines de otro XBee, por ejemplo para un timbre inalámbrico.

Los módulos de la serie 1 tienen 9 pines de I(O, seis de los cuales pueden leer valores analógicos.

Ejercicio completo en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#reference/r_pin_pairing.htm%3FTocPath%3DXBee%2520transparent%2520mode%7CPin%2520pairing%7C_____0

Aclaraciones:

  • Al poner el parámetro MY a FFFF habilita la recepción de paquetes de direcciones 64 bits deshabilitando la recepción de paquetes de direcciones de 16 bit.
  • Señalar que el parámetro I/O Input Address (IA) enlaza una salida de XBee a una dirección de 64 bit específica. Esto significa que las salidas digitales de un XBee configuradas con el parámetro IA sólo aceptarán cambios de las peticiones que lleguen de la dirección configurada. De ahí la necesidad de configurar este parámetro en el XBee_B.
  • En el ejercicio no explica bien porque poner el parámetro IC a 10. La explicación completa está en: http://www.digi.com/resources/documentation/Digidocs/90001456-13/tasks/t_obtain_data_from_sensor.htm

NOTA IC = 0x10 = B10000

DIO12 DIO11 DIO10 DIO9 DIO8 DIO7 DIO6 DIO5 DIO4 DIO3 DIO2 DIO1 DIO0
0 0 0 0 0 0 0 0 1 0 0 0 0

Ejercicio: cómo obtener datos de un sensor conectado a un módulo XBee: http://www.digi.com/resources/documentation/Digidocs/90001456-13/tasks/t_obtain_data_from_sensor.htm

Ejemplo Pin Pairing para manejar un joystick

Para manejar un joystick y controlar remotamente un elemento inalámbricamente podemos usar el pin pairing de forma sencilla.

La configuración de los módulos XBee usando direcciones de 16 bits para simplificar quedaría:

Param Joystick Follower Effect
CH C C Define la frecuencia a usar. Debe ser la misma en ambos módulos.
ID 2015 2015 Define la red a la que se conecta la radio. Debe ser la misma en ambos módulos.
DH 0 DH y DL forma la dirección de destino. Es donde las notificaciones son mandadas cuando cambia el valor del pin. Se configura como dirección corta de 16 bits.
DL 5678
MY 1234 5678 Define la dirección corta de 16 bits.
D1 DI [3] DO Low [4] Configura el pin DIO1/AD1 en el joystick como entrada y en el follower como salida. Lo que ocurre en el joystick como entrada se transmite al follower como salida.
D2 DI [3] DO Low [4] Configura el pin DIO2/AD2 en el joystick como entrada y en el follower como salida.
D3 DI [3] DO Low [4] Configura el pin DIO3/AD3 en el joystick como entrada y en el follower como salida.
D4 DI [3] DO Low [4] Configura el pin DIO4/AD4 en el joystick como entrada y en el follower como salida.
IC 1E Configura en el joystick que pines monitorizar para mandar la señal al detectar un cambio. 00011110 (binario) = 1E (hexadecimal)
IA 1234 Defines the address of the transmitting module (leader) to pair for I/O.

Los pines I/O de Xbee son: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#reference/r_xbee_s3b_io_pins.htm

Con esta simple configuración ya puedo manejar remotamente con un joystick unas luces o incluso un coche con las conexiones adecuadas.

Modelos de Comunicación

Existen dos tipos de comunicación y en ambos casos es bidireccional:

  • Punto a punto
  • Punto a multipunto

Punto a punto

Para que se establezca la comunicación los módulos deben estar en el mismo canal (CH) y en la misma Network ID (ID), además para iniciar la comunicación es necesario saber la dirección MAC de 64-bit del destinatario. La forma en que se comunican dos módulos punto a punto depende si están en modo API o transparente.

Ejemplo de comunicación punto a punto en modo API: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_ex_chat.htm

Punto a multipunto

En este modelo un módulo puede comunicarse con un módulo o múltiples módulos que estén en la misma red. Esta comunicación implica un nodo central coordinador con varios nodos remotos (end devices) conectándose al nodo central.

En el protocolo 802.15.4 los módulos XBee tienen dos roles:

  • Coordinator, es el nodo central de la red. Inicia la red y permite a otros dispositivos conectarse, puede seleccionar la frecuencia del canal y hace la sincronización de la red. Para configurar un nodo como coordinador hay que cambiar el parámetro CE (Coordinator Enable) a 1.
  • End device, es un nodo remoto de la red. Puede comunicarse con el coordinador y con otros end devices de la red. Se puede poner en modo sleep.

Las redes mesh solo son soportadas en protocolo ZigBee o Digimesh.

Es posible hacer broadcast, es decir, mandar el mismo datos a todos los nodos posible de la red. Para mandar un broadcast, la dircción de destino debe ser 000000000000FFFF.

En el coordinador, cambiado el parámetro A2 es posible asociar end devices sin que conozcan el canal (CH) y el ID de red (ID) siendo inicializados por el coordinador.

Más información: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#concepts/c_comm_models.htm

I/O en módulos XBee

Todos los módulos XBee tienen un conjunto de pines que pueden usarse para conectar sensores o actuadores y configurarlos. A diferencia de un Arduino, estos módulos no pueden ejecutar un programa para interaccionar, sino que es necesario un elemento externo como un Arduino para programarlos o cambiar su comportamiento.

Con estos pines se puede recoger el dato de un sensor y mandarlo a otro módulo o encender una luz al estar conectado a un relé cuando recibe la señal sobre un pin configurado como salida.

Pines I/O para módulos XBee 802.15.4

Pin name Physical pin # Parameter
DIO0, AD0 20 D0
DIO1, AD1 19 D1
DIO2, AD2 18 D2
DIO3, AD3 17 D3
DIO4, AD4 11 D4
DIO5, AD5 15 D5
DIO6 16 D6
DIO7 12 D7
DI8 9 D8
PWM0 6 P0
PWM1 7 P1

(D = digital; I = input; O = output; AD = analog input; PWM = pulse-width modulation)

Leer Sensores

Los módulos XBee se pueden usar leer datos de una red de sensores. Para recibir los datos es necesario configurar los módulos remotos para escuchar en el pin donde se conecta el sensor y mandar los datos al módulo principal que estará conectado a una MCU o un ordenador.

En función del tipo de sensor o actuador que conectemos al pin del módulo deberemos configurarlo adecuadamente.

Hay dos formas de obtener datos de un sensor:

  • Haciendo una consulta para que lea todos los pines habilitados como entradas. Queried sampling (IS)
  • Transmitir automáticamente los datos del sensor de forma periódica o cuando un pin digital cambia. El parámetro IR configura la frecuencia con que se mandan los datos leídos de los sensores. El parámetro IC configura que pines monitorizar para detección de cambio, cuando los pines monitorizados detectan un cambio inmediatamente se leen los sensores y se mandan los datos.

Para seleccionar qué pines monitorizar, se asigna un valor binario a IC basado en este patrón:

DIO12 DIO11 DIO10 DIO9 DIO8 DIO7 DIO6 DIO5 DIO4 DIO3 DIO2 DIO1 DIO0
0 0 0 0 0 0 0 0 0 0 0 0 0

Ejercicios en:

Controlar Dispositivos

Un módulo XBee es capaz de recibir comandos y poner una salida digital o analógica a un valor al ocurrir un determinado evento, sin el uso de un microcontrolador externo.

Los módulos XBee 802.15.4 tienen 8 salidas digitales (D0 a D7) y pueden configurarse con una resistencia de pull up o pull down. Los módulos también tienen salida analógica mediante PWM, en el caso de los módulos XBee 802.15.4 tienen 2 salidas analógicas (P0 y P1)

Recordar que los módulos XBee van a 3.3 V.

Para mandar una actuación a un módulo hay que mandar un comando AT configurado de la manera correcta para actuar sobre un pin, una vez se ha configurado anteriormente como salida.

Ejercicios en:

Más información: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_inputs_and_outputs.htm

Señal y Rangos de Frecuencia

La distancia de alcance de la señal de los módulos XBee está afectada por diversos factores:

  • Algunos materiales pueden reflejar las ondas de radio provocando interferencias. En particular materiales metálicos.
  • Las ondas de radio pueden ser absorbidas por objetos en su camino
  • Las antenas pueden ajustarse para incrementar la distancia.
  • La línea de visión puede ayudar a incrementar la fiabilidad de la señal.

RSSI (Received Signal Strength Indicator) es el indicador de la cantidad de energía presente en una señal de radio. Midiendo la fuerza de la señal en la antena receptora es una forma de determinar la calidad del enlace de comunicación. Su valor se mide en dBm cuanto mayor sea el valor negativo la señal es más débil. Por lo tanto -50dBm es mejor que -60 dBm.

El pin 6 de los módulos XBee puede ser configurado como salida PWM con el valor de RSSI, para ello configurar P0 como RSSI [1]. También en el parámetro DB se guarda el valor de RSSI del último paquete recibido expresado en valor decimal.

Además del valor de RSSI, es conveniente medir la fiabilidad del enlace en porcentaje de paquetes perdidos, puesto que en entornos con interferencias el valor de RSSI puede ser alto pero el enlace será malo.

Antenas

A la de lograr un determinado alcance de un módulo, es importante la antena, para ello los módulos RF XBee disponen de distintos tipos de antenas:

  • PCB antenna: está formada en la propia PCB con trazo conductor.

  • Cable de antena integrada: es un pequeño cable de 80mm colocado perpendicularmente a la PCB y soldado directamente a la PCB

  • Whip antenna: una antena sólida pero flexible que sobresale 25mm del módulo. Esta antena aumenta el rango sobre la antena del chip pero en el exterior.

  • U.FL antenna: es un pequeño conector para una antena externa. Es una buena opción si el módulo está en una caja y queremos poner la antena en el exterior.

  • RP-SMA antenna: es un conector mayor para una antena externa.

Prueba de Rango

Una prueba de rango nos sirve para medir la calidad del enlace entre dos módulos XBee. XCTU permite hacer una prueba de rango que supone enviar paquetes desde un módulo local a uno remoto esperando el echo, contabilizando el número de paquetes enviados y recibidos y midiendo el RSSI.

Como hacer una prueba de rango: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#tasks/t_try_range_test.htm

Más información: http://www.digi.com/resources/documentation/Digidocs/90001456-13/Default.htm#containers/cont_signal_strength.htm

XBee Gateways

Hemos visto cómo hacer una red inalámbrica con los módulos XBee y como hay un intercambio de información entre los módulo. Ahora bien, si esos datos los queremos sacar fuera de esa red inalámbrica necesitaremos un gateway que nos saque esos datos a una red ethernet (Internet/Intranet) o a un bus de campo standard donde poder almacenar y mostrar los datos y estados de la red Xbee.

XBee to IP Gateway

Este gateway habilita la conexión remota, configuración y gestión de las redes Xbee con redes IP. Todos los datos Xbee enviados al gateway están automáticamente disponibles para aplicaciones on line via Device Cloud. Este gateway puede ejecutar aplicaciones python que comuniquen y gestionen la red XBee.

Más información: http://www.digi.com/products/xbee-rf-solutions/gateways/xbee-gateway

XBee RF Modems

Los módems XBee RF se usan para comunicar con sistemas que usen RS-232, RS-485 o interfaz USB.

Más información: http://www.digi.com/products/xbee-rf-solutions/modems/xbee-pro-900hp-rf-modems

Interacción Software – Xbee

Todo el código está disponible en:

Los proyectos propuestos en este tutorial para interacción de XBee con software, en este caso con processing son:

Librería XBee Java: https://github.com/digidotcom/XBeeJavaLibrary

Instalar la librería XBee java http://docs.digi.com/display/XBeeArduinoCodingPlatform/Installing+the+xbjlib+Processing+librarypara que una aplicación de processing pueda comunicarse con un modulo XBee

XBee y Arduino

Instalar librería XBee Arduino http://docs.digi.com/display/XBeeArduinoCodingPlatform/Installing+the+xbee-arduino+library

Librería: https://github.com/andrewrapp/xbee-arduino

Trabajando con Arduino: http://docs.digi.com/display/XBeeArduinoCodingPlatform/Working+with+Arduino

Primer proyecto http://docs.digi.com/display/XBeeArduinoCodingPlatform/Stop-It%21+LED+Game

Todo el código está disponible en:

Los proyectos propuestos en este tutorial para uso de XBee con Arduino son:

HW para Arduino

Los dos kits lleva los módulos Serie 1:

XBee Shields:

El Xbee Explorer:

Poner en marcha un Xbee shield:

XBee en la industria: http://www.digi.com/industries

Librerías Arduino

Manejar Librerías Arduino

Las librerías son trozos de código hechas por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y permite la abstracción haciendo que nuestro programa sea más sencillo de hacer y de entender. En este apartado también veremos cómo escribir o modificar librerías.

Disponemos de infinidad de librerías a nuestra disposición para facilitarnos el trabajo, todas ellas son open source y disponemos de su código.

Las librerías normalmente incluyen los siguientes archivos comprimidos en un archivo ZIP o dentro de un directorio. Estas siempre contienen:

  • Un archivo .cpp (código de C++)
  • Un archivo .h o encabezado de C, que contiene las propiedades y métodos o funciones de la librería.
  • Un archivo Keywords.txt, que contiene las palabras clave que se resaltan en el IDE (opcional).
  • Muy posiblemente la librería incluye un archivo readme con información adicional de lo que hace y con instrucciones de como usarla.
  • Directorio denominado examples con varios sketchs de ejemplo que nos ayudará a entender cómo usar la librería (opcional).

La distintas formas de instalar librerías se puede ver en https://aprendiendoarduino.wordpress.com/2016/06/27/librerias-3/

Para usar una librería que acabamos de instalar, lo que hay que hacer es leer la documentación de esa librería si es que está disponible y luego leer y probar los ejemplos que dispone la librerías.

Pero ahora que ya sabemos manejar clases y objetos, si queremos entrar a fondo en una librería para saber usarla, podemos abrir el fichero del encabezado (.h) y ver las propiedades y métodos, ver si hereda de otra librería, etc… y luego incluso ver cómo funciona la propia librería leyendo el código en el fichero .cpp e incluso si nos atrevemos añadir nuevos métodos o modificar un método que nos interese.

Problemas comunes con las librerías: https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use/common-library-problems

Con el nuevo gestor de librerías es mucho más fácil gestionar las librerías instaladas así como su versión y actualización, algo que antes había que hacer manualmente

El listado de librerías se actualiza de internet y se guarda en:

  • C:\Users\usuario\AppData\Local\Arduino15\library_index.json

Las placas se actualiza de internet (del gestor de tarjetas) y se guarda en:

  • C:\Users\usuario\AppData\Local\Arduino15\package_index.json

El fichero library_index.json se actualiza al abrir el IDE y en caso de tener problemas, se puede borrar y se genera de nuevo al abrir el IDE.

Es importante darse de alta en github puesto que nos permite estar al día de las novedades en las librerías y recibir notificaciones cada vez que haya una modificación en las mismas. Además podemos fácilmente actualizar nuestro repositorio de librerías con el comando PULL de git y poner una u otra versión en nuestro repositorio con el comando checkout.

Librería Time y Timezone

Como ejemplo para aprender a usar una librería, veamos las librerías Time y Timezone.

Para aprender a manejarlas, simplemente leer el fichero readme que viene en el repositorio de github y luego los ejemplos.

Algunas funciones librería Time:

  • hour();            // the hour now  (0-23)
  • minute();          // the minute now (0-59)
  • second();          // the second now (0-59)
  • day();             // the day now (1-31)
  • weekday();         // day of the week, Sunday is day 0
  • month();           // the month now (1-12)
  • year();            // the full four digit year: (2009, 2010 etc)
  • hourFormat12();    // the hour now in 12 hour format
  • isAM();            // returns true if time now is AM
  • isPM();            // returns true if time now is PM
  • now();             // returns the current time as seconds since Jan 1 1970
  • setSyncProvider(getTimeFunction);  // set the external time provider
  • setSyncInterval(interval);         // set the number of seconds between re-sync

Algunas funciones librería Timezone:

  • time_t toLocal(time_t utc); Converts the given UTC time to local time, standard or daylight as appropriate.
  • TimeChangeRule myRule = {abbrev, week, dow, month, hour, offset};
    • abbrev is a character string abbreviation for the time zone; it must be no longer than five characters.
    • week is the week of the month that the rule starts.
    • dow is the day of the week that the rule starts.
    • hour is the hour in local time that the rule starts (0-23).
    • offset is the UTC offset in minutes for the time zone being defined.

Ejemplos:

  • TimeChangeRule usEDT = {“EDT”, Second, Sun, Mar, 2, -240};  //UTC – 4 hours
  • TimeChangeRule usEST = {“EST”, First, Sun, Nov, 2, -300};   //UTC – 5 hours

Una vez visto el manual de la librería, veamos cómo está escrita la librería Time de Arduino que nos ofrece funcionalidades para mantener la fecha y hora con un hardware externo o sin él. Nos permite obtener la fecha y hora como: segundo, minuto, hora, día, mes y año. También da el tiempo con el tipo de dato del estándar C time_t, siendo sencillo el cálculo del tiempo transcurrido.

Existe una nueva versión de la librería Time cuyo código está derivado librería DateTime del Arduino Playground pero está actualizada para ofrecer una API que es más flexible y fácil de usar.

Más información de la librería time: http://www.prometec.net/time-arduino/

La librería Time no requiere de ningún HW especial. Internamente depende de la función millis() de Arduino para llevar un registro del tiempo transcurrido. El tiempo se puede sincronizar con varios tipos de HW como GPS, NTP, RTC, etc…

Veamos cómo está escrita la librería. El código lo encontramos en https://github.com/PaulStoffregen/Time. En el readme.txt explica con detalle el uso de la librería. Explica las funciones disponibles, los ejemplos dentro del directorio examples y el funcionamiento interno de la librería. también hay un fichero keywords.txt con las palabras clave que se van a resaltar en el IDE.

También pueden verse los ficheros:

  • library.json – Información de la librería
  • library.properties – datos de la librería y clasificación que sale en el Gestor de Librerías

Los ficheros que componen la librería son:

  • Time.h — Simplemente incluye a TimeLib.h, se hace por temas de compatibilidad
  • TimeLib.h — Fichero de cabecera de la libreria Time.
  • Time.cpp — código de la librería time
  • DateStrings.cpp — contiene los strings de las fecha en inglés, pero se podría hacer una versión modificada para español.

Fichero TimeLib.h:

  • #ifdef __cplusplus permite a la sección del programa que está dentro ser compilado solo si la macro especificada como parámetro de ha definido. en este caso __cplusplus es una macro definida cuando el compilador de c++ está en uso. Esto se usa para comprobar si una cabecera está compilada bajo C o C++
  • #include <inttypes.h> incluye la cabecera de inttypes.h que está en C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include, cuyo propósito es proveer un conjunto de tipos de enteros cuyas definiciones sean consistentes en todas las máquinas e independientes de sistemas operativos. Define una serie de macros para usar con printf y scanf, así como funciones para trabajar con el tipo de dato intmax_t que es un entero con el máximo de anchura soportado. Ver http://www.cplusplus.com/reference/cstdint/
  • Se define el tipo de dato http://www.cplusplus.com/reference/ctime/time_t/ del estandar C++ que no dispone el avr libc y se “simula” la librería time.h de C++ que no tiene implementada AVR.
  • Se definen una serie de variables enumeradas con typedef enum y typedef struct
  • Se definen unas macros para convertir los años de tmYear a un valor de 4 dígitos
  • El valor de domingo es 1 como se puede ver en:
    typedef enum {dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday} timeDayOfWeek_t; y como lo define en la macro #define dayOfWeek(_time_)
  • Se definen luego las funciones y se puede ver que hay sobrecarga en las funciones. Por ejemplo en la función setTime que puede ajustar el tiempo pasando un time_t o los datos de hora, minuto, segundo, día, mes y año.

Ficheros Time.cpp y DateStrings.cpp:

  • La función más importante es now() que calcula el numero de segundo transcurridos desde la última llamada a now()
  • En la función now() es donde comprueba si toca sincronizar de nuevo la hora con (nextSyncTime <= sysTime)
  • En la variable sysTime se guarda la hora, es una variable privada.
  • Se definen primero las funciones públicas en Time.cpp y luego las funciones de bajo nivel.
  • Para sincronizar la hora externamente se definen las funciones:
    • setSyncProvider(getTimeFunction);  // set the external time provider
    • setSyncInterval(interval);         // set the number of seconds between re-sync
  • En DateStrings.cpp está la definición de los strings de fechas. Los strings los guarda en la memoria flash para no ocupar memoria RAM. Para ello usa PROGMEM y si no es para AVR, define el PROGMEM y las funciones de progmem de AVR.
  • Se modifican las funciones que devuelven el string del día y del mes pasando el entero con el número de día o mes.

Adicionalmente tenemos otras librería:

La librería Time no dispone de ajuste de hora por zona horaria (time zone o TZ) ni ajuste de DST (Daylight Saving Time): https://en.wikipedia.org/wiki/Daylight_saving_time. Se podría añadir estas funcionalidades a la librería Time o se puede crear una nueva librería que implemente estas funcionalidades y haga uso de la librería Time.

Para solucionar esta carencia disponemos de la librería Time zone https://github.com/JChristensen/Timezone. Esta librería no es accesible desde el gestor de librerías, por lo que habrá que hacer una instalación manual de la misma.

La librería Timezone está diseñada para trabajar en conjunto con la librería Time y debe ser referenciada en el sketch que se use con timezone. Esta librería convierte el Universal Coordinated Time (UTC) a la hora local correcta incluso si hay Daylight Saving Time (DST). La hora puede obtenerse de un GPS, NTP server o un RTC.

La librería Timezone implementa dos objetos para facilitar la conversión de zona:

  • Un objeto TimeChangeRule que describe cuando la hora local cambia de hora estándar a hora de verano y viceversa.
  • Un objeto Timezone que usa TimeChangeRule para hacer las conversiones y las funciones relacionadas. También puede escribir y leer de la EEPROM el TimeChangeRule. Es posible implementar varias zonas horarias definiendo varios objetos Timezone.

Para establecer la TimeChangeRule se hace mediante dos reglas por zona, una para definir cuando comienza el horario de verano y otra cuando comienza el horario estándar. En España el cambio de horario se produce el último domingo de octubre a las 3.00 y el último domingo de marzo a las 2.00 (https://es.wikipedia.org/wiki/Horario_de_verano_en_el_mundo)

Definir un Timezone:

  • TimeChangeRule SDT = {“SDT”, Last, Sun, Mar, 2, 60};  //Spain Daylight Time UTC + 0 hours
    TimeChangeRule SST = {“SST”, Last, Sun, Oct, 3, 180};   //Spain Standard Time UTC + 1 hours
  • Timezone spainTZ(SDT,SST)

Métodos asociados:

  • toLocal(time_t utc) — Convierte la hora obtenida en UTC a hora local

Ficheros librería:

  • Timezone.h
    • Implementa compatibilidad con IDEs Arduino anteriores a version 1.x comprobando la versión de ARDUINO
    • Incluye la librería Time
    • Define las constantes de los nombres con enum y el número de inicio. Define como día 1 el Domingo en lugar del Lunes
    • TimeChangeRule es un struct con el nombre (abbrev), 4 bytes para: semana, día de la semana, mes y hora y un entero para el offset de la zona horaria
    • Clase Timezone con dos constructores, 7 métodos públicos, 2 métodos privados y 6 variables privadas.
  • Timezone.cpp
    • __AVR__ es una macro que indica si el compilador puede usar procesadores AVR y en ese caso uso la librería EEPROM
    • Puedo construir un objeto Timezone con el par de reglas DST o si la tengo guarda en la EEPROM, pasando la dirección donde la tengo guardada.
    • calcTimeChanges() calcula el nuevo dst start y std start al llamarlo.
    • Al usar toLocal() ya autocalculo las fechas de dst start y std start. Ver ejemplo https://github.com/JChristensen/Timezone/blob/master/examples/Clock/Clock.pde

Más información del uso de la librería Timezone en:

NOTA: la librería timezone no soporta leapsecond https://es.wikipedia.org/wiki/Segundo_intercalar pero podría implementarse en la librería. Ver http://stackoverflow.com/questions/9168970/arduino-leap-second-clock

Crear Librerias Arduino

Las librerías son trozos de código hechos por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y permite la abstracción haciendo que nuestro programa sea más sencillo de hacer y de entender. En este apartado veremos cómo escribir o modificar librerías.

Librerías Arduino: https://www.arduino.cc/en/Main/Libraries

El IDE de Arduino incluye una serie de librerías ya instaladas: https://www.arduino.cc/en/Reference/Libraries

Listado de librerías del playground de Arduino: http://playground.arduino.cc/Main/LibraryList, pero existen otras muchas librerías creadas por usuarios o por los fabricantes de HW para facilitar el uso de esos dispositivos con Arduino.

Este tutorial explica como crear una librería: http://arduino.cc/en/Hacking/LibraryTutorial. Explica cómo convertir la función morse en en una librería.

Ejemplo morse.ino

int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

Para convertir en una librería de código morse, vemos que hay dos funciones dot() y dash() para iluminar un led durante 250 ms y 1 segundo y  una variable que es ledPin que determina que pin usar. Este es un estilo de programación clásico usando funciones en lugar de objetos.

Para una librería se necesitan al menos dos ficheros:

  • Un fichero de cabecera con la extensión .h. Este fichero tiene las definiciones de la librería, básicamente un listado de todo lo que hay dentro de la librería
  • Un fichero fuente con la extensión .cpp. Este fichero el que contiene el código

En este caso la librería se va a llamar morse y generamos un fichero de cabecera llamado morse.h.

Veamos el código de morse.h donde se define la clase Morse donde tiene una línea por cada función o método y también una línea por cada variable o propiedad de la clase.

class Morse
{
  public:
    Morse(int pin);	//constructor
    void dot();
    void dash();
  private:
    int _pin;
};

Una clase es una colección de funciones (métodos) y variables (propiedades) que se guardan todas juntas en un solo lugar. Las funciones pueden ser públicas (public), es decir, pueden llamarse por quien usa la librería o privadas (private), es decir, que solo pueden llamarse desde dentro de la propia clase. Todas las clases tienen una función llamada constructor, que es usada para crear una instancia de la clase. El constructor tiene el mismo nombre que la clase y no tiene tipo de variable de devolución.

En el fichero de cabecera de una librería es necesario la declaración #include que de acceso a los tipos y constantes estándar del lenguaje de Arduino (esto se añade automáticamente en los sketches pero no a las librerías). Esta declaración debe ponerse antes de la definición de la clase. La declaración debe ser:

  • Versión IDE 1.x: #include “Arduino.h”
  • Versión IDE 0.x: #include “WProgram.h”

También es común poner todo el fichero de cabecera entre estas instrucciones:

#ifndef Morse_h
#define Morse_h
// the #include statment and code go here…
#endif

Esto evita problemas si alguien accidentalmente incluye dos veces la librería, lo que provocaría un error de compilación. A esto se llama guardián de inclusión múltiple o include guard

// Guardián de inclusión múltiple
#ifndef FICHERO_YA_INCLUIDO
#define FICHERO_YA_INCLUIDO

Así se evita que un compilador poco sofisticado abra otra vez el mismo conjunto de ficheros cuando se incluye un fichero de cabecera dos o más veces. Puede darse el caso de no poner las inclusiones en el inicio de un fichero.

La directiva #include existe en dos versiones. En una se pone el nombre de fichero entre comillas, en la otra entre paréntesis angulares (el signo menor y mayor como “comillas”).

#include “fichero_con_comillas.h”
#include <fichero_entre_menor_y_mayor.h>

La versión con los paréntesis angulares busca los ficheros en todos los directorios que se han especificado en la llamada al compilador – normalmente con la opción “-I”. Estos directorios se suelen rastrear por el fichero incluido en el orden en que aparecen en la línea de comando.

Cuando se incluye un fichero entre comillas, entonces el compilador busca este fichero primero en el mismo directorio que el fichero actualmente compilado y después en los demás directorios. Es decir, la versión con comillas se diferencia de la versión con paréntesis angulares únicamente por buscar primero en el directorio del fichero compilado. Tras no encontrarlo ahí actaa igual.

Cuando se crea una librería se debe documentar poniendo un comentario al comienzo de la librerías con el nombre, breve descripción, quien la ha escrito, fecha, licencia, etc…

El fichero de cabecera queda:

/*
  Morse.h - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/
#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Una vez hecho el fichero de cabecera hay que codificar el fichero fuente Morse.cpp

Primero deben ponerse las declaraciones, esto da al resto de código acceso a las funciones estándar de Arduino y a las definiciones del fichero de cabecera:

#include “Arduino.h”
#include “Morse.h”

Lo siguiente es poner el constructor de la clase. Esto define que ocurre cuando se crea una instancia de la clase. En este caso el usuario debe especificar cual es el pin que se va a usar. Configuramos el pin como salida y los guardamos en una variable privada para usarlo desde otras funciones.

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

El código “Morse::” antes del nombre de la función indica que la función es parte de la clase Morse. Esto se ve en todas las funciones de la clase. La variable llamada “_pin” es una variable privada tal y como se ha definido en el fichero de cabecera y se pone el simbolo “_” delante por convención para indicar que es privada y para diferenciarlo del argumento de la función, pero puede llamarse de cualquier forma mientras coincida con la definición en el fichero de cabecera.

Después de definir el constructor, se deben definir las funciones o métodos de la clase. Son las funciones que se habían definido anteriormente en el sketch:

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

También es habitual añadir el comentario del fichero al principio del fichero. El fichero Morse.cpp queda de la siguiente forma:

/*
  Morse.cpp - Library for flashing Morse code.
  Created by David A. Mellis, November 2, 2007.
  Released into the public domain.
*/

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

De esta forma ya tenemos una librería completa. Ahora para incluirla en nuestro IDE debemos crear un directorio Morse dentro del subdirectorio “libraries” del directorio de nuestro entorno de trabajo definido en las propiedades del IDE. Copiar Morse.h y Morse.cpp dentro del directorio Morse y abrir o reiniciar el IDE de Arduino. A partir de este momento veremos nuestra librería disponible en el IDE y podremos incluirla en los sketches con la declaración #include <Morse.h>. La librería será compilada por los sketches que la usen.

El anterior sketch quedaría ahora sustituido por:

#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Podemos ver que primero se llama a la declaración de la librería Morse. Esto hace que la librería esté disponible en el sketch y lo incluye en el código enviado a la placa Arduino, lo que hace que si la librería es muy pesada, ocupe mucha más memoria nuestro sketch y si no voy a usar una librería es mejor no incluirla para ahorrar espacio.

También observamos que creamos una instancia de la clase Morse llamada “morse”. Al ejecutar esta línea el constructor de la clase es llamado pasando un argumento, creando así el objeto “morse” en nuestro sketch. Luego podemos llamar a los métodos dot() y dash() precedidos del prefijo morse del nombre del objeto.

Es posible tener múltiples instancias de la clase Morse, cada una un pin diferente guardado en la variable privada “_pin”.

Si creamos una librería es conveniente crear el fichero keywords.txt dentro del directorio Morse. De esta forma conseguiremos resaltar las palabras clave que definamos en el fichero keywords. En cada línea del fichero keywords.txt se indica el nombre de la palabra clave y seguido por un tabulador, el tipo de keyword.

  • Las clases deben ser del tipo KEYWORD1 que se resaltan en naranja.
  • Las funciones deben ser del tipo KEYWORD2 que se resaltan en marrón

Para que el fichero keywords.txt se aplique al IDE es necesario reiniciar el IDE.

El fichero keywords.txt quedaría:

Morse KEYWORD1
dash KEYWORD2
dot KEYWORD2

También es aconsejable ofrecer ejemplos de uso de la librería para que los posibles usuarios sepan usarla. Esto se hace creando un directorio “examples” dentro del directorio Morse y añadir en el subdirectorio los sketches de ejemplos que serán visibles desde el IDE.

La librerías morse con los ejemplos y el fichero keywords.txt se puede descargar en: http://www.arduino.cc/en/uploads/Hacking/Morse.zip

Para más información sobre la creación de librerías con un buen “estilo Arduino”, ver la Guía de Estilo de API. Guia de estilo para escribir librerías: http://arduino.cc/en/Reference/APIStyleGuide

Ver ejemplos simples de librerías:

Más información:

Guía de estilo para escribir una librería de Arduino

Para facilitar el entendimiento de las librerías o APIs de Arduino y hacer que el entorno de Arduino sea más homogéneo, desde arduino.cc dan unas pautas para escribir librerías al “estilo Arduino”. Algunas de estas prácticas van en contra de la programación programación profesional, pero esto hace posible a los principiantes comenzar a trabajar de forma sencilla con Arduino.

  • Se amable con el usuario. Tener una modelo claro del concepto que va tratar la librería y las funciones que se van a implementar.
  • Organizar las funciones pública alrededor de los datos y funcionalidades que el usuario quiere. Frecuentemente el conjunto de comandos para un módulo electrónico son demasiado complicado, pero podemos reorganizar las funciones desde un punto de vista de uso a alto nivel. Pensar en lo que hace la mayoría de las personas y organizar la API en función de ese uso. Un ejemplo es la librería https://github.com/adafruit/Adafruit-BMP085-Library. La función readPressure() hace todos los pasos para obtener la presión final, siendo transparente para el usuario todos los complejos pasos intermedios. Esto abstrae al usuario no solo de los comandos I2C necesarios, sino de de los cálculos intermedios.
  • Usar palabras claras para escribir la librerías. Usar nombres de funciones y variables claros que expresen claramente lo que son o hacen y no usar términos técnicos.
  • Evitar palabras que tengan diferente significado para el público en general.
  • Documentar todo. Al escribir ejemplos usar esta guía: http://www.arduino.cc/en/Reference/StyleGuide
  • Usar las librerías del core de Arduino y su estilo:
    • Usar read() para leer entradas y write() para escribir salidas
    • Usar las librerías Stream.h y print.h cuando se estén manejando byte streams. Si no es posible al menos intentar usar su modelo de API.
    • Para aplicaciones de redes, usar las librerías de Client y Server como base.
    • Usar begin() para inicializar unas instancia de una librería, normalmente con unos parámetros de configuración. Usar end() para finalizarla.
  • Usar funciones camel case, no con guión bajo. Por ejemplo analogRead en lugar de analog_read. Esto es una adopción de processing por motivos de facilidad la lectura.
  • No usar nombre de constantes largos que son difíciles de leer.
  • Evitar usar argumentos booleanos. En su lugar es preferible ofrecer dos funciones diferentes con nombres que describan la diferencia entre ellas.
  • No asumir conocimiento de punteros. Los principiantes en C suelen encontrar dificultades con el uso de & y *, en la medida de lo posible tratar de evitar el uso de punteros.
  • Al usar la comunicación serie de cualquier tipo, permitir al usuario especificar el objeto de stream en lugar de solo “Serial”, esto hará la librería compatible con los los puertos del Arduino Mega y Due y también poder usar interfaces alternativos como el SoftwareSerial. ver el uso de begin(Stream &serial) en las librerías https://github.com/firmata/arduino y https://github.com/andrewrapp/xbee-arduino
  • Cuando se escriba una librería que ofrezca comunicación byte-stream heredada de la la clase Stream de Arduino, de forma que la librería pueda ser usada por otras librerías que acepte objetos Stream: si es posible, el método read() inmediatamente accede a los datos del buffer sin esperar a que lleguen más datos y si es posible el método write() debe guardar los datos al buffer de transmisión, pero debe esperar si el buffer no tiene suficiente espacio para guardar inmediatamente todo los datos salientes. La función yield() debe llamarse mientras se espera. De esta forma mantenemos compatibilidad con el restos de librerías Stream.

Unos ejemplos de librerías que definen realmente bien las funciones de alto nivel son:

Viendo los métodos públicos que definen es muy fácil entender que es lo que hacen.

Otro buen ejemplo de abstracción de la librerías wire (I2C) se puede ver en: https://github.com/adafruit/RTClib donde con una serie de métodos obtengo los datos del RTC siendo totalmente transparente para el usuario el uso de la librería wire.

Es frecuente que nos encontremos que algunas librerías se quedan cortas para algunas funcionalidades avanzadas, puesto que como se ha visto en la guía de estilo se tiende a simplificar. Un ejemplo de esto es la librería ethernet donde muchas de las funcionalidades del Wiznet 5100 no están implementadas en la librería, pero esto no impide que nosotros podamos añadirlas.

Más información: https://www.arduino.cc/en/Reference/APIStyleGuide

Modificar librerías

Es habitual que al usar una librería necesitemos una propiedad o un método que no esté implementado o queramos modificar el comportamiento, para ello podemos modificar localmente la librería que tengamos instalada en nuestro IDE.

En el caso que queramos colaborar en una librería hecha, podemos hacer un fork de esa librería en nuestra cuenta de github y añadir una nueva funcionalidad o corregir ese fallo e incluso podemos hacer un pull request para que se añada en la librería original nuestra modificación. También podemos tener nuestra propia librería como un fork de la librería original.

Un pull request es una petición que el propietario de un fork de un repositorio hace al propietario del repositorio original para que este último incorpore los commits que están en el fork. Explicación de pull request: http://aprendegit.com/que-es-un-pull-request/

Para modificar la librería time y añadir una funcionalidad, lo primero es hacer un fork del código https://github.com/PaulStoffregen/Time en tu repositorio, en mi caso en https://github.com/jecrespo/Time y luego clonarlo en el ordenador para modificarlo.

Ejercicios Librerías

Modificar Librería Ethernet

Ver como averiguar la IP de quien se ha conectado y  modificar librería ethernet para implementar un método que de devuelva la IP de origen.

Librería ethernet modificada en https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Otros/Ethernet%20Modificada

Crear una librería para NTP

Ejercicio: A partir del ejemplo de Arduino para obtener la hora de un servidor NTP https://www.arduino.cc/en/Tutorial/UdpNtpClient, crear una librería para obtener de forma sencilla la hora con una llamada a la función readNTP().

Ver Ejercicio39-NTP para obtener fecha y hora en Arduino sin necesidad de un RTC usando NTP: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio39-NTP

Basado en:

Protocolo:

Las firmas de tiempo que se usan en NTP, son de 32 bits indicando la parte entera en segundos desde  el 1 de Enero de 1900, y una parte fraccionaria también de 32 bits. Por ello la resolución teórica de NTP sería de 232 segundos =136 años, y una resolución teórica de 2E-32 segundos, o sea 0,233 nanosegundos.

Nota: Ojo al llamar al constructor de algo p.e. ethernet sin hacer el begin ethernet antes.

Solución: https://github.com/jecrespo/simpleNTP  

Funciones definidas por usuario

Una función es un bloque de código que tiene un nombre y un conjunto de instrucciones que son ejecutadas cuando se llama a la función. Son funciones setup() y loop() de las que ya se ha hablado.

Las funciones de usuario pueden ser escritas para realizar tareas repetitivas y para reducir el tamaño de un programa. Segmentar el código en funciones permite crear piezas de código que hacen una determinada tarea y volver al área del código desde la que han sido llamadas.

Las funciones se declaran asociadas a un tipo de valor. Este valor será el que devolverá la función, por ejemplo ‘int’ se utilizará cuando la función devuelva un dato numérico de tipo entero. Si la función no devuelve ningún valor entonces se colocará delante la palabra “void”, que significa “función vacía”

Sintaxis:

tipo nombreFunción (parámetros) {

  instrucciones;

}

Para llamar a una función, simplemente:

nombreFunción(parámetros);

En una función que devuelve un valor siempre debe tener la instrucción Return, este termina una función y devuelve un valor a quien ha llamado a la función: http://arduino.cc/en/Reference/Return

Ventajas del uso de funciones:

  • Ayuda a tener organizado el código.
  • Una función codifica una tarea en un lugar de nuestro sketch, así que la función solo debe ser pensada una sola vez.
  • Reduce la probabilidad de errores al modificar el código.
  • Hacen que el tamaño del sketch sea menor porque el código de la función es reutilizado.
  • Facilita la lectura del código.
  • Hace más sencillo reutilizar código en otros sketches.

Más información: http://arduino.cc/en/Reference/FunctionDeclaration

Nombres de funciones

Generalmente los nombres de las funciones deben ser en minúscula, con las palabras separadas por un guión bajo, aplicándose éstos tanto como sea necesario para mejorar la legibilidad.

“mixedCase” (primera palabra en minúscula) es aceptado únicamente en contextos en donde éste es el estilo predominante con el objetivo de mantener la compatibilidad con versiones anteriores.

En el caso de las clases, los nombres deben utilizar la convención “CapWords” (palabras que comienzan con mayúsculas).

Las funciones en Arduino pueden estar dentro del mismo fichero .ino o en otro fichero con extensión .ino dentro del directorio del sketch.

Paso por Valor y Paso por Referencia

Hasta ahora siempre hemos declarado los parámetros de nuestras funciones del mismo modo. Sin embargo, éste no es el único modo que existe para pasar parámetros.

La forma en que hemos declarado y pasado los parámetros de las funciones hasta ahora es la que normalmente se conoce como “por valor”. Esto quiere decir que cuando el control pasa a la función, los valores de los parámetros en la llamada se copian a “objetos” locales de la función, estos “objetos” son de hecho los propios parámetros.

int funcion(int n, int m) { 
  n = n + 2; 
  m = m - 5; 
  return n+m; 
}

int a = 10;
int b = 20;
Serial.println(funcion(a,b));
Serial.println(funcion(10,20));

Empezamos haciendo a = 10 y b = 20, después llamamos a la función “funcion” con las objetos a y b como parámetros. Dentro de “funcion” esos parámetros se llaman n y m, y sus valores son modificados. Sin embargo al retornar al programa que lo llama, a y b conservan sus valores originales. Lo que pasamos no son los objetos a y b, sino que copiamos sus valores a los objetos n y m. Es lo mismo que hacer funcion(10,20), cuando llamamos a la función con parámetros constantes. Si los parámetros por valor no funcionasen así, no sería posible llamar a una función con valores constantes o literales.

Las referencias sirven para definir “alias” o nombres alternativos para un mismo objeto. Para ello se usa el operador de referencia (&).

Por ejemplo:

int a;
int &r = a;
a = 10;
Serial.println(r);

En este ejemplo los identificadores a y r se refieren al mismo objeto, cualquier cambio en una de ellos se produce en el otro, ya que son, de hecho, el mismo objeto. El compilador mantiene una tabla en la que se hace corresponder una dirección de memoria para cada identificador de objeto. A cada nuevo objeto declarado se le reserva un espacio de memoria y se almacena su dirección. En el caso de las referencias, se omite ese paso, y se asigna la dirección de otro objeto que ya existía previamente. De ese modo, podemos tener varios identificadores que hacen referencia al mismo objeto, pero sin usar punteros.

Si queremos que los cambios realizados en los parámetros dentro de la función se conserven al retornar de la llamada, deberemos pasarlos por referencia. Esto se hace declarando los parámetros de la función como referencias a objetos. Por ejemplo:

int funcion(int &n, int &m) {
   n = n + 2; 
   m = m - 5; 
   return n+m;
}

int a = 10;
int b = 20;

Serial.println(funcion(a,b));
Serial.println("a = " + String(a) + " b = " + String(b));
//es ilegal pasar constantes como parámetros cuando estos son referencias
Serial.println(funcion(10,20));

En este caso, los objetos “a” y “b” tendrán valores distintos después de llamar a la función. Cualquier cambio de valor que realicemos en los parámetros dentro de la función, se hará también en los objetos referenciadas. Esto quiere decir que no podremos llamar a la función con parámetros constantes, ya que aunque es posible definir referencias a constantes, en este ejemplo, la función tiene como parámetros referencias a objetos variables. Y si bien es posible hacer un casting implícito de un objeto variable a uno constante, no es posible hacerlo en el sentido inverso. Un objeto constante no puede tratarse como objeto variable.

Una const reference es una referencia a que no permite cambiar la variable a través de esa referencia. Por ejemplo const int &r = a; en r tengo el valor de a pero no puedo cambiar el valor de a usando r.

No confundir este concepto con el modificador de variable static, que es utilizado para crear variables que solo son visibles dentro de una función, sin embargo, al contrario que las variables locales que se crean y destruyen cada vez que se llama a la función, las variables estáticas mantienen sus valores entre las llamadas a las funciones.

Más información:

Sobrecarga de Funciones

Hay operadores que tienen varios usos, como por ejemplo *, &, << o >>. Esto es lo que se conoce en C++ como sobrecarga de operadores. Con las funciones existe un mecanismo análogo, de hecho, en C++, los operadores no son sino un tipo especial de funciones, aunque eso sí, algo peculiares.

Así que en C++ podemos definir varias funciones con el mismo nombre, con la única condición de que el número y/o el tipo de los argumentos sean distintos. El compilador decide cuál de las versiones de la función usará después de analizar el número y el tipo de los parámetros. Si ninguna de las funciones se adapta a los parámetros indicados, se aplicarán las reglas implícitas de conversión de tipos.

Las ventajas son más evidentes cuando debemos hacer las mismas operaciones con objetos de diferentes tipos o con distinto número de objetos. También pueden usarse macros para esto, pero no siempre es posible usarlas, y además las macros tienen la desventaja de que se expanden siempre, y son difíciles de diseñar para funciones complejas. Sin embargo las funciones serán ejecutadas mediante llamadas, y por lo tanto sólo habrá una copia de cada una.

Ejemplo:

int mayor(int a, int b);
char mayor(char a, char b); 
float mayor(float a, float b);
int mayor(int a, int b, int c, int d);

int mayor(int a, int b) { 
   if(a > b) return a; else return b; 
}
 
char mayor(char a, char b) { 
   if(a > b) return a; else return b; 
}
 
float mayor(float a, float b) { 
   if(a > b) return a; else return b; 
}

int mayor(int a, int b, int c, int d) { 
   return mayor(mayor(a, b), mayor(c, d)); 
}

Las llamadas a funciones sobrecargadas se resuelven en la fase de compilación. Es el compilador el que decide qué versión de la función debe ser invocada, después de analizar, y en ciertos casos, tratar los argumentos pasados en la llamadas. A este proceso se le llama resolución de sobrecarga.

Tener en cuenta que el tipo de retorno de la función no se considera en la sobrecarga de funciones. Consideremos el caso en el que desea escribir una función que devuelve un número aleatorio, pero se necesita una versión que devolverá un entero, y otra versión que devolverá un doble.

int getRandomValue();
double getRandomValue();

Pero el compilador toma esto como un error. Estas dos funciones tienen los mismos parámetros (ninguno) y en consecuencia, la segunda getRandomValue () serán tratada como una redeclaración errónea de la primera. En consecuencia, tendrán que ser dado diferentes nombres a estas funciones.

Más información:

Sobrecarga de operadores:

Ámbito de las variables

Una variable puede ser declarada al inicio del programa antes de la parte de configuración setup(), a nivel local dentro de las funciones, y, a veces, dentro de un bloque, como para los bucles del tipo if.. for.., etc. En función del lugar de declaración de la variable así se determinará el ámbito de aplicación, o la capacidad de ciertas partes de un programa para hacer uso de ella.

Una variable global es aquella que puede ser vista y utilizada por cualquier función y estamento de un programa. Esta variable se declara al comienzo del programa, antes de setup().

Recordad que al declarar una variable global, está un espacio en memoria permanente en la zona de static data y el abuso de variables globales supone un uso ineficiente de la memoria.

Una variable local es aquella que se define dentro de una función o como parte de un bucle. Sólo es visible y sólo puede utilizarse dentro de la función en la que se declaró. Por lo tanto, es posible tener dos o más variables del mismo nombre en diferentes partes del mismo programa que pueden contener valores diferentes, pero no es una práctica aconsejable porque complica la lectura de código.

En el reference de Arduino hay una muy buena explicación del ámbito de las variables:http://arduino.cc/en/Reference/Scope

La variables estáticas solo se crean e inicializan la primera vez que la función es llamada. Ver ejemplo en: http://arduino.cc/en/Reference/Static

Más información:

Inline

Cuando usamos el nombre de una función, indicando valores para sus argumentos, dentro de un programa, decimos que llamamos o invocamos a esa función. Esto quiere decir que el procesador guarda la dirección actual, “salta” a la dirección donde comienza el código de la función, la ejecuta, recupera la dirección guardada previamente, y retorna al punto desde el que fue llamada.

Esto es cierto para las funciones que hemos usado hasta ahora, pero hay un tipo especial de funciones que trabajan de otro modo. En lugar de existir una única copia de la función dentro del código, si se declara una función como inline, lo que se hace es insertar el código de la función, en el lugar (y cada vez) que sea llamada. Esta indica al compilador que cada llamada a la función inline deberá ser reemplazado por el cuerpo de esta función. En la práctica la función inline es utilizado sólo cuando las funciones son pequeñas para evitar generar un ejecutable de tamaño considerable.

La palabra reservada inline tiene la ventaja de acelerar un programa si éste invoca regularmente a la función inline. Permite reducir considerablemente el código, en particular para los accesadores de una clase. Un accesador de clase es típicamente una función de una línea.

Ejemplo:

inline int mayor(int a, int b) {
  if(a > b) return a; 
  return b; 
}

Más información de inline:

Prototipos de Funciones

Primero recordar que en el lenguaje de Arduino al contrario que en estandar C, no es necesario declarar los prototipos de las funciones, puesto que de eso se encarga el de incluirlo el arduino builder, al igual que de añadir el main..

En C++ es obligatorio usar prototipos. Un prototipo es una declaración de una función. Consiste en una presentación de la función, exactamente con la misma estructura que la definición, pero sin cuerpo y terminada con un “;”.

En general, el prototipo de una función se compone de las siguientes secciones:

  • Opcionalmente, una palabra que especifique el tipo de almacenamiento, puede ser extern o static. Si no se especifica ninguna, por defecto será extern.
  • El tipo del valor de retorno, que puede ser void, si no necesitamos valor de retorno.
  • Modificadores opcionales.
  • El identificador de la función. Es costumbre, muy útil y muy recomendable, poner nombres que indiquen, lo más claramente posible, qué es lo que hace la función, y que permitan interpretar qué hace el programa con sólo leerlos.
  • Una lista de declaraciones de parámetros entre paréntesis. Los parámetros de una función son los valores de entrada (y en ocasiones también de salida).

Un prototipo sirve para indicar al compilador los tipos de retorno y los de los parámetros de una función, de modo que compruebe si son del tipo correcto cada vez que se use esta función dentro del programa, o para hacer las conversiones de tipo cuando sea necesario.

Normalmente, los prototipos de las funciones se declaran dentro del fichero del programa, o bien se incluyen desde un fichero externo, llamado fichero de cabecera, (para esto se usa la directiva #include).

Ya lo hemos dicho más arriba, pero las funciones son extern por defecto. Esto quiere decir que son accesibles desde cualquier punto del programa, aunque se encuentren en otros ficheros fuente del mismo programa. En contraposición las funciones declaradas static sólo son accesibles dentro del fichero fuente donde se definen.

Más información:

Bibliotecas/Librerías en C++

Junto con los compiladores de C y C++, se incluyen ciertos archivos llamados bibliotecas más comúnmente librerías. Las bibliotecas contienen el código objeto de muchos programas que permiten hacer cosas comunes, como leer el teclado, escribir en la pantalla, manejar números, realizar funciones matemáticas, etc.

Las bibliotecas están clasificadas por el tipo de trabajos que hacen, hay bibliotecas de entrada y salida, matemáticas, de manejo de memoria, de manejo de textos y como imaginarás existen muchísimas librerías disponibles y todas con una función específica.

La declaración de librerías, tanto en C como en C++, se debe hacer al principio de todo nuestro código, antes de la declaración de cualquier función o línea de código, debemos indicarle al compilador que librerías usar, para el saber qué términos están correctos en la escritura de nuestro código y cuáles no. La sintaxis es la siguiente: #include <nombre de la librería> o alternativamente #include “nombre de la librería”. En tu código puedes declarar todas las librerías que quieras aunque en realidad no tienen sentido declarar una librería que no vas a usar en tu programa, sin embargo no existe límite para esto.

La directiva de preprocesador #include se usa en los lenguajes C y C++ para “incluir” las declaraciones de otro fichero en la compilación. Esta directiva no tiene más misterio para proyectos pequeños. En cambio, puede ayudar aprovechar bien esta directiva en proyectos con un gran número de subdirectorios.

Ejemplo:

#include “iostream”
#include “string”
#include <math.h>
using namespace std;

Lo único adicional, es la línea que dice using namespace std; esta línea nos ayuda a declarar un espacio de nombre que evita tener que usarlo cada que accedemos a alguna función específica de una librería. Teniendo este namespace declarado podemos llamar por ejemplo el comando cout >>, que pertenece a la librería iostream, sin embargo sin este namespace sería std::cout >>, imagina tener que hacer esto cada vez que uses algún comando o función de las librerías, sería bastante tedioso.

A continuación pondré algunas de las librerías de uso más común de C++ y que forman parte de las librerías estándar de este lenguaje.

  • fstream: Flujos hacia/desde ficheros. Permite la manipulación de archivos desde el programar, tanto leer como escribir en ellos.
  • iosfwd: Contiene declaraciones adelantadas de todas las plantillas de flujos y sus typedefs estándar. Por ejemplo ostream.
  • iostream: Parte del a STL que contiene los algoritmos estándar, es quizá la más usada e importante (aunque no indispensable).
  • math: Contiene los prototipos de las funciones y otras definiciones para el uso y manipulación de funciones matemáticas.
  • memory: Utilidades relativas a la gestión de memoria, incluyendo asignadores y punteros inteligentes (auto_ptr). “auto_ptr” es una clase que conforma la librería memory y permite un fácil manejo de punteros y su destrucción automáticamente.
  • ostream: Algoritmos estándar para los flujos de salida.
  • Librería stdio: Contiene los prototipos de las funciones, macros, y tipos para manipular datos de entrada y salida.
  • Librería stdlib: Contiene los prototipos de las funciones, macros, y tipos para utilidades de uso general.
  • string: Parte de la STL relativa a contenedores tipo string; una generalización de las cadenas alfanuméricas para albergar cadenas de objetos.
  • vector: Parte de la STL relativa a los contenedores tipo vector; una generalización de las matrices unidimensionales C/C++
  • list: Permite implementar listas doblemente enlazadas (listas enlazadas dobles) fácilmente.
  • iterator: Proporciona un conjunto de clases para iterar elementos.
  • regex: Proporciona fácil acceso al uso de expresiones regulares para la comparación de patrones.
  • thread: Útil para trabajar programación multihilos y crear múltiples hilos en nuestra aplicación.

Más información:

Ejercicios Funciones

Ver ejemplo sencillo de uso de funciones: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio02-Funciones

Comparar las funciones con librerías: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio03-Librerias

Ejercicio: Menú interactivo con Arduino. Con todo lo visto de Strings, operadores, estructuras de control y funciones, hacer un ejemplo de un menú interactivo donde se dan varias opciones y pulsando cada una de ellas se ejecuta una acción concreta. Si el valor pulsado no es ninguna de las opciones avisar y volver a mostrar el menú hasta que se pulse una opción correcta.

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio46-Estructuras_de_Control

Librerías vs Funciones en Arduino

Como se ha visto anteriormente, las librerías son trozos de código hechas por terceros que usamos en nuestro sketch. Esto nos facilita mucho la programación y hace que nuestro programa sea más sencillo de hacer y luego de entender. Más adelante veremos cómo hacer una librería.

Las librerías en Arduino incluyen los siguientes archivos comprimidos en un archivo ZIP o dentro de un directorio. Estas siempre contienen:

  • Un archivo .cpp (código de C++)
  • Un archivo .h o encabezado de C, que contiene las propiedades y métodos o funciones de la librería.
  • Un archivo Keywords.txt, que contiene las palabras clave que se resaltan en el IDE (opcional).
  • Muy posiblemente la librería incluye un archivo readme con información adicional de lo que hace y con instrucciones de como usarla.
  • Directorio denominado examples con varios sketchs de ejemplo que nos ayudará a entender cómo usar la librería (opcional).

Más información: https://aprendiendoarduino.wordpress.com/2016/06/27/librerias-3/

Una librería a diferencia de las funciones debe estar al menos en un fichero diferente con extensión .h y opcionalmente en otro .cpp y además debe ser llamada con #include desde el sketch de arduino y estar en una ruta accesible desde el IDE de Arduino, ya sea el mismo directorio del sketch o en algunas de las rutas configuradas para librerías.

La ventaja de usar librerías frente a las funciones es que no es necesario incluir el código cada vez que se va a reutilizar sino que con tener la librería instalada en el IDE y llamarla mediante #include ya la puedo usar en mi código.

Al llamar a una librería desde un sketch, la librería completa es cargada a la placa de Arduino incrementando el tamaño del espacio usado en el microcontrolador, tanto en la memoria flash como en la RAM.

NOTA: Como instalar una librería de github: http://scidle.com/install-github-libraries-on-arduino-ide/ habla de quitar caracteres no ASCII, pero comprueba también que no funcione porque haya subdirectorios.

Las librerías que usamos para los sketches tienen una versión, que se suelen actualizar con frecuencia. También tenemos un control de versiones en el nuevo IDE a partir de 1.6.4 que nos facilita la gestión de la versión de las librerías usadas. Este aspecto es importante porque un sketch que funciona con una versión de una librería es posible que al compilarlo con otra versión en otro IDE no funcione. Por ello es importante documentar con que versión de librería está hecho o distribuir el sketch con la librería con la que se ha creado. Generalmente las librerías tienen compatibilidad hacia atrás, pero puede que no ocurra o que el comportamiento de la librería sea diferente.

Al cambiar el IDE también nos podemos encontrar que nuestro sketch no es compatible con la versión de la librería que estemos usando, que es diferente con la que se diseñó originalmente el sketch.

Listado de librerías: http://playground.arduino.cc/Main/LibraryList

Ejercicio Librerías vs Funciones

Ya hemos visto lo que son las librerías y las funciones definidas por usuario, el uso de unas u otras dependerá de varios factores como la posibilidad de reutilización del código o la experiencia del programador con el uso de librerías.

Ejemplos simples de librerías:

Bus I2C/TWI

Al comenzar a usar Arduino puede resultar algo complejo entender las diferencias entre los diferentes tipos de interfaces de comunicación (y protocolos asociados).

Dentro la comunicación serie integrada en los microcontroladores de Arduino tenemos:

  • UART (recepción-transmisión asíncrona universal) es uno de los protocolos serie más utilizados. La mayoría de los microcontroladores disponen de hardware UART. Usa una línea de datos simple para transmitir y otra para recibir datos. Comúnmente, 8 bits de datos son transmitidos de la siguiente forma: un bit de inicio, a nivel bajo, 8 bits de datos y un bit de parada a nivel alto. UART se diferencia de SPI y I2C en que es asíncrono y los otros están sincronizados con señal de reloj. La velocidad de datos UART está limitado a 2Mbps
  • SPI es otro protocolo serie muy simple. Un maestro envía la señal de reloj, y tras cada pulso de reloj envía un bit al esclavo y recibe un bit de éste. Los nombres de las señales son por tanto SCK para el reloj, MOSI para el Maestro Out Esclavo In, y MISO para Maestro In Esclavo Out. Para controlar más de un esclavo es preciso utilizar SS (selección de esclavo).
  • I2C es un protocolo síncrono. I2C usa solo 2 cables, uno para el reloj (SCL) y otro para el dato (SDA). Esto significa que el maestro y el esclavo envían datos por el mismo cable, el cual es controlado por el maestro, que crea la señal de reloj. I2C no utiliza selección de esclavo, sino direccionamiento.

Más información en:

I2C es un bus de comunicaciones en serie. Su nombre viene de Inter-Integrated Circuit (Inter-Circuitos Integrados). La versión 1.0 data del año 1992 y la versión 2.1 del año 2000, su diseñador es Philips. La velocidad es de 100 kbit/s en el modo estándar, aunque también permite velocidades de 3.4 Mbit/s. Es un bus muy usado en la industria, principalmente para comunicar microcontroladores y sus periféricos en sistemas integrados (Embedded Systems) y generalizando más para comunicar circuitos integrados entre si que normalmente residen en un mismo circuito impreso.

La principal característica de I²C es que utiliza dos líneas para transmitir la información: una para los datos y otra para la señal de reloj. También es necesaria una tercera línea, pero esta sólo es la referencia (masa). Como suelen comunicarse circuitos en una misma placa que comparten una misma masa esta tercera línea no suele ser necesaria.

Las líneas se llaman:

  • SDA: datos
  • SCL: reloj
  • GND: tierra

Las dos primeras líneas son drenador abierto, por lo que necesitan resistencias de pull-up.  Dos o más señales a través del mismo cable pueden causar conflicto, y ocurrirían problemas si un dispositivo envía un 1 lógico al mismo tiempo que otro envía un 0. Por tanto el bus es “cableado” con dos resistencia para poner el bus a nivel alto, y los dispositivos envían niveles bajos. Si quieren enviar un nivel alto simplemente lo comunican al bus.

Más información:

Los dispositivos conectados al bus I2C tienen una dirección única para cada uno. También pueden ser maestros o esclavos. El dispositivo maestro inicia la transferencia de datos y además genera la señal de reloj, pero no es necesario que el maestro sea siempre el mismo dispositivo, esta característica se la pueden ir pasando los dispositivos que tengan esa capacidad. Esta característica hace que al bus I2C se le denomine bus multimaestro.

Las líneas SDA y SCL son del tipo drenaje abierto, es decir, un estado similar al de colector abierto, pero asociadas a un transistor de efecto de campo (o FET). Se deben polarizar en estado alto (conectando a la alimentación por medio de resistores “pull-up”) lo que define una estructura de bus que permite conectar en paralelo múltiples entradas y salidas.

Fijaros también que hay unas resistencias de Pullup conectadas a SDA y SCL. Son imperativas, ya que el bus es activo bajo (Esto es, la señal activa es un 0, no un 1). Cuando vayas a conectar algo al bus I2C, es imprescindible que leas el manual para saber si los pullups los tienes que poner tú, o vienen puestos en el componente.

Puede haber más de un master. La norma propone un sistema de arbitraje, para transferir el control de uno a otro, pero en un instante dado, sólo uno puede ser el master.

El proceso de comunicación en el bus I2C es:

  • El maestro comienza la comunicación enviando un patrón llamado “start condition”. Esto alerta a los dispositivos esclavos, poniéndolos a la espera de una transacción.
  • El maestro se dirige al dispositivo con el que quiere hablar, enviando un byte que contiene los siete bits (A7-A1) que componen la dirección del dispositivo esclavo con el que se quiere comunicar, y el octavo bit (A0) de menor peso se corresponde con la operación deseada (L/E), lectura=1 (recibir del esclavo) y escritura=0 (enviar al esclavo).
  • La dirección enviada es comparada por cada esclavo del bus con su propia dirección, si ambas coinciden, el esclavo se considera direccionado como esclavo-transmisor o esclavo-receptor dependiendo del bit R/W.
  • Cada byte leído/escrito por el maestro debe ser obligatoriamente reconocido por un bit de ACK por el dispositivo maestro/esclavo.
  • Cuando la comunicación finaliza, el maestro transmite una “stop condition” para dejar libre el bus.

Las transacciones en el bus I2C tienen este formato:

| start | A7 A6 A5 A4 A3 A2 A1 R/W | ACK | … DATA … | ACK | stop | idle |

Cuando los datos son enviados por SDA, los pulsos de reloj son enviados por SCL para mantener el maestro y el esclavo sincronizados. Puesto que los datos son enviados como un bit en cada pulso de reloj, la transferencia de datos es un octavo la frecuencia de reloj. La frecuencia del reloj estándar originalmente se puso a 100 KHz y la mayoría de los integrados y microcontroladores soportan esta velocidad. En posteriores actualizaciones, se introdujo una fast speed de 400 KHz y una high speed de 1.7 a 3.4 MHz. Arduino puede soportar la velocidad estándar y fast speed, BeagleBoard tiene tres buses I2C cada uno a una velocidad distinta y tanto BeagleBoard como Raspberry Pi soportan velocidad estándar y fast speed. Fast speed corresponde a una velocidad de transferencia de 50Kbytes/sec lo que puede ser una velocidad muy baja para algunas aplicaciones de control. Una opción en ese caso es usar SPI en lugar de I2C.

The speed grades:

I2C también se conoce como TWI (Two Wire Interface) y no dispone de un conector estandarizado. Únicamente por motivos de licencia se le denomina TWI, no obstante, la patente caducó en 2006, por lo que actualmente no hay restricción sobre el uso del término I2C.

I2C bus specification: http://i2c.info/i2c-bus-specification

Especificación y manual de usuario de I2C: http://www.nxp.com/documents/user_manual/UM10204.pdf

I2C bus y como usarlo: http://www.i2c-bus.org/fileadmin/ftp/i2c_bus_specification_1995.pdf

Presentación de I2C por Philips: http://www.nxp.com/documents/application_note/AN10216.pdf

I2C no tiene limitaciones de velocidad, el maestro genera la velocidad de reloj y I2C provee de un mecanismo que si el esclavo es más lento es capaz de ponerse el maestro en modo de espera.

SMBus (Bus de Administración del Sistema) es un subconjunto del protocolo I2C definido por Intel en 1995. Todas las placas bases modernas tienen un bus SMBus al que se conectan la mayor parte de los chips de monitorización del sistema. Estos chips sirven para medir temperaturas de componentes, velocidad de ventiladores, voltajes, etc. Toda clase de información sobre hardware.

Direccionamiento I2C

En principio, el número de dispositivos que se puede conectar al bus no tiene límites, aunque hay que observar que la capacidad máxima sumada de todos los dispositivos no supere los 400 pF. El valor de los resistores de polarización no es muy crítico, y puede ir desde 1K8 (1.800 ohms) a 47K (47.000 ohms). Un valor menor de resistencia incrementa el consumo de los integrados pero disminuye la sensibilidad al ruido y mejora el tiempo de los flancos de subida y bajada de las señales. Los valores más comunes en uso son entre 1K8 y 10K.

Lo más común en los dispositivos para el bus I2C es que utilicen direcciones de 7 bits, aunque existen dispositivos de 10 bits. Este último caso es raro. Una dirección de 7 bits implica que se pueden poner hasta 128 dispositivos sobre un bus I2C, ya que un número de 7 bits puede ir desde 0 a 127. Cuando se envían las direcciones de 7 bit, de cualquier modo la transmisión es de 8 bits. El bit extra se utiliza para informarle al dispositivo esclavo si el dispositivo maestro va a escribir o va a leer datos desde él. Si el bit de lectura/escritura (R/W) es cero, el dispositivo maestro está escribiendo en el esclavo. Si el bit es 1 el maestro está leyendo desde el esclavo. La dirección de 7 bit se coloca en los 7 bits más significativos del byte y el bit de lectura/escritura es el bit menos significativo.

Direcciones de esclavos con 7,8 y 10 bits: http://www.totalphase.com/support/articles/200349176

Cada fabricante usa su propio direcionamiento para los esclavos, por ejemplo Philips: http://www.diolan.com/downloads/i2c-address-allocation-table.pdf

I2C en Arduino

Arduino dispone de soporte I2C por hardware vinculado físicamente a ciertos pines. También es posible emplear cualquier otro grupo de pines como bus I2C a través de software, pero en ese caso la velocidad será mucho menor.

Los pines a los que está asociado varían de un modelo a otro. La siguiente tabla muestra la disposición en alguno de los principales modelos. Para otros modelos, consultar el esquema de patillaje correspondiente.

MODELO SDA SCK
Uno A4 A5
Nano A4 A5
Mini Pro A4 A5
Mega 20 21

Para usar el bus I2C en Arduino, el IDE Standard proporciona la librería “Wire.h”, que contiene las funciones necesarias para controlar el hardware integrado.

Existen otras librerías más avanzadas que Wire.h para manejar el bus I2C, como por ejemplo I2Cdevlib o I2C library.

Para saber más de i2c en el Atmega328p ver página 260 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

Características de I2C en ATmega328p:

  • Simple, yet Powerful and Flexible Communication Interface, only two Bus Lines Needed
  • Both Master and Slave Operation Supported
  • Device can Operate as Transmitter or Receiver
  • 7-bit Address Space Allows up to 128 Different Slave Addresses
  • Multi-master Arbitration Support
  • Up to 400kHz Data Transfer Speed
  • Slew-rate Limited Output Drivers
  • Noise Suppression Circuitry Rejects Spikes on Bus Lines
  • Fully Programmable Slave Address with General Call Support
  • Address Recognition Causes Wake-up When AVR is in Sleep Mode
  • Compatible with Philips I2C protocol

Módulo TWI en ATmega328p:

i2c-arduino

El I2C en el microcontrolador ATmega328p usa los registros:

Register Name Function
TWCR Two Wire Control Register Controls the actions of the TWI module
TWSR Two Wire Status Register Reports the status of the TWI actions
TWDR Two Wire Data/Address Register Contains the data you want to transmit or have received
TWBR Two Wire Bit Rate Register Controls the frequency of the clock (SCL)

La librería para manejar el bus I2C en Arduino es Wire: http://arduino.cc/en/reference/wire

Esta librería permite comunicar con I2C/TWI Arduino con otros dispositivos. En las placas Arduino con el diseño R3 (1.0 pinout), la SDA (línea de datos) y SCL (línea de reloj) están en los pines cerca del pin AREF.

El Arduino Due tiene dos interfaces I2C / TWI SDA1 y SCL1 que están cerca del pin AREF y los adicionales en los pines 20 y 21.

Funciones:

  • begin() – Inicia la librería Wire y especifica si es master o slave
  • requestFrom() – Usado por el maestro para solicitar datos del esclavo
  • beginTransmission() – Comenzar transmisión con esclavo.
  • endTransmission() – Finaliza la transmisión que comenzó con un esclavo y transmite los bytes en cola.
  • write() – Escribe datos desde un esclavo como respuesta a una petición del maestro o pone en cola la transmisión de un maestro.
  • available() – Devuelve el número de bytes para leer
  • read() – Lee un byte transmitido desde un esclavo a un maestro o viceversa
  • onReceive() – Llama a una función cuando un esclavo recibe una transmisión de un maestro. Registra una función de callback.
  • onRequest() – Llama a una función cuando un maestro solicita datos de un maestro. Registra una función de callback.

Y para profundizar más en el I2C en Arduino:

Aplicaciones I2C

Podríamos usar el bus I2C para comunicar Raspberry Pi con Arduino y otros dispositivos:

Como conectar via I2C Arduino y Raspberry Pi.

Tutorial de I2C en Raspberry: http://www.instructables.com/id/Raspberry-Pi-I2C-Python/all

Librería python para usar I2C en Raspberry Pi

https://github.com/quick2wire/quick2wire-python-api

Usar I2C en Linux:

También disponemos de controladores I2C en caso de no tener disponibles puertos I2C en el microcontrolador: http://www.nxp.com/documents/application_note/AN10148.pdf que reciben datos en formato pararlelo, lo pasana  serie y lo mandan a un bus I2C y en sentido contrario.

Otro ejemplo de aplicación I2C es este shield que maneja 8 relés usando dos pines I2C e incluso se pueden apilar para manejar, 16 o 24 relés en el mismo bus I2C:

Como ejemplo curioso de I2C el Wii Remote (conocido como Wiimote) es el dispositivo de entrada principal de Wii. Es un dispositivo inalámbrico que utiliza la tecnología Bluetooth estándar para comunicarse con la Wii. Está construido alrededor de un Broadcom BCM2042 bluetooth System-on-a-chip, y contiene múltiples periféricos que le proporcionan datos, así como un puerto de expansión para complementos externos.

El mando Wii incluye un puerto de expansión de 6 pines que permite conectar periféricos externos. Las comunicaciones son I2C síncronas bidireccionales y los dispositivos proporcionan un bloque de registro virtual de longitud 0x100 que está mapeado en 0xa40000 en el espacio de direcciones del Wii Remote (La dirección I2C de 7 bits es 0x52).

Webs del proyecto:

Ejercicios I2C Arduino

Escaner I2C

Cada componente que conectamos al bus I2C tiene una dirección única, y cada mensaje y orden que transmitimos al bus, lleva anexa esta dirección, indicando cuál de los muchos posibles, es el receptor del mensaje.

Esto implica que sabemos la dirección del componente. Lo normal es comprobar la información técnica del fabricante del componente, y ahí suele decirnos cuál es la dirección por defecto. Pero es posible que tengamos un dispositivo sin documentación, para ello hay un programa para Arduino, que nos informa, de lo que hay en nuestro bus y con qué dirección.

Este programa, no tiene ni idea de quien responde y lo que hace, pero bastante es que nos informe de que hay alguien en la dirección xx. Si no sabemos en qué dirección está un componente dado, basta con colocarlo solo en el bus, y ver qué dirección nos reporta el I2C scanner.

En algunas ocasiones, sobre todo al comprar en vendedores chinos, el fabricante no nos facilita la dirección del dispositivo o incluso lo proporciona de forma incorrecta. Esta es una circunstancia común y nada preocupante. Para eso disponemos de un sketch llamado “Scanner I2C” que realiza un barrido por todas las posibles direcciones del bus, y muestra el resultado en caso de encontrar un dispositivo en la dirección.El sketch scanner I2C está disponible en este enlace, o podéis usar la siguiente versión reducida y traducida.

Ver http://playground.arduino.cc/Main/I2cScanner

#include "Wire.h"
extern "C" {
    #include "utility/twi.h"
}
void scanI2CBus(byte from_addr, byte to_addr, void(*callback)(byte address, byte result) )
{
  byte rc;
  byte data = 0;
  for( byte addr = from_addr; addr <= to_addr; addr++ ) {
    rc = twi_writeTo(addr, &data, 0, 1, 0);
    callback( addr, rc );
  }
}
void scanFunc( byte addr, byte result ) {
  Serial.print("addr: ");
  Serial.print(addr,DEC);
  Serial.print( (result==0) ? " Encontrado!":"   	");
  Serial.print( (addr%4) ? "\t":"\n");
}


const byte start_address = 8;
const byte end_address = 119;
 
void setup()
{
    Wire.begin();
    Serial.begin(9600);
    Serial.print("Escaneando bus I2C...");
    scanI2CBus( start_address, end_address, scanFunc );
    Serial.println("\nTerminado");
}
 
void loop()
{
    delay(1000);
}

Comunicar Arduinos por I2C

Ejercicio33-I2C: comunicar por I2C tres arduinos

Ejemplo de uso con dos Arduinos:

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio33-I2C

Dispositivos I2C

En el mercado hay multitud de sensores y dispositivos I2C para utilizar con Arduino, veamos alguno de los más importantes.

Para escribir en el dispositivo I2C las tramas serían las indicadas en el gráfico siguiente.

Tras la condición de Start iniciada por el microcontrolador se envía el primer byte conteniendo la dirección del dispositivo I2C con el que se quiere comunicar y en el último bit aparece el 0 indicando que se va a escribir, la información pasa del microcontrolador al dispositivo I2C.

Le sigue a esta trama el bit de acknowledgement generado por el esclavo para confirmar la correcta recepción de byte anterior. El byte siguiente se refiere a la dirección o registro del dispositivo I2C donde se quiere escribir o enviar el dato desde el microcontrolador.

A partir del tercer byte, se envían los datos de forma sucesiva, y es el dispositivo I2C el que va autoincrementando la dirección de memoria donde se guarda el dato recibido.Se finaliza la comunicación con el bit de parada.

Para leer datos del dispositivo I2C, la secuencia es la siguiente.

Inicia el dispositivo master la comunicación a través del bit de Start. Después viene la dirección del esclavo al que se dirige, indicando que va realizar la lectura del mismo (bit R/W=1). En este caso, el dispositivo I2C también va realizando un autoincremento del registro a leer.

Aplicaciones I2C: http://en.wikipedia.org/wiki/I%C2%B2C#Applications

Listado de productos I2C: http://www.nxp.com/products/interface_and_connectivity/i2c/

En el mercado podemos encontrar muchos dispositivos I2C. Ejemplos de dispositivos I2C:

Cabe destacar: módulos GPS, RTC (Real Time Clock), IMU (Inertial Measurement Unit), Display gráfico, EEPROM, Sensores de temperatura y humedad, sensor de presión, distancia, driver motor, etc…

I2C al tratarse de una comunicación serie también puede convertirse a otros protocolos serie.

Doble UART con I2C/SPI interface: http://www.nxp.com/documents/data_sheet/SC16IS752_SC16IS762.pdf, este chip ofrece conversion de protocolo bidireccional de I2C/SPI a RS-232/RS-485

Conversor de USB a I2C:

Este circuito utiliza el integrado convertidor de USB a Serie de la casa FTDI por lo que hay disponibles controladores tanto para Windows como para Apple y Linux. Una vez instalado el driver el circuito aparece en el ordenador como un puerto serie más, por lo que todo lo que hace falta para poder controlar dispositivos I2C es mandar las órdenes directamente al puerto serie y el circuito se encarga de traspasarlas directamente al bus I2C.

Más información en:

Ejemplo de pantalla LCD por I2C de http://www.prometec.net/bus-i2c/

De usb a I2C con software: http://www.telos.de/conniimm20/

Interfaz I2C de módulo EEPROM: http://www.miniinthebox.com/es/at24c02-i2c-interfaz-de-modulo-de-memoria-eeprom-blue_p643036.html

Y el código para usarlo: http://playground.arduino.cc/Code/I2CEEPROM

Colección de librerías para usar dispositivos I2C con Arduino: http://www.i2cdevlib.com/ y los dispositivos: http://www.i2cdevlib.com/devices

Si queremos saber los dispositivos que hay conectados en un bus I2C con las direcciones de cada uno de ellos y mostrarlo por consola disponemos de este código: I2C scanner http://playground.arduino.cc/Main/I2cScanner

IMU (Inertial Measurement Unit)

Una IMU es un dispositivo capaz de medir la fuerza (aceleración) y la velocidad. Generalmente consta de un Acelerómetro y un Giroscopio. Por lo tanto una IMU no mide ángulos, por lo menos no directamente, requiere algunos cálculos.

Un dispositivo I2C muy interesante es el MPU-6050 que nos sirve para probar e introducirnos en el mundo de los giroscopios y acelerómetros.

Definiciones:

El MPU-6050 es una IMU de 6DOF (se lee “6 Degrees Of Freedom“ o 6 grados de libertad). Esto significa que lleva un acelerómetro y un giroscopio, ambos de 3 ejes (3+3 = 6DOF). Hay IMUs de 9DOF, en ese caso también llevan un magnetómetro. Otras pueden tener 5DOF, en cuyo caso el giroscopio sólo mide dos ejes, etc.

El MPU-6050 opera con 3.3 voltios, aunque algunas versiones llevan un regulador que permite conectarla a 5V. El MPU-6050 utiliza el protocolo de comunicación I2C.

El acelerómetro mide la aceleración. La aceleración puede expresarse en 3 ejes: X, Y y Z, las tres dimensiones del espacio. Por ejemplo, si mueves la IMU hacia arriba, el eje Z marcará un cierto valor. Si es hacia delante, marcará el eje X, etc. La gravedad de la Tierra tiene una aceleración de aprox. 9.8 m/s², perpendicular al suelo como es lógico. Así pues, la IMU también detecta la aceleración de la gravedad terrestre. Gracias a la gravedad terrestre se pueden usar las lecturas del acelerómetro para saber cuál es el ángulo de inclinación respecto al eje X o eje Y.

Supongamos que la IMU esté perfectamente alineada con el suelo. Entonces, como puedes ver en la imagen, el eje Z marcará 9.8, y los otros dos ejes marcarán 0. Ahora supongamos que giramos la IMU 90 grados. Ahora es el eje X el que está perpendicular al suelo, por lo tanto marcará la aceleración de la gravedad.

Si sabemos que la gravedad es 9.8 m/s², y sabemos qué medida dan los tres ejes del acelerómetro, por trigonometría es posible calcular el ángulo de inclinación de la IMU. Una buena fórmula para calcular el ángulo es:

Dado que el ángulo se calcula a partir de la gravedad, no es posible calcular el ángulo Z (giro sobre si mismo) con esta fórmula ni con ninguna otra. Para hacerlo se necesita otro componente: el magnetómetro, que es un tipo de brújula digital. El MPU-6050 no lleva, y por tanto nunca podrá calcular con precisión el ángulo Z. Sin embargo, para la gran mayoría de aplicaciones sólo se necesitan los ejes X e Y.

El giroscopio mide la velocidad angular, es decir, el número de grados que se gira en un segundo.En un principio, los giroscopios eléctricos eran unos voluminosos artefactos muy caros usados en aviones y para dirigir cohetes. Actualmente la miniaturización de circuitos y gracias a la tecnología MEMS, es posible tener un giroscopio en un chip a un precio muy asequible.

La media de velocidad angular en vez de medirse en grados por segundo, se mide en otra unidad que son radianes por segundo (1rad/s = 180/PI grados/s). Si sabemos el ángulo inicial de la IMU, podemos sumarle el valor que marca el giroscopio para saber el nuevo ángulo a cada momento. Supongamos que iniciamos la IMU a 0º. Si el giroscopio realiza una medida cada segundo, y marca 3 en el eje X, tendremos el ángulo con esta sencilla fórmula:

Dónde Δt es el tiempo que transcurre cada vez que se calcula esta fórmula, AnguloYAnterior es el ángulo calculado la última vez que se llamó esta fórmula y GiroscopioY es la lectura del ángulo Y del giroscopio.

Y lo mismo pasa con los ejes X, Z. Sólo que se suele ignorar el eje Z, puesto que al no poder calcular un ángulo Z con el Acelerómetro, no se puede aplicar un Filtro Complementario para el eje Z.

Podemos ver los datos del acelerómetro y giroscopio de nuestro smartphone con https://play.google.com/store/apps/details?id=com.mtorres.phonetester&hl=es

Error en las medidas. Las IMUs son unos artefactos mágicos que con un poco de trigonometría pueden dar un ángulo con total exactitud. Pero hay dos problemas muy importantes: el ruido y los errores. El ruido son todas aquellas interferencias que afectan a los dispositivos electrónicos. El acelerómetro es capaz de medir cualquier ángulo, sin embargo sus lecturas son ruidosas y tienen un cierto margen de error. Si se dibuja un gráfico de las medidas de un acelerómetro en función del tiempo, se verá algo de este estilo:

El ángulo real (ideal) está marcado en azul, y las medidas reales están en rojo. Por si esto fuera poco, el acelerómetro también detecta cualquier aceleración que no sea la de la gravedad. Por tanto, si mueves la IMU sin girarla, al aplicar una aceleración en otro eje, la IMU lo detectará como un cambio de rotación.

Por otra parte tenemos el giroscopio. A diferencia del acelerómetro, da las medidas con mucha precisión. Pero al realizar los cálculos del ángulo es inevitable que se produzca un pequeño error, que con el tiempo va acomulándose hasta que cualquier similitud con la realidad es pura coincidencia. Esto en inglés se llama drift (deriva). Hay varias formas de combinar los datos del acelerómetro y el giroscopio para así obtener medidas exactas. Para ello son necesarios los filtros.

Hay que conseguir eliminar el ruido, el drift y conseguir que el acelerómetro no cambie de ángulo al detectar otra fuerza que no sea la gravedad. Hay distintos algoritmos, llamados filtros, que hacen esta tarea. Uno de los mejores es el Filtro de Kálman, se utiliza en los aviones, cohetes y satélites geoestacionarios.

El filtro de Kálman es sorprendente. Capaz de calcular el error de cada medida a partir de las medidas anteriores, eliminarlo y dar el valor real del ángulo. En cierto modo es un algoritmo que aprende en cada iteración.

Sin embargo tiene dos problemas: Tiene un coste de procesamiento algo elevado y es muy complicado de entender. Ver https://es.wikipedia.org/wiki/Filtro_de_Kalman

Existen otros filtros a nuestra disposición. El que vamos a utilizar es conocido como Filtro Complementario o Complementary Filter en inglés. Es ideal para implementar con Arduino: fácil de utilizar, bajo coste de procesamiento y con una precisión muy buena.

¿En qué consiste exactamente? El Filtro Complementario es en realidad una unión de dos filtros diferentes: un High-pass Filter para el giroscopio y un Low-pass Filter para el Acelerómetro. El primero deja pasar únicamente los valores por encima de un cierto límite, al contrario que el Low-pass filter, que sólo permite a los que están por debajo.

La fórmula resultante de combinar (complementar, de aquí el nombre) los dos filtros es:

Dónde AnguloGyro es el ángulo del Giroscopio que hemos calculado previamente, y AnguloAccel el ángulo del Acelerómetro calculado con la fórmula de la tangente. Δt es el tiempo que ha pasado (en segundos) desde la última vez que se ha calculado el filtro. Esta fórmula es la misma para el eje X, Y.

Es posible probar a cambiar el valor de 0.98 y 0.02 por un valor personalizado. Eso sí, ambos tienen que sumar 1.

Más información para uso de MPU-6050:

DMP: El MPU6050 IMU contiene un DMP (Digital Motion Processor) que fusiona los datos del acelerómetro y del giroscopio para minimizar los efectos de los errores inherentes a cada sensor. El DMP calcula los resultados en términos de cuaterniones, y puede convertir los resultados en ángulos de Euler y realizar otros cálculos con los datos. Invensense no revelará el algoritmo que utilizan para combinar los datos.

Mientras que la información del giroscopio (velocidad angular) o acelerómetro (aceleración lineal) para cada eje (X, Y y Z) se puede leer de forma directa, el acceso a los registros del DMP es algo más compleja, debido a que el fabricante no ha abierto toda la información al respecto.

Más información de DMP:

MEMS – sistemas micro-electro-mecánicos. Los IMUs son sistemas MEMS, los avances en el campo de los semiconductores están dando lugar a circuitos integrados con características tridimensionales e incluso con piezas móviles. Estos dispositivos, llamados Sistemas Micro electromecánicos (MEMS), pueden resolver muchos problemas que un microprocesador más el software o configuración no ASIC (Chip integrados de aplicación específica) no pueden. La tecnología MEMS puede aplicarse utilizando un sin número de diferentes materiales y técnicas de fabricación; la elección dependerá del tipo de dispositivo que se está creando y el sector comercial en el que tiene que operar.

Los acelerómetros actuales son simples dispositivos MEMS consistentes en una micro estructura suspendida con una masa determinada de referencia conocida como masa sísmica dentro de una cámara de gas sellada. Bajo la influencia de aceleraciones externas la masa cambia de su posición neutral y ese cambio es convertido en una señal eléctrica mediante piezoresistencias con cambio de la capacitancia.

ABC del acelerometro (MUY BUENO): http://5hertz.com/tutoriales/?p=228

Los giroscopios basados en sistemas MEMs usan el mismo sistema que el péndulo de Foucault y usa un elemento vibrante (giroscopios  de estructura vibrante). El principio físico subyacente es que un objeto vibrante tiende a continuar vibrando en el mismo plano que gira su apoyo. En la literatura de ingeniería, este tipo de dispositivos se conocen como “giroscopio de vibración de Coriolis”, debido a que a medida que el plano de oscilación gira, la respuesta detectada por el transductor resulta del término de Coriolis presente en sus ecuaciones de movimiento. Los giroscopios de estructura vibrante son más simples y económicos, que los giroscopios de rotación convencionales. Los dispositivos de estado sólido que usan este principio son un tipo económico de indicadores de posición.

Como funciona internamente un girocopio: http://www5.epsondevice.com/en/information/technical_info/gyro/

Diferentes tipos de giroscopios:

Más información:

Guia de compras de IMU de sparkfun: https://www.sparkfun.com/pages/accel_gyro_guide

Un IMU de 9 grados de libertad incorpora un magnetómetro (brújula digital) al conjunto de acelerómetro y giroscopio. El MPU-9150 que es un MPU-6050 + brujula (magnetometro): http://playground.arduino.cc/Main/MPU-9150

Otro ejemplo de IMU de 9 grados de libertad: https://www.sparkfun.com/products/10724 que tiene un magnetometro. Tutorial de este IMU: https://github.com/ptrbrtz/razor-9dof-ahrs/wiki/Tutorial

Otra IMU con 9DOF es el GY-85. Al contrario que el MPU-6050 tiene en el mismo chip el acelerómetro y el giroscopio, el GY-85  integra tres sensores: Acelerómetro de 3 ejes, Giroscopio de 3 ejes y sensor de campo magnético de 3 ejes.

Comprar: http://www.dx.com/es/p/gy-85-6dof-9dof-imu-sensor-module-for-arduino-148436#.VzRp0oSLTcs

Las referencias de los integrados son:

Esquemático:

esquematico GY-85

Ejercicio IMU MPU6050

El MPU6050 que vamos a utilizar es un chip de 6 dof o grados de libertad porque incluye un acelerómetro de 3 ejes y un giróscopo de 3 ejes.

Aunque lo que miden los sensores internos son aceleraciones lineales y angulares el procesador interno del IMU es capaz de realizar cálculos sobre la marcha para darnos informaciones más útiles como los ángulos de inclinación con respecto a los 3 ejes principales. Conseguir los datos en bruto del MPU6050 es la parte fácil, procesarlos y reaccionar es otra historia que se puede complicar un poco más.

Para estas sesión vamos a utilizar una Breakout board bastante típica llamada GY-521, que incluye un MPU650 y un regulador de tensión, con lo que podemos alimentar a tanto 3.3V como a 5V

El siguiente diagrama muestra la orientación de los ejes de sensibilidad y la polaridad de rotación.

Esquema de conexión

Probar el I2C scanner http://playground.arduino.cc/Main/I2cScanner con el MPU conectado normal y luego con AD0 a masa y comprobar que cambia su dirección.

Ver los ejercicios 62 de https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio62-MPU6050

  • raw_data
  • advanced_values

Este programa es de lo más sencillo, y lo que hace es leer e imprimir los valores en bruto que los acelerómetros y giroscopios que le envían a Arduino, es decir sin procesar en absoluto, algo que sirve de muy poco. Son las medidas reales que producen los acelerómetros y los giróscopos, pero así no valen de mucho.

Hay 6 valores. Los tres primeros corresponden a los acelerómetros y los tres últimos a los giróscopos. Si vas girando el IMU en direcciones perpendiculares verás con rapidez cual es cada uno de los ejes, porque aunque el chip esté en movimiento inercial aun detectará la aceleración de la gravedad, claramente en una de las direcciones.

Vamos a instalar las librerías correspondientes y a probar con uno de los programas de ejemplo que tanto las conexiones como el  MPU6050 están en condiciones operativas.

Para medir los ángulos, vamos a usar estas librerías. Cargar el ejemplo MPU6050_DMP6.

El texto que aparece al principio de cada línea “ypr” corresponde a las iniciales en ingles de los tres ángulos de referencia Yaw, Pitch y Roll. Y los valores que van cambiando son poco más o menos el valor instantáneo de cada uno de esos ángulos.

Si el IMU dispone además de un magnetómetro de 3 ejes, ya está en condiciones de apuntar al Norte además y puede fijar con precisión el segundo eje, y el tercero cae por su propio peso, pero no es el caso del MPU6050 que estamos usando.

Ahora conectar un servo para moverlo girando la IMU:

Ver ejercicio MPU6050_DMP6_SERVO: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio62-MPU6050

RTC (Real Time Clock)

Ya conocemos la librería Time para Arduino. Empezamos utilizando el reloj interno, pero tiene varios problemas evidentes, como el hecho de que el reloj interno se reinicia cada 50 días aproximadamente, y además si cortamos la corriente a nuestro Arduino, pone el reloj a 00 y vuelve a empezar desde el 1 de enero de 1970. Una solución, que nos permita usar un reloj fiable que se mantenga incluso cuando apagamos Arduino es usar un RTC

Uno RTC muy extendido para reloj digital es el DS1307 de Maxim. Por muy poco dinero realiza todas las funciones para llevar un reloj con calendario y solo hay que añadirle una batería externa para que no pierda la hora al perder la corriente.

Un RTC es un reloj:

Generalmente los RTC son dispositivos I2C y los más comunes son el DS1307 y el DS3231.

Para usar un RTC con Arduino es necesaria la librería time que ofrece la funcionalidad de mantener la fecha y hora en Arduino y se usa el RTC como fuente de sincronización de la hora.

La librería time añade funcionalidades de tiempo a Arduino, ya sea con o sin HW externo. Nos permite obtener la fecha y hora como año, mes, dia, hora, minuto, segundo, día de la semana, etc… También da la hora con el tipo de dato estándar de C time_t de la librería <time.h>: http://www.cplusplus.com/reference/ctime/

Sabe que la librería estandard de C time.h (http://www.cplusplus.com/reference/ctime/) no existe para los microcontroladores AVR, pero si para los ARM y gracias a esta librería es posible añadir esta funcionalidad a Arduino.

La versión anterior de la librería Time es la DateTime: http://playground.arduino.cc/Code/DateTime

Las características de un RTC a la hora de comprar son:

  • Dirección I2C configurable
  • Compensación de año bisiesto (leap year)
  • Fecha máxima

Generalmente encontramos RTCs con bus I2C,pero también se pueden encontrar con puerto serie o bus SPI o 1-Wire.

RTCs de TI: http://www.ti.com/lsds/ti/clock-and-timing/real-time-clocks-products.page

RTCs de ST: http://www.st.com/web/en/catalog/sense_power/FM151/CL1410?sc=rtc

RTCs de MI: https://www.maximintegrated.com/en/products/digital/real-time-clocks.html

Más información:

El DS1307 de Maxim/Dallas es un circuito integrado capaz de almacenar y llevar la cuenta de la fecha y hora, además disponemos de unos cuantos bytes de datos de usuario en su memoria RAM no volátil (NVRAM). Arduino posee una CPU potente, sin embargo aún así es incapaz de mantener la hora correctamente a largo plazo, debido a los microsegundos que se atrasan al no dividir la frecuencia del reloj principal para dar con el segundo exacto.

Características DS1307:

  • RTC: Real Time Clock, o reloj de tiempo real. Que lleva la cuenta de segundos minutos y horas además de día mes y año automáticamente, válido hasta el año 2100.
  • 56 byte de memoria RAM respaldada por una batería exterior, que mantiene la fecha y hora cuando no hay corriente.
  • Detección automática de corte de corriente y cambio a modo batería.
  • Muy bajo consumo, lo que ayuda a que la batería dure entre 5 y 10 años.
  • I2C integrado en el mismo chip.

El DS1307 requiere un cristal de 32.768 Khz, este valor viene dado por el hecho de que 2^15 = 32,768. Esto quiere decir que la frecuencia es divisible binariamente para generar un segundo exacto. El cristal ya se incluye si compramos algún módulo con el DS1307.

El DS1307 requiere dos fuentes de alimentación: Por una parte, requiere alimentación de 5 volts que opera mientras el circuito está encendido y funcionando y otra fuente que proviene de una batería de litio (tipo reloj) que mantiene funcionando el reloj/calendario mientras la alimentación principal NO está disponible. El cambio entre ambas fuentes de alimentación es gestionado por el DS1307 de manera automática.

Disponemos de un pin de salida que puede ser configurado para que el RTC entregue una onda cuadrada con una frecuencia configurable, las frecuencias disponibles se muestran en la siguiente tabla y se configuran mediante los bits RS1, RS0 y SQWE de registro de control, si se usa este pin hay que agregar una resistencia pull-up, ya que es del tipo “Open drain” (solo puede “drenar” corriente no es fuente de corriente)

Los registros del DS1307 almacenan la fecha y la hora en formato BCD. La dirección de cada registro y la información almacenada en cada uno se muestra en la siguiente tabla:

El funcionamiento del chip se controla mediante el bit 7 del registro del segundero (0x00) y el registro de control (0x07): El bit CH del segundero detiene el reloj cuando está en alto (así se entregan los módulos de fábrica) y en este modo NO se lleva el conteo de tiempo porque el oscilador está detenido. ES MUY IMPORTANTE PONER A 0 ESTE BIT PARA QUE EL RTC FUNCIONE. El registro de control maneja la funcionalidad del pin de salida de onda cuadrada. A partir de la dirección 0x08 disponemos de memoria RAM no volátil (NVRAM) cuyo contenido no se borra con la pérdida de energía, este segmento de memoria funciona de manera similar a una memoria EEPROM.

El el DS1307 es uno de los RTC más usados y mejor documentados para Arduino. Características:

El DS1307 devuelve los valores de la hora via I2C en formato decimal codificado en binario (BCD – https://es.wikipedia.org/wiki/Decimal_codificado_en_binario). Para evitar hacer los cálculos y olvidarnos del protocolo I2C disponemos de varias librerías:

Uso del DS1307 con Raspberry Pi: http://www.instructables.com/id/DIY-RTC-DS1307-Module-and-use-with-Raspberry-Pi/?ALLSTEPS

Conexión de DS1307 con Arduino:

Otros RTCs:

Ver los ejemplos de la librería time. Dirección I2C: 0x68

__DATE__ Y __TIME__ son macros estándar predefinidas en C++ y están disponibles en todos lo compiladores que implementan el estándar.  Podemos aprovechar esto para poner en hora el reloj interno corriendo un programa que convierta estos valores a formato fecha time_t y asignarlo al valor del reloj interno, como hace SetTime.

La macro __DATE__ expande a un string que describe la fecha en la que el preprocesador/compilador está corriendo. El string contiene 11 caracteres en el formato “mmm dd yyyy”

La macro __TIME__ expande a un string que describe la hora del preprocesador/compilador. El string contiene 8 caracteres en el formato: “hh:mm:ss”

Ejercicio43-RTC1: Actualizar hora de RTC manualmente por puerto serie.

Modulo que usado para practica: https://www.sparkfun.com/products/12708

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio43-RTC/actualiza_hora_manual

Ejercicio43-RTC2: Mostrar en una pantalla LCD la fecha y hora obtenida del RTC.

Display puerto serie usado: https://www.sparkfun.com/products/9395

Quick Start: https://www.sparkfun.com/tutorials/246

Uso: http://playground.arduino.cc/Learning/SparkFunSerLCD

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/blob/master/Ejercicio43-RTC/Display_Hora/Display_Hora.ino

GPS

Los módulos GPS generalmente también vienen con interfaz I2C

Ejemplo: http://arduinoguruji.blogspot.com.es/p/gps-interfacing-with.html

Datasheet: https://www.u-blox.com/sites/default/files/products/documents/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf

Librerías:

Bus SPI

Ya hemos visto la comunicación serie asíncrona el la comunicación serie con la UART, donde para sincronizar se debe usar la misma frecuencia de reloj en ambos extremos y se añade un par de bits extra a la comunicación en forma de un bit de Start y otro de Stop. (Para enviar 8 bits de datos tenemos que enviar 10 bits).

El SPI es un protocolo de comunicación  síncrona de 4 hilos, entre dispositivos electrónicos presentado por Motorola, ahora Freescale, http://www.freescale.com/ en 1982, que ha ganado bastante aceptación en la industria como sistema de comunicación de muy corta distancia, normalmente dentro la placa de circuito impreso. Es un protocolo de transmisión que permite alcanzar velocidades muy altas y que se diseñó pensando en comunicar un microcontrolador con distintos periféricos y que funciona a full dúplex.

SPI utiliza una solución síncrona, porque utiliza unas líneas diferentes para los datos y el Clock. El Clock es una señal que indica al que escucha exactamente cuándo leer las líneas de datos, con lo que el problema de pérdida de sincronía se elimina de raíz.

Uno de los motivos por los que SPI es tan popular es que el hardware de recepción puede ser un sencillo Shift register, lo que es una solución mucho más simple y barata que una UART (Universal Asíncronous Receiver Transmitter o  sistema universal asíncrono de recepción y transmisión serie) de comunicación serie.

El Bus SPI (del inglés Serial Peripheral Interface) es un estándar de comunicaciones, usado principalmente para la transferencia de información entre circuitos integrados en equipos electrónicos. El bus de interfaz de periféricos serie o bus SPI es un estándar para controlar casi cualquier dispositivo electrónico digital que acepte un flujo de bits serie regulado por un reloj (comunicación sincrónica).

El bus SPI incluye una línea de reloj, dato entrante, dato saliente y un pin de chip select, que conecta o desconecta la operación del dispositivo con el que uno desea comunicarse. De esta forma, este estándar permite multiplexar las líneas de reloj.

Muchos sistemas digitales tienen periféricos que necesitan existir pero no ser rápidos. La ventajas de un bus serie es que minimiza el número de conductores, pines y el tamaño del circuito integrado. Esto reduce el coste de fabricar montar y probar la electrónica. Un bus de periféricos serie es la opción más flexible cuando se tiene tipos diferentes de periféricos serie. El hardware consiste en señales de reloj, data in, data out y chip select para cada circuito integrado que tiene que ser controlado. Casi cualquier dispositivo digital puede ser controlado con esta combinación de señales. Los dispositivos se diferencian en un número predecible de formas. Unos leen el dato cuando el reloj sube otros cuando el reloj baja. Algunos lo leen en el flanco de subida del reloj y otros en el flanco de bajada. Escribir es casi siempre en la dirección opuesta de la dirección de movimiento del reloj.

Más información en:

El bus SPI se define mediante 4 pines:

  • SCLK o SCK : Señal de reloj del bus. Esta señal rige la velocidad a la que se transmite cada bit.
  • MISO(Master Input Slave Output): Es la señal de entrada a nuestro dispositivo, por aquí se reciben los datos desde el otro integrado.
  • MOSI(Master Output Slave Input): Transmisión de datos hacia el otro integrado.
  • SS o CS: Chip Select o Slave Select, habilita el integrado hacia el que se envían los datos. Esta señal es opcional y en algunos casos no se usa.

El funcionamiento para un envío de un Master es el siguiente:

  • Se habilita el chip al que hay que enviar la información mediante el CS (Opcional).
  • Se carga en el buffer de salida el byte a enviar.
  • La línea de Clock empieza a generar la señal cuadrada donde normalmente por cada flanco de bajada se pone un bit en MOSI.
  • El receptor normalmente en cada flanco de subida captura el bit de la linea MISO y lo incorpora en el buffer.

Se repite el proceso 8 veces y se ha transmitido un byte. Si se ha terminado de transmitir se vuelve a poner la linea CS en reposo. Hay que tener en cuenta que a la vez que el Master está enviando un dato también lo recibe así que si el Slave ha depositado algún byte en el buffer de salida, este también será enviado y recibido por el Master, comunicación full-duplex.

La señal de reloj es generada por el master y la línea SS normalmente se mantiene HIGH y se activa con LOW, lo que despierta al esclavo seleccionado. Cuando se termina la transferencia la línea se levanta a HIGH y el esclavo se desactiva.

A diferencia de otros buses el SPI no implementa el nivel del enlace entre dispositivos, es decir no hay un campo para la dirección ni un campo para ACK, etc. El SPI se comporta como un shift register donde a cada golpe de clock se captura un bit. En parte no es necesaria hacer un direccionamiento de los chips ya que mediante la señal Chip select, habilitamos al integrado al que queremos enviar los datos.

Ventajas de SPI sobre I2C.

  • I2C No es Full-Duplex por lo que no permite envíos y recepciones al mismo tiempo.
  • I2C un poco más complejo que SPI.
  • I2C no tiene control de errores, por ejemplo mediante paridad etc. Aunque se puede realizar por Software.
  • Velocidades de comunicación relativamente elevadas. En el caso de Arduino de hasta 8 Mhz.
  • Completo control sobre la trama de bits al no exigir direccionamiento ni ACK.
  • Se requiere un hardware sencillo (Barato)
  • Requiere un menor consumo y menor electrónica de conexión que el I2C
  • Como el Clock lo proporciona el master, los esclavos no necesitan osciladores (más barato)

Desventajas de SPI:

  • No hay control del flujo por hardware.
  • Las comunicaciones tiene que estar perfectamente establecidas de antemano. No puedes enviar mensajes de diferentes longitudes cuando convenga.
  • No hay confirmación de la recepción como ocurre en I2C con el ACK. Es decir no sabemos si el mensaje a llegado al destino.
  • Usa más pines que otros buses, ya que necesita uno por cada esclavo. Eso implica que no hay direccionamiento en la propia trama. A menos que se diseñe por software.
  • Funcionamiento a distancias cortas
  • Master único y casi sin posibilidad de master múltiple

Tanto el bus SPI como el I2C son llamados buses de tarjeta, es decir están pensados para trabajar a distancias pequeñas del entorno de una tarjeta, en caso de necesitar un bus serie a larga distancia hay que ir a buses de campo como RS485, CAN, etc…

El canal SPI fue diseñado para aplicaciones de transmisión de datos a velocidades altas (10 Mbps) y distancias cortas, del orden de 10 a 20 cms, ó bien dentro de un mismo PCB (circuito impreso), entre 2 circuitos integrados como podrían ser un  microcontrolador y otro dispositivo, por ejemplo, un circuito integrado con la función RFID. Las señales de transmisión de datos y control del canal SPI, usan niveles de voltaje TTL ó bien 3.3 volts, dependiendo de la tecnología de fabricación del dispositivo.

Más información en:

La mayoría de los microcontroladores modernos tienen soporte HW para SPI, así como las placas BeagleBoard y Raspberry Pi.

El estándar SPI es un estándar de facto, es decir, no ha sido consensuado ni legitimado por un organismo de estandarización al efecto. Por el contrario, se trata de una norma generalmente aceptada y ampliamente utilizada por iniciativa propia de un gran número de interesados. Por lo tanto cada fabricante puede implementar de forma diferente el SPI y debemos prestar atención al datasheet del dispositivo.

Estandar SPI: http://www.st.com/st-web-ui/static/active/en/resource/technical/document/technical_note/DM00054618.pdf

SPI block Guide: http://www.nxp.com/files/microcontrollers/doc/ref_manual/S12SPIV4.pdf

Cuando el número de esclavos crece suele ser más frecuente conectarlos en cascada, con el MISO de uno (Salida), conectado al MOSI (Entrada) del siguiente. En este caso solo usamos una única línea SS, que se comparte entre todos los esclavos.

Esta configuración es típica de una situación en la que le master envía datos pero no recibe nada de vuelta, como en el caso de una cadena de múltiples display LEDs (Matrices de 8×8), en los que se envía información para ser mostrada pero, no hay datos de vuelta. En este caso incluso podemos desconectar la línea MISO.

Una vez que todos los datos son enviados la línea SS es activada y todos los chips son activados simultáneamente.

SPI en daisy-chaining: http://www.maximintegrated.com/en/app-notes/index.mvp/id/3947

SPI en Arduino

Arduino soporta de serie el bus SPI, con una librería estándar se llama SPI que está incluida en el IDE de Arduino y que gestiona todas las complicaciones y el arbitraje del protocolo.

Para usar el bus SPI en Arduino, se usa la librería: http://arduino.cc/en/Reference/SPI

En un primer paso importamos la librería del SPI con #include <SPI.h>. En el setup hay que iniciar y configurar el SPI con SPI.begin() y además hay que definir el pin SS como salida.

Finalmente mediante la función SPI.transfer enviamos el byte que queremos.

Métodos SPI:

  • begin() — Inicializa el bus SPI
  • end() — Deshabilita el bus SPI.
  • setBitOrder() — Configura el orden de los bits enviados como el menos significativo primero o el más significativo primero.
  • setClockDivider() — Configura del divisor de reloj en el bus SPI. ES decir configura la velocidad del bus.
  • setDataMode() — Configura el modo de dato del bus SPI, es decir, polaridad y fase del reloj.
  • transfer() — Transfiere un byte sobre el bus SPI, tanto de envío como de recepción.
  • SPI settings: https://www.arduino.cc/en/Reference/SPISettings

Para controlar el bus SPI es necesario usar esos unos pines inexcusablemente, aunque podemos elegir entre dos juegos de ellos, entre ciertos pines digitales, según el modelo y la tabla que especificamos abajo, y usando los equivalentes en el bus ICSP. La siguiente tabla muestra que pines corresponden a los líneas SPI en cada Arduino:

Arduino / Genuino Board MOSI MISO SCK SS (slave) SS (master) Level
Uno or Duemilanove 11 or ICSP-4 12 or ICSP-1 13 or ICSP-3 10 5V
Mega1280 or Mega2560 51 or ICSP-4 50 or ICSP-1 52 or ICSP-3 53 5V
Leonardo ICSP-4 ICSP-1 ICSP-3 5V
Due ICSP-4 ICSP-1 ICSP-3 4, 10, 52 3,3V
Zero ICSP-4 ICSP-1 ICSP-3 3,3V
101 11 or ICSP-4 12 or ICSP-1 13 or ICSP-3 10 10 3,3V
MKR1000 8 10 9 3,3V

Los pines ICSP son los pines del conector ICSP de Arduino que tienen la siguiente disposición:

Todas las placas con microcontroladores AVR tienen un pin SS que se usa cuando actúa como esclavo. Dado que esta librería solo soporta el estado maestro, este pin debe ponerse siempre como output, sino el interfaz SPI podría ponerse como esclavo. Sin embargo, es posible usar el pin SS para dispositivos, por ejemplo el pin 4 y 10 son usados para controlar las conexiones de la Ethernet Shield.

En el Arduino Due, el interfaz SPI funciona diferente al resto de Arduinos. Este tiene 3 pines para dispositivos SS. Uso extendido de la librería SPI en Due: http://arduino.cc/en/Reference/DueExtendedSPI

Para microcontroladores “Microchip PIC” y “ARM-based” podemos tener varios modos de funcionamiento:

SPI Mode Clock Polarity

(CPOL/CKP)

Clock Edge

(CKE/NCPHA)

0 0 1
1 0 0
2 1 0
3 1 1

Uso del bus SPI: https://www.pjrc.com/teensy/td_libs_SPI.html

Un magnífico ejemplo de como usar el bus SPI en los microcontroladores AVR y uso con registros de desplazamiento: http://www.ermicro.com/blog/?p=1050

Más información:

Registros Arduino para SPI

Internamente el microcontrolador ATmega328p tiene un bus SPI que puede trabajar como master o slave. Para manejar internamente este bus, se hace uso de una serie de registros.

En la página 215 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf puede verse el funcionamiento del bus SPI en el microcontrolador.

Características del bus SPI en Arduino:

  • Full-duplex, Three-wire Synchronous Data Transfer
  • Master or Slave Operation
  • LSB First or MSB First Data Transfer
  • Seven Programmable Bit Rates
  • End of Transmission Interrupt Flag
  • Write Collision Flag Protection
  • Wake-up from Idle Mode
  • Double Speed (CK/2) Master SPI Mode

Diagrama de bloques:

spi-block-diagram

Los microcontroladores AVR tienen los siguientes tres registros para manejar SPI:

  • SPCR – SPI Control Register – This register is basically the master register i.e. it contains the bits to initialize SPI and control it.

  • SPSR – SPI Status Register – This is the status register. This register is used to read the status of the bus lines.

  • SPDR – SPI Data Register – The SPI Data Register is the read/write register where the actual data transfer takes place.

Los 4 modos disponibles de funcionamiento de SPI son:

Velocidad del bus SPI:

De una forma más detallada, el funcionamiento es de la siguiente forma: el SPI Master (servidor) inicializa el ciclo de comunicación cuando se coloca en bajo el Selector de Esclavo (SS-Selector Slave)(cliente). Master y Slave(servidor y cliente) preparan los datos a ser enviados en sus respectivos registros de desplazamiento y el Master genera el pulso del reloj en el pin SCK para el intercambio de datos. Los datos son siempre intercambiados desde el Maestro al Esclavo en MasterOut-SlaveIn, MOSI, y desde Esclavo al Maestro en MasterIn-SlaveOut, MISO. Después de cada paquete de datos el Maestro debe sincronizar el esclavo llevando a ‘alto’ el selector de Esclavo, SS.

Cuando se configure como Maestro, la interfaz SPI no tendrá un control automático de la línea SS. Este debe ser manejado por software antes de que la comunicación pueda empezar, cuando esto es realizado, escribiendo un byte en el registro de la SPI comienza el reloj de la SPI, y el hardware cambia los 8 bits dentro del Esclavo. Después de cambiar un Byte, el reloj del SPI para, habilitando el fin de la transmisión (SPIF). Si la interrupción del SPI está habilitado (SPIE) en el registro SPCR, una interrupción es requerida. El Master podría continuar al cambio del siguiente byte escribiendo dentro del SPDR, o señalizar el fin del paquete colocando en alto el Esclavo seleccionado, línea SS. El último byte llegado se mantendrá en el registro Buffer para luego usarse.

Cuando lo configuramos como un Esclavo, la interfaz SPI permanecerá durmiendo con MISO en tres-estados siempre y cuando el pin SS esté deshabilitado. En este estado, por el software se podría actualizar el contenido del registro SPDR, pero los datos no serán desplazados por la llegada del pulso de reloj en el pin SCK hasta que el pin SS no sea habilitado( ‘0’ ). Será visto como un byte completamente desplazado en el fin de la transmisión cuando SPIF se habilite. Si la interrupción SPI, SPIE en SPCR, está habilitada, una interrupción es solicitada. El Esclavo podría continuar para colocar nuevos datos para ser enviados dentro del SPDR antes de seguir leyendo la data que va llegando. El último byte que entra permanecerá en el buffer para luego usarse.

Más información:

Dispositivos SPI

Los dispositivos SPI se comunican entre sí utilizando un bus de 4 señales (MOSI, MISO, SCK, SS) y un esquema maestro/esclavo, en el cual el maestro inicia el protocolo de trasmisión de los datos. En ocasiones, las interfaces SPI son circuitos que están ya disponibles como parte del hardware en los microcontroladores como por ejemplo el 18F2550 (Microchip) ó Atmega8 (Atmel-AVR) ó bien en dispositivos como módulos lectores RFID, tarjetas de memoria, convertidores A/D, etc.

Diferentes tipos de periféricos con SPI: http://www.mct.net/faq/spi.html

Aplicaciones SPI : http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Applications

Varios dispositivos con interfaz SPI: https://www.sparkfun.com/search/results?term=spi

Veamos varios ejemplo de dispositivos SPI:

Adaptadores

También disponemos de adaptadores o bridges de SPI a otros puertos.Por ejemplo de USB a SPI: http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Host_adapters

Conversor de USB a SPI: http://www.maximintegrated.com/en/products/interface/controllers-expanders/MAX3421E.html

Otro conversor SPI a USB: https://www.sparkfun.com/products/9235

SPI to serial (UART):

SPI to I2C:

ISP (In Sytem Programming)

Recordar que el conector ICSP se usa  para hacer la programación del microcontrolador y que comparte los pines. In system programming:

Por ejemplo un ISP: http://www.pololu.com/product/1300. Lleva este PIC http://ww1.microchip.com/downloads/en/DeviceDoc/41350E.pdf con un firmware para realizar esta función.

Y podemos programar un microcontrolador por el conector ICSP:

Ejercicios SPI Arduino

Ejemplos de uso de SPI:

Ejercicio34-SPI: Comunicar dos arduinos por SPI.

Cómo hacerlo:

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio36-ArduinoISP