Me considero un aprendiz en temas de Arduino y quiero que esta serie de artículos tengan a la vez un valor testimonial y un valor divulgativo. Después de un tiempo programando mi Arduino va siendo hora de comentar algunas cosillas. Se trata de compartir los conocimientos que voy adquiriendo.
Probablemente no interesará a todo el mundo porque no todo el mundo querrá construir un controlador por ordenador para su acuario, pero más de uno puede sentir curiosidad por esta maravilla de «juguetito».
Por ello, y sin pretender ser otra cosa que un aprendiz y con ánimo divulgativo y de compartir, me propongo hacer un resumen de algunos de los temas de software de Arduino con los que me he ido tropezando.
El entorno de programación de Arduino:
Pese a lo interesante de Arduino, y a que su entorno IDE no está nada mal, (merece mucho la pena usarlo), he notado algunos pequeños fallos de madurez y que suponen algún que otro inconveniente a los que uno ha de acostumbrarse. A esto hay que sumar que trabajar con el cacharrito de Arduino tiene una serie de particularidades.
El lenguaje Arduino no usa un lenguaje C totalmente estandar. El lenguaje propuesto para trabajar con Arduino es una variante del lenguaje C que tiene sus peculiaridades y de vez en cuando tropiezas con alguna sorpresita que otra.
Tengo entendido que en lugar del IDE de Arduino también se puede usar Eclipse con un plugin para AVR, o el IDE de NetBeans con una adecuada configuración.
El IDE de Arduino está escrito en Java y se basa en avr -gcc y en otros programas todos ellos de software libre. Mi experiencia se limita al uso de este software desde un equipo Linux.
Yo para la edición de código uso el editor Vim ya que el editor que trae el IDE es muy básico, pero seguro que continuará mejorando. Podeis descargar el IDe de Arduino en Download the Arduino Software.
He tenido algunos problemas que me tenían muy desconcertado. Concretamente en el manejo de variables grandes me han ocurrido algunos comportamientos arbitrarios y al reducir el tamaño de ciertas variables el programa funcionaba bien. No descarto estar haciendo algo mal, pero desconfío de la gestión de memoria después de ver cosas como estas: Problems with using large constant arrays with AVR/Arduino
El compilador parece atascarse algunas veces y puede requerir lanzarlo varias veces hasta que logra compilar el fuente y ello pese a no realizar ningún cambio en el mismo. También el IDE se me ha colgado alguna vez. Los mensajes de errores de compilación no siempre resultan demasiado orientativos.
El proceso avrdude algunas veces se desmadra y se queda consumiendo un montón de recursos del ordenador incluso despues de salir del IDE y hay que matarlo haciendo un killall avrdude para que el disco duro del ordenador recupere su tranquilidad.
En este entorno de programación no podemos usar escritura de trazas en pantalla ni en disco duro debido a que Arduino no tiene ni una cosa ni otra. A tal fin las trazas de ejecución para depurar programas se envían generalmente al puerto serie y el IDE facilita un monitor de entrada salida Serial muy útil a tal efecto. Vendría muy bien incluir en este monitor la posibilidad de usar copy paste, pero no funciona.
En Arduino, la versión por defecto de la función sprintf() no contempla el uso de formatos de coma flotante. Si se intenta usar dichos formatos aparece un signo de interrogación. Esto se debe a que la librería libc AVR no soporta el uso de coma flotante en ninguna de las funciones de tipo xxprintf(). El motivo de ello es el ahorro de código en estas librerías dadas las limitaciones de memoria de muchas placas de Arduino. En mi caso he recurrido a una implementación del paso de float a formato de cadena de caracteres: char * float2s(float f, unsigned int digits)
No son problemas graves pero son indicativos de que hay mucho trabajo por delante. El entorno actualmente no facilita una productividad demasiado buena. La programación se hace algo incómoda con esta serie de detallitos y nuestro proyecto CAO es un proyecto que necesitará un desarrollo importante de software. A cambio de esto las librerías que se estan contribuyendo para Arduino son muy interesantes y permiten el uso de un montón de hardware con los más diversos protocolos de comunicación.
Pasemos pues a hablar de este punto fuerte de Arduino. Vamos a comentar el uso de algunas de sus librerías más interesantes con vistas a nuestro proyecto CAO.
Librería Serial: (Esta librería está incluida y no necesita ser importada)
El protocolo de serie asíncrono es un protocolo de comunicación tradicional y común en el mundo de la electrónica y de la informática. Es utilizado por los controladores y dispositivos de comunicación de datos. El protocolo se implementa mediante un dispositivo llamado UART o USART (Receptor Asíncrono Universal / Transmitter). Cuando un dispositivo se comunica usando el protocolo Serial, su UART transmite datos sobre la línea «TX» y recibe datos sobre la línea «RX».
La comunicación Serial significa que sólo un bit de información se envía en un preciso instante (en oposición a la comunicación paralela). Muchos de los dispositivos se comunican en serie. Una de la variantes más conocidas es el RS-232 pero Arduino no lo usa porque las tensiones que usa el RS232 son de (+/-) 12 voltios. La mayoría de los ordenadores actuales ya tampoco suelen usar puertos de comunicación RS232 porque están siendo sustituidos ventajosamente por el uso de puertos USB.
En Arduino podemos usar el USB para enviar y recibir comunicaciones serie. Esta es la forma en que Arduino se comunica con el ordenador habitualmente. El puerto serie de Arduino o UART (transmisor universal receptor asíncrono) se encuentra en los pines digitales 1 y 0 respectivamente (RX) y (TX), que reciben y envían datos también respectivamente. Tienen LEDs correspondientes etiquetados RX y TX que parpadean cuando los datos circulan por la línea.
Cuando se utiliza un puerto serie con un dispositivo en particular, no se podrá usar para conectarlo simultaneamente a otros dispositivos, porque el protocolo no incluye direccionamiento. Se trata de una comunicación bidireccional entre dos únicos puntos.
El puerto serie es en muchos casos la forma más natural de comunicación de Arduino con el exterior. Ya hemos comentado que las trazas de ejecución se envían al puerto serie y basta abrir el monitor serial en el IDE para verlas. Este monitor actuará como un terminal de pantalla conectado al puerto serie. También se puede enviar mensajes a Arduino a través del puerto serie por el uso de la caja de texto en la pantalla.
Nosotros usaremos en CAO este puerto para enviar una serie de mensajes de distinto tipo. Concretamente serán: Mensajes de traza de ejecución para depuración de errores, mensajes de errores de distinto tipo atendiendo a su gravedad, envió bidireccional de comandos y respuestas a los mismos, y Pings a intervalos de 5 segundos para transmitir toda la información importante que permita al interfaz Web un refresco de la información en pantalla.
Nuestro protocolo se basará en una idea sencilla. Arduino debe de ser capaz de funcionar sin la colaboración del servidor Raspberry Pi.
Los mensajes deberán tener una estructura que facilite la gestión de los mismos por parte de Raspberry Pi. Incluirán la información de fecha y hora. Los mensajes se leerán como líneas de texto completas en las cuales existirán ciertos campos separados por un carácter especial. En nuestro caso usaremos el caracter ‘|’. El primer campo será un caracter que determinará el tipo de mensaje que estamos enviando. Una caracteristica de los mensajes de traza es que pueden ser desactivados, además incluirán un campo en el que figure la cantidad de memoria disponible porque el lenguaje C se presta a cierto tipo de errores (Memory leaks) que van agotando poco a poco la memoria hasta generar un fallo total.
Necesitamos poder detectar de forma visual los bloqueos del sistema. En nuestro proyecto CAO, usamos un Arduino Mega y el software hará parpadear el led de la placa asociado al pin 13 para indicar que el bucle principal está funcionando correctamente y que el bucle loop() no ha quedado bloqueado en ningún punto.
Gracias a ello podemos comprobar que la escritura en el puerto serie no es bloqueante. Esto resultaría un serio problema para nosotros y para la mayoría de los usos en Arduino. Por ello, si se perdiera la comunicación serie, Arduino continuaría funcionando con normalidad y esas escrituras simplemente se perderían.
Si una vez perdida la comunicación serie, esta se recuperara posteriormente, sucederá un reinicio automático equivalente al que ocurre cuando encendemos Arduino o cuando pulsamos reset para reiniciar.
Si se pone alguna traza dentro de la función setup() comprobaremos que esta se ejecutará nuevamente. Esto no es obra nuestra. Es el comportamiento normal de Arduino.
Si quisieramos prevenir este comportamiento, se podría conectar mediante una resistencia de 100 ohmios el pin reset con el pin +5V, pero no suele hacer falta. Nosotros en CAO tampoco lo necesitamos. Dado que en CAO el puerto serie se usará para conectar Arduino con el servidor Raspberry Pi, la reconexión del puerto serie, nos interesa que provoque ese reinicio de todo el sistema y eso es lo que sucederá sin que tengamos que programar nada para ello.
Esto en nuestro caso no afectará a la fecha y hora del sistema ya que en CAO dispondremos de un reloj de tiempo real (RTC) con su propia pila tipo botón y de larga duración, para mantener la hora aunque falle el fluido eléctrico.
Los programadores de luz para acuarios muchas veces son electromecánicos y se paran en ausencia de fluido eléctrico introduciendo retrasos en el horario después del apagón. En nuestro CAO, tenemos que asumir que pueden ocurrir caidas por cortes de suministro eléctrico o por perdida de comunicaciones, etc. y el sistema debe de ser capaz de reiniciar con normalidad y continuar como si nada prosiguiendo con las tareas propias en función del horario establecido.
La librería Wire nos proporciona Serial en los pines 0 (RX) y 1 (TX) pero la placa Arduino Mega utilizada en nuestro proyecto CAO tiene tres puertos adicionales de serie que son: Serial1 en los pines 19 (RX) y 18 (TX), Serial2 en los pines 17 (RX) y 16 (TX), Serial3 en los pines 15 (RX) y 14 (TX). Para utilizar estos pines para comunicar con un ordenador personal, necesitarás un adaptador USB adicional conectado a esos pines.
Ejemplo para Arduino Mega:
// Arduino Mega usando sus 4 puertos serie // (Serial, Serial1, Serial2, Serial3), // con diferentes velocidades de datos: void setup(){ Serial.begin(9600); Serial1.begin(38400); Serial2.begin(19200); Serial3.begin(4800); Serial.println("Hola ordenador"); Serial1.println("Hola Serial 1"); Serial2.println("Hola Serial 2"); Serial3.println("Hola Serial 3"); }
Por el momento en nuestro proyecto CAO el único puerto que usaremos será el Serial (Pines 0 y 1) pero hemos documentado los restantes puertos para que la explicación quedara completa.
Un posible uso para alguno de estos otros puertos podría ser el de utilizar un Display LCD de tipo Serial conectado a alguno de estos puertos. En tal caso el Serial comunicaría con Raspberry Pi y Serial1 con el Display LCD por ejemplo. Para una versión de CAO sin interfaz Web podría resultar útil este enfoque, de hecho ha proyectos de controladores de acuarios que lo usan.
Hay displays LCD que usan distintos protocolos de comunicación. Antes de comprar uno de estos Displays comprueba que protocolo y que librería Arduino necesita ese modelo en particular. Por desgracia los componentes para Arduino no siempre se ofrecen acompañados de una información completa cosa muy necesaria tratándose de un campo donde no todo el mundo que usa estos cacharritos es un experto en electrónica. Sé precavido antes de comprar.
Yo por el momento no tengo pensado poner a mi controlador CAO ni botones, ni conmutadores ni displays LCD ni nada. Lo estoy diseñando como si fuera una caja negra que podrá ser integramente manejada desde un interface Web desde cualquier lugar.
Lo que NO deseo hacer son cosas como las que podeis ver en este vídeo: Arduino Aquarium Introduction.
El acuario es precioso y la instalación parece muy completa, pero fijaros la cantidad de interruptores que usa. No solo necesita más hardwarwe, sino que te ata a ese lugar para su manejo. Los botones interruptores y mandos en general nosotros los implementaremos con software y los situaremos en una página web.
Sí que necesitaremos incluir sensores, relés, y dispositivos que inevitablemente tienen que interactuar con el acuario, pero casi toda la interacción con la aplicación CAO se hará mediante comandos software.
Librería Wire: También conocida como: Two Wire Interface (TWI/I2C)
Permite comunicar con dispositivos que usan el protocolo I2C (o TWI). Es un protocolo de comunicación serie que se usa para comunicar por ejemplo sensores con microcontroladores. Se asume que además de compartir entre ellos la misma conexión a masa, existirá un par de líneas (SDA y SCL) de comunicación bidireccional entre sensores y microcontrolador. A diferencia de la comunicación Serial, los dispositivos son direccionables y se conectan en paralelo a estas líneas (bus I2C):
- SDA (Serial Data Line): Linea de datos.
- SCL/CLK (Serial Clock Line): Linea de reloj que marcará el tiempo de RW (Lectura/Escritura)
En estas dos únicas líneas SDA SLC (bus IC2) se pueden conectar varios periféricos ya que cada dispositivo estará identificado por una dirección diferente. En muchos sensores para Arduino se comparte además una línea para la alimentación a (+5V).
También en este bus se pueden conectar varios controladores (en nuestro caso podrían conectarse si lo deseáramos varios Arduinos) pero siempre uno de ellos deberá cumplir el papel de maestro y todos los demás deberán estar en modo esclavo.
La librería Wire de Arduino usa siempre 7 bits de direccionamiento. (Ojo porque hay dispositivos I2C que usan direccionamiento de 8 bits y no funcionarían con Wire).
Cada dispositivo debe tener una dirección única en el intervalo de 8 a 119. La dirección 0 está reservada como una dirección de broadcast (emisión a todos los nodos del bus). Las direcciones 1 a 7 están reservadas para otros fines, y las direcciones de la 120 a la 127 están reservadas para futuros usos.
Wire tiene dos forma de inicialización:
- Wire.begin() inicializa como maestro.
- Wire.begin(Dirección) inicializa como esclavo en esa dirección.
En la mayoría de las placas Arduino, la línea SDA (línea de datos) usa el pin analógico 4, y la línea SCL (línea de reloj) usa el pin analógico 5, pero en Arduino Mega, se evita gastar estos pines y para SDA se usa el pin digital 20 y para SCL el pin digital 21. Son pines digitales cuya modificación consiste en que internamente tienen unas resistencias pull-up que proporcionan un nivel alto en ausencia de señal. Se podrían usar otros pines digitales colocando externamente estas resistencias pull-up son dos de 10kohmios. Una sitiada entre SDA y +5Vcc y la otra entre SCL.
En nuestro proyecto usamos Arduino Mega, y no tendremos que hacer nada salvo conectar SDA y SCL en los pines 20 y 21.
Nuestro reloj será un RTC que use I2C y usaremos la librería RTClib. Ya he trabajado con ello y funciona muy bien. En la función setup() usaremos la secuencia:
- Wire.begin();
- RTC.begin();
Por el momento no usaremos otros dispositivos con I2C pero quizás en un futuro podríamos usar por ejemplo algún sensor digital que sí lo use. El uso del RTC con I2C me ha resultado muy sencillo y muy cómodo.
Para más detalles sobre este protocolo veasé INTRODUCCIÓN AL I2C en Arduino.
Dallas Semiconductor’s 1-Wire Protocol:
Controla dispositivos (de Dallas Semiconductor) que usan el protocolo One Wire.
Cada dispositivo 1-Wire contiene un código único de 64 bits (código almacenado en rom), que consiste en un código de 8 bits de la familia, el número de serie de 48 bits y un código de redundancia CRC de 8 bits para verificar la integridad de los datos.
El bus 1-Wire es un bus de maestro único y múltiples esclavos. El conexionado de los esclavos se realiza en paralelo entre la línea de datos-alimentación y tierra. Además, es un bus definido como AND-wired. Esto significa que si todos los dispositivos (incluido el maestro) ponen una tensión de 5V en el bus, el resultado es 5V en el cable; pero como solamente uno de ellos fuerce 0V en el bus, el resultado serán 0V. Antes de enviar un comando a un dispositivo esclavo, el maestro debe seleccionar primero el dispositivo utilizando su código almacenado en su rom.
Los sensores de temperatura 1-Wire son muy populares, por su bajo costo y facilidad de uso. Proporcionan directamente lecturas de temperatura digitales calibradas. Son tolerantes al uso de cables largos entre el sensor y Arduino. De momento no he trabajado con ningún dispositivo de este tipo. Nosotros necesitamos sensores de temperatura sumergibles mucho más escasos y algo más caros.
Entradas analógicas:
Nuestros primeros prototipos usaran sensores de este tipo. Son sensibles a la longitud del cable y requieren ser calibrados previamente, pero también son los más baratos. En muchos casos se usan sensores que o bien entregan directamente un voltaje variable que será medido por Arduino (es el caso de algunos sensores de PH y de potencial Redox) o bien varían su resistencia y deberán ser insertados en un divisor de tensión para entregar un voltaje que variará dependiendo de su resistencia. Este es el caso de los termistores y de los fotoresistores.
La calibración consistirá en mapear una serie de datos de entrada con datos de salida conocidos. A partir de ellos podremos interpolar cualquier otro valor intermedio dentro de un rango de valores. Los datos que Arduino nos va a entregar dependiendo del voltaje detectado en uno de sus pins de entrada analógicos será siempre un valor entre 0 y 1023.
Las lecturas vendrán aleatoriamente ligeramente alteradas por una serie de factores. Uno de ellos son las interferencias recogidos por el cable del sensor. Otras podrían ser debidas a la temperatura en el cable. Para minimizar el efecto de las interferencias lo que se hará es implementar una lectura múltiple para obtener así un valor promedio mucho más preciso y estable. A tal fin nuestras rutinas manejarán un decimal más devolviendo valores entre 0 y 10230 que serán los que tendremos que mapear con los valores de salida que deseamos obtener.
Por ejemplo en el caso de un termistor NTC traduciríamos los datos obtenidos a grados centígrados. No podemos obtener mucha precisión en este tipo de lecturas ya que estamos limitados al rango de valores que hemos comentado.
No he considerado necesario trabajar en coma flotante para evaluar sensores analógicos. Los valores de salida los obtendremos como valores enteros y la coma en caso necesario se colocaría posteriormente. Concretamente para 27.3 ºC entregaríamos el valor 273 y la coma se colocaría a la hora de representarlo en pantalla. Mis primeras batallitas con sensores las estoy sufriendo con este tipo de sensores.
A medida que aprenda cosas nuevas, ya os iré comentando.
oscar
Hola Antonio,
también estoy aprendiendo Arduino y me he encontrado con algunos de tus problemas,
sobre la memoria, ten en cuenta que al RAM es muuuy limitada,
es posible que haya algún bug, pero si quieres puedes comprobar en cualquier momento la memoria libre añadiendo esta función (quizá ya la conozcas):
//This function calculates the RAM memory left
int mem(){
uint8_t * heapptr, * stackptr;
stackptr = (uint8_t *)malloc(4);
heapptr = stackptr;
free(stackptr);
stackptr = (uint8_t *)(SP);
return stackptr – heapptr;
}
y llamándola con:
Serial.println(mem());
yo la suelo tener en el loop();
para asegurarme de que no introduzco alguna función que no libera correctamente toda la memoria que utiliza, y que pueda suponer un riesgo a la larga 🙂
Sobre el Monitor Serial del IDE…
en mi caso como a veces tengo más de un Arduino a la vez, no me servia el monitor del IDE, este script sencillo de python te servirá para leer el puerto serie y poder hacer c&p…. o puedes modificar el script para procesar los datos y guardarlos en un fichero…
https://dl.dropboxusercontent.com/u/3093875/readSerial.py
aparte del sensor de temperatura y luz…
¿qué otros sensores tienes pensados? ¿los has encontrado en algún sitio a un precio asequible?
Saludos 🙂
Antonio Castro
Muchas gracias por tus aportaciones. Tengo incorporado dentro de uno de mis módulos la función freeMemory() que devuelve la cantidad de memoria libre. Se trata de una función algo más sofisticada que la que indicas pero el resultado debe de ser similar.
La he sacado de aquí, y el módulo donde la uso, entre otras cosas me sirve para poner trazas, las cuales incluyen siempre un campo donde se indica la cantidad de memoria libre. La función para todo el que le intereses es esta.
/*** *** *** *** *** *** *** *** *** *** *** ***/
#if (ARDUINO >= 100)
#include
#else
#include
#endif
extern unsigned int __heap_start;
extern void *__brkval;
// * The free list structure as maintained by the
// * avr-libc memory allocation routines.
struct __freelist {
size_t sz;
struct __freelist *nx;
};
// * The head of the free list structure
extern struct __freelist *__flp;
// — #include «MemoryFree.h»;
// * Calculates the size of the free list
int freeListSize() {
struct __freelist* current;
int total = 0;
for (current = __flp; current; current = current->nx) {
total += 2; // * Add two bytes for the memory block’s header
total += (int) current->sz;
}
return total;
}
int freeMemory() {
int free_memory;
if ((int)__brkval == 0) {
free_memory = ((int)&free_memory) – ((int)&__heap_start);
} else {
free_memory = ((int)&free_memory) – ((int)__brkval);
free_memory += freeListSize();
}
return free_memory;
}
/*** *** *** *** *** *** *** *** *** *** *** *** ***/
Me estoy reservando para publicar mi código a que este esté un poco más estable y así poder cerrar una versión.
Gracias por las líneas en Python. Yo en el lado de Raspberry pi creo que tendré un proceso python dedicado a leer lo que le mande arduino y registrarlo en tablas SQL que serían leídas más tarde desde el interface web en PHP.
Voy avanzando despacito tanto en la parte hardware como en la parte software.
Sobre sensores hay un proyecto de hardware y software libre que permite leer sondas de PH y sonda de potencial redox del acuario. Se basa en el uso de amplificadores operacionales y estos necesitan alimentación simétrica. Dado que consumen muy poco no necesitan un alimentador para eso. Lo que hacen es implementar un inversor de voltaje. (supongo que usan un oscilador y luego rectificar la señal y la filtran).
El proyecto es muy interesante se llama Phduino. Puedes verlo en http://code.google.com/p/phduino/
Viene la información para hacérselo, pero me gustaría saber si alguien vende ya plaquitas de esas y por cuanto. No he encontrado nada en ese sentido.
Estoy fabricándome un sensor de temperatura sumergible con un Termistor NTC y estoy buscando la forma de construir un medidor de conductividad. He pensado usar dos electrodos de grafito.
Gracias por compartir. Si tienes curiosidad por más cosas estaré encantado de contestarte dentro de mis limitaciones ya que no soy un experto.
oscar
Todo el tema de electrónica a tan bajo nivel (amplificadores, electrodos,…) y construirme mi propio PhDuino creo que ahora mismo queda fuera de mi alcance…
por otro lado (quizá diga alguna tontería… jeje), para el lector de Ph, sería posible desmontar algo como esto (10€)
http://www.amazon.co.uk/EiioX-Digital-Water-Tester-Aquarium/dp/B008KWTYAC/ref=sr_1_1?ie=UTF8&qid=1366800270&sr=8-1&keywords=ph+meter
y tomar de este sensor lo que nos interesa?
saludos : )
Antonio Castro
No sabía que fueran tan baratos. Supongo que podría hacerse, pero ignoro como de complicado o entretenido sería hackearlo, ni la calidad de esos cacharrillos. Si los chips entregan la señal directamente al display habría que pillar la señal de cada uno de los segmentos de cada dígito y decodificar todo eso. Las soldaduras de tanto cablecito sobre ese aparatito miniatura serían muy complicadas. También cabe la posibilidad de que por ese precio te estén vendiendo un cacharrito con obsolescencia programada o con una calidad poco recomendable. Tarde o temprano tendré que probar alguna solución para medir el PH. No tengo aún nada decidido.
oscar
Gracias por explicar y documentar todo 🙂
Antonio Castro
Muchas gracias a ti Oscar por tu interés.
Un saludo.
piensos para perros
Lo digo con el corazón en la mano: escribes formidable
Antonio Castro
Lo tomo como un cumplido y como tal lo agradezco, pero soy consciente de mis muchas carencias.