Domotica en Un Invernadero
Domotica en Un Invernadero
Domotica en Un Invernadero
Sensorización y domotización de
sistema de riego.
Rubén Jorge López-Tarruella Pereo
Grado en ingeniería informática
Arduino
Junio de 2018
i
Esta obra está sujeta a una licencia de
Reconocimiento-NoComercial-
SinObraDerivada 3.0 España de Creative
Commons
ii
FICHA DEL TRABAJO FINAL
iii
Abstract (in English, 250 words or less):
The project aims to address the existing updating needs for current irrigation
systems. These systems do not satisfactorily cover the programming needs of
new generation crops such as hydroponic irrigation, as well as poor sensorization
and the impossibility of telematic interaction at a low cost.
The device is developed using the Arduino platform. In addition, the device has
air temperature and humidity sensors, soil humidity sensors (one per controlled
zone), and web server capability connected via Ethernet cable, which facilitates
the interaction with the device to configure it and check the alarms recorded by
the sensors.
The device allows setting alert values for the sensors of temperature, air humidity
and soil moisture, as well as schedule starting times (up to four different starting
times) or adjusting the frequency (hydroponic cultivation could require more than
four times a day) and independent irrigation duration, completely independent for
each zone.
In conclusion, IoT devices are becoming more widespread thanks largely to
Arduino’s ability to develop low cost devices that fulfill the needs of any field, not
only partially as with commercial devices.
iv
Índice
1. Introducción .................................................................................................... 2
1.1. ¿Qué es un riego? .................................................................................... 2
1.1.2. Sistemas de riego tradicionales ....................................................... 2
1.1.3. Sistemas actuales de riego .............................................................. 2
1.2. Sistemas programadores de riego ........................................................... 4
1.2.1. Sistemas electromecánicos y electrónicos....................................... 4
1.2.2. Sistemas de riego inteligentes ......................................................... 5
1.3. ¿Qué es Arduino? .................................................................................... 5
1.3.1. ¿Por qué Arduino? ........................................................................... 5
2. Objetivos del proyecto .................................................................................... 7
3. Descripción del proyecto ................................................................................ 9
4. Estudio de viabilidad económica .................................................................. 11
5. Planificación del proyecto ............................................................................. 13
5.1. Primera aproximación............................................................................. 13
5.2. Planificación temporal ............................................................................ 14
6. Diseño y desarrollo ....................................................................................... 16
6.1. Desarrollo Hardware............................................................................... 16
6.1.1. Componentes seleccionados ......................................................... 16
6.1.2. Esquema de conexiones ................................................................ 20
6.1.3. Problemas encontrados ................................................................. 22
6.2. Desarrollo Software ................................................................................ 23
6.2.1. Entorno de desarrollo ..................................................................... 23
6.2.2. Fichero de configuración ................................................................ 23
6.2.3. Test de hardware y librerías ........................................................... 23
6.2.4. Estructura del programa................................................................. 25
6.2.5. Problemas encontrados ................................................................. 39
7. Conclusiones ................................................................................................ 41
7.1. Versiones futuras ................................................................................... 41
8. Bibliografía ................................................................................................... 42
1
1. Introducción
Se diseñará un sistema de control de riego automático que dispondrá de
distintos tipos de sensores (humedad del aire, temperatura y humedad de la
tierra). Dichos sensores, unido a los parámetros configurados (horario de riego,
duración, repetición, etc.) decidirá de forma autónoma si se ha de realizar un
riego no.
Haremos una breve introducción de los distintos sistemas de riego, y algunas
particularidades de los mismos.
2
- Por goteo o riego localizado.
- Por hidroponía.
[1]
1.1.3.1. Riego por aspersión o difusión
En este tipo de sistema los elementos de riego son unas turbinas, aspersores
o difusores. Aunque la utilización variará en función de las necesidades de cada
zona, como elemento común destacar que se tratan de elementos de riego
sectoriales. Es decir, cada elemento abarca unos determinados metros de radio
a los que regará cuando funcionen. Esto quiere decir que los elementos deberán
de ir sucediéndose a lo largo de la zona que se quiera regar hasta cubrir toda la
extensión.
El tiempo de riego de unas zonas a otras con este tipo de riego puede variar,
así como las veces que es necesario regar al día según las necesidades del
terreno. En el caso de un jardín, no excederá de 3 o 4 veces al día y unos minutos
cada vez.
[2]
1.1.3.2. Riego por goteo o riego localizado
Los elementos de riego en estas instalaciones son unos goteros que son
instalados justo en las plantas que queramos que sean regadas. Al ser una
acción localizada el consumo de agua se reduce considerablemente, al no ser
un riego indiscriminado. Está muy extendido su uso en la agricultura, sobre todo
en aquellos terrenos más secos donde los recursos de agua son muy limitados.
Para estos tipos de riego, la programación (tiempo de riego y horarios) varía
al igual que en el caso anterior. Lo normal es una programación de un par veces
al día de unos minutos cada una.
[3]
1.1.3.3. Riego por hidroponía
Este tipo de riego, cada vez más extendido en la agricultura, es algo especial
y diferente a los vistos anteriormente. En este tipo de riego las plantas no se
encuentran plantadas en tierra (no usan como sustrato la tierra), sino que en su
lugar se usa fibra de coco, u otros elementos que directamente sustentan a la
planta. En el riego por hidroponía la planta toma los nutrientes directamente del
agua (solución nutritiva), por lo que tiene características especiales con respecto
a los vistos anteriormente.
Las necesidades de riego de alguno de este tipo de riego, requieren que el
riego se produzca cada pocos minutos, dado que se trata de mantener hidratada
la planta en todo momento y vaya cogiendo los nutrientes de forma paulatina. El
agua en ningún caso se pierde (excepto por evaporación) y forma parte de un
circuito cerrado en el que cada cierto tiempo se ha de reponer el agua perdida
por evaporación y equilibrar los nutrientes disueltos en el agua.
[4]
3
1.2. Sistemas programadores de riego
Se entiende por un sistema programador de riego aquel que nos permite
definir unos horarios para automatizar el proceso de riego. Dicho sistema,
normalmente, está encargado de controlar las válvulas (electroválvulas) que dan
servicio a las diferentes zonas (áreas) en las que se suele dividir una parcela,
plantación, invernadero, etc. Para su estudio lo dividiremos en dos tipos, los
electromecánicos y electrónicos, y los inteligentes, en función del grado de la
tecnología que utilizan en su implementación.
Ilustración 2 Ilustración 1
Estos sistemas presentan las siguientes limitaciones:
- Franja de riego única para todas las zonas. (Todas las zonas regarán a la
misma hora, de forma secuencial. Para aclararlo con un ejemplo, no se
puede configurar una hora de inicio para la zona 1 y otra hora de inicio para
la zona 2; ambas deben de ser la misma hora, aunque la zona 2 no
comenzará hasta haber terminado la zona 1.
- En el caso de riegos por hidroponía es importante realizar riegos de varios
minutos en repeticiones cortas (cada hora o cada media hora), este tipo de
programadores normalmente tienen limitadas la capacidad de poder
gestionar este tipo de riegos, y o bien disponen de unas limitadas franjas
horarias (de tres a cuatro franjas), o bien repeticiones cada cierto tiempo,
siendo el mínimo de una hora. No existe un sistema que permita
compatibilizar ambas opciones y tener una zona con un tipo de programación
y otra con otro tipo de programación.
- Falta de sensorización. Los programadores más avanzados sí cuentan con
cierta sensorización, en la que un sensor de lluvia determinará si se ha de
regar o no, pero no controlan la humedad del aire, de la tierra en las
diferentes zonas o la temperatura.
4
- Falta de domotización. Esta es la diferencia clave con respecto a lo que
describiremos como sistemas de riego inteligentes. Estos sistemas no
proveen de una configuración remota vía web, y en algunos casos no
disponen ni de pantalla para poder configurarlos.
5
una gran comunidad internacional que cuenta con estudiantes, profesores,
aficionados a la tecnología, etc.
Existen miles de proyectos realizados con Arduino, desde objetos de uso
diario (conectar una tostadora a la red y accionarla a través del wifi), hasta
complejos instrumentos para experimentos y mediciones científicas.
Aunque existen otras placas programables con microcontroladores
disponibles en el mercado actualmente; Arduino ofrece varias ventajas:
- Es económico. El precio es bastante más bajos que sus competidoras.
- Multiplataforma. El software de desarrollo se puede ejecutar en cualquier
sistema operativo para ordenadores (Windows, Macintosh OSX o Linux).
- Un entorno de desarrollo simple y limpio. Posee una interfaz de uso sencilla
para los principiantes.
- Código abierto y software ampliable. El software de Arduino está publicado
como herramientas de código abierto, disponible para que los
programadores expertos puedan incrementar las funcionalidades del mismo.
- Código abierto y hardware ampliable. Los planos de las placas de Arduino
se encuentran publicados bajo licencia Creative Commons lo que permite a
los diseñadores de circuito experimentados crear sus propias versiones de
los módulos, ahorrando así bastante dinero.
[5], [6]
6
2. Objetivos del proyecto
El proyecto tratará de dar una solución económica y viable a los sistemas de
automatización de riego existentes. La implementación del dispositivo podrá
realizarse en cualquier ámbito, desde un sistema de riego doméstico hasta una
instalación de regadío para la agricultura.
Los sensores dotarán de la capacidad necesaria al sistema para decidir de
forma autónoma si es necesario realizar un riego extra o por el contrario
determinar si no es necesario realizar el que estaba previsto por configuración.
La configuración que el usuario podrá realizar por cada zona (el prototipo
cuenta con dos zonas) será de los siguientes parámetros:
- Nombre de la zona, para una correcta identificación.
- Cuatro horas de inicio diferentes. Así cubrirá las necesidades de los riegos
de aspersión y por goteo.
- Duración de cada riego.
- Repetición del riego, para los sistemas en los que no sea suficiente con
cuatro horas de inicio se configurará cada cuántos minutos se ha de regar.
Así cubrirá las necesidades de los riegos por hidroponía.
- Límites de humedad relativa del aire y humedad de la tierra. Por encima del
valor máximo no hará falta regar y por debajo del valor mínimo habrá que
aumentar el tiempo de riego al doble para el siguiente riego.
- Límite de temperatura, por encima del máximo se aumentará el tiempo de
riego al doble y por debajo del mínimo no se regará.
- Encendido, donde se podrá activar y desactivar cada zona de forma
independiente.
Además de los parámetros anteriores, el usuario podrá configurar la fecha y
hora del sistema.
La interfaz web proporcionará la configuración telemática del dispositivo
facilitando al usuario no tener que estar presentes físicamente en donde se
encuentre el sistema de riego, asistiendo en el control y gestión del mismo.
Por último, el sistema generará una alarma al superar los umbrales máximos
y mínimos establecidos en la configuración para los diferentes sensores. Dichas
alarmas quedarán registradas en un log (un histórico de eventos), accesible de
forma remota, junto con la fecha y hora a la que se produjo.
Con el fin de cumplir las expectativas y las necesidades del sistema se fijan
como objetivos del proyecto los siguientes:
- El dispositivo ha de poder ser utilizado en la franja de temperaturas de 0º-
50ºC sin distorsión de su funcionamiento.
- El dispositivo debe permitir conocer su configuración mediante la web
(programación y hora actual) y permitir actualizarla desde la misma web que
sirve.
- La latencia de respuesta del dispositivo a una conexión realizada mediante
un navegador web dentro de la red local ha de ser inferior a los 2s.
7
- El acceso desde la web al dispositivo no debe bloquear el funcionamiento
habitual del dispositivo.
- El dispositivo ha de revisar las lecturas de los sensores cada 10 minutos, y
registrar una alerta cuando se superen los umbrales establecidos mediante
la configuración web.
- El dispositivo no ha de perder la configuración actual en caso de corte del
suministro de luz.
- El dispositivo debe de regar en las horas establecidas en la configuración
web, con la duración determinada en función de la configuración web y de si
existe alguna alarma registrada para dicho riego (cancelación o duplicación
de la duración configurada).
- Tras una nueva configuración, el dispositivo ha de reiniciar las alertas
registradas, para que no tengan efectos sobre la nueva configuración.
- El dispositivo debe permitir la consulta del log de alertas registradas, y su
borrado completo desde la página web servida desde el propio dispositivo.
8
3. Descripción del proyecto
El diseño conceptual es el siguiente:
Ilustración 3
9
- Placa de dos relés.
- Tarjeta micro-SD.
- Fuente de alimentación de 5V para alimentar la placa programable.
- Fuente de alimentación de 24V para la apertura de las electroválvulas.
- Dos Electroválvulas para dar servicio a dos zonas de riego.
Esquema de conexiones de componentes:
Ilustración 4
Ilustración 5
10
4. Estudio de viabilidad económica
Se detallan a continuación los costes de fabricación del sistema de riego. Se
trata del coste propio del prototipo, por lo que los costes asociados a una posible
versión comercial del mismo serían o igual o inferiores en todo caso.
Costes de componentes:
Tabla 1
Componente Sin IVA Con IVA Cantidad Total
Arduino Mega 2560 R3 30,25 € 36,60 € 1 36,60 €
Ethernet Wiznet W5100 10,12 € 12,25 € 1 12,25 €
Sensor higrómetro YL-69 1,15 € 1,39 € 2 2,78 €
Sensor aire DHT11 2,30 € 2,78 € 1 2,78 €
Módulo DS3231 2,85 € 3,45 € 1 3,45 €
Placa de relés 2,72 € 3,30 € 1 3,30 €
Tarjeta micro-sd 8,26 € 10,00 € 1 10,00 €
Fuente alimentación 5V 8,26 € 10,00 € 1 10,00 €
Cableado interconexión 1,65 € 2,00 € 1 2,00 €
Total 83,16 €
11
Tabla de coste por dispositivos (estimación):
Tabla 4
Número de dispositivos Horas Total coste Coste unitario
1 2 25,86 € 25,86 €
2 2,25 29,09 € 14,54 €
3 2,5 32,32 € 10,77 €
10 4,25 54,95 € 5,49 €
100 26,75 345,87 € 3,45 €
En base a estos cálculos, el coste del prototipo sigue siendo inferior a los
actuales sistemas de riegos comerciales que aportan similares características.
A todo ello, hay que tener en cuenta que los costes de los materiales puede
verse disminuidos drásticamente si se opta por otras placas de montaje más
baratas con las mismas prestaciones (Elengoo tiene una placa totalmente
compatible por 12€, XCSOURCE tiene otra por 13€), o bien una vez terminado
el código analizar las necesidades de computación y de memoria reales y
cambiar el modelo de la placa de montaje utilizada por una de inferiores
características. (Arduino UNO 20€, Arduino Leonardo 16€, Arduino Micro 16€);
o una combinación de ambas opciones.
Además, como ya comentamos antes, los costes de los materiales al
comprarse de forma individual son más elevados que si se comparan en grandes
cantidades para poder llevar a cabo una producción con fines comerciales.
Los costes asociados a la creación y mantenimiento de una empresa creemos
están exentos de ser estudiados en este apartado, y corresponderían a un
posterior estudio de viabilidad de empresas y no al de viabilidad propia del
proyecto en cuestión.
12
5. Planificación del proyecto
5.1. Primera aproximación
El sistema lo dividiremos en dos partes, una la correspondiente al hardware y
otra al software.
El Hardware, a su vez lo dividiremos en otras dos, una parte lógica y una
funcional:
- Lógica: Contemplará la instalación del hardware requerido y los
componentes adicionales (placa wifi, relés, sensor de humedad y
temperatura, sensor higrómetro y reloj).
- Funcional: Las electroválvulas para el control del riego.
El software se diseñará por módulos. Atendiendo a las diferentes necesidades
del sistema:
- Módulo de inicialización, donde se realizarán los setup iniciales que requiera
el hardware y los sensores instalados. También se realizará la lectura de los
valores configurados previamente los cuales quedarán almacenados en un
fichero en una tarjeta de memoria micro-sd.
- Módulo de servidor web, el cual atenderá a las diferentes peticiones que se
realicen desde los clientes sirviéndole una web para configurar las opciones
del sistema y que mostrará los valores de temperatura y humedad relativa
del aire actuales. Dicho módulo realizará una llamada al módulo de lectura
de la tarjeta de memoria para servir el fichero solicitado.
- Módulo de sensores, inicialmente se recogerán en un único módulo las
lecturas de todos los sensores, aunque se prevé que cada tipo de sensor
tenga una función específica que será llamada por la función principal. Este
módulo llamará al módulo de escritura en caso que sea necesario registrar
una incidencia en las condiciones ambientales.
- Módulo de riego, en el que teniendo en cuenta las lecturas de los sensores
y la configuración actual del sistema se decidirá en función de la hora si se
ha de regar o si se ha de parar de regar.
- Módulo de lectura/escritura de ficheros de la tarjeta de memoria. Se utilizará
tanto para cargar los valores de configuración actuales al arrancare sistema,
como para la escritura de los mismos tras ser modificado por el usuario a
través de la web. También leerá la página web almacenada para poder
devolverla al módulo de servidor web. Seguramente dicho módulo habrá que
dividirlo en dos funciones, una para la lectura y otra para la escritura.
- Módulo de reloj, el cual permitirá obtener la hora actual y modificarla en caso
que lo solicite el usuario a través de la web.
- Módulo principal, en el que en principio se ejecutarán los módulos de servidor
web, sensores y riego de forma continuada con la técnica de polling. Se
intentará implementar un sistema de interrupciones (ISR) atendiendo a las
posibilidades de Arduino de atender a las interrupciones de tipo Timers y de
tipo hardware, aunque únicamente atiende a las interrupciones digitales sí
sería conveniente implementar los timers para que la lectura de sensores no
13
se realice excesivas veces, sino una vez por segundo o incluso cada más
tiempo.
- Página web. El desarrollo de la página web se realizará en HTML y
JavaScript y será almacenada en la tarjeta de memoria.
Ilustración 6
14
Diagrama de Gantt:
Ilustración 7
15
6. Diseño y desarrollo
6.1. Desarrollo Hardware
A continuación se explicará cuáles han sido los componentes utilizados y sus
características, cómo se han llevado a cabo las conexiones, y los problemas
encontrados durante el proceso.
16
Una vez concluya el desarrollo del prototipo, y para una reducción de costes
se debería de contemplar opciones inferiores, para ello deberíamos analizar las
necesidades reales del código generado.
Ilustración 8
Ilustración 9
17
6.1.1.3. Sensor de humedad del aire y temperatura
A la hora de elegir el sensor de humedad y temperatura, existían dos factores
a tener en cuenta, uno de ellos era el económico, y otro el rango de los valores
obtenidos. A continuación comparativa entre los distintos sensores disponibles:
Tabla 7 [10]
Modelo DHT11 DHT22
Precio 2-3€ 6-10€
Potencia de trabajo 3a5V 3a5V
Rango de humedad 20 - 80% con precisión del 5% 0-100% con precisión del 2-5%
Rango de temperatura 0 - 50ºC con precisión ±2ºC -40 – 80ºC con precisión ±0.5ºC
Muestras máximas Una por segundo Una cada dos segundos
Ilustración 10
Ilustración 11
18
6.1.1.5. Módulo de reloj de precisión
En el proyecto se requiere un módulo de reloj para poder almacenar la hora
configurada en el dispositivo para poder realizar los riegos a las horas
programadas. Existen multitud de opciones disponibles en el mercado, siendo el
más común el que hemos utilizado en el proyecto finalmente. Únicamente debía
de cumplir el requisito de que almacenara la hora, y la guardara en caso de
perder la corriente, por ello el factor económico ha sido determinante finalmente
como en otros componentes.
El módulo DS3231 se trata de un reloj en tiempo real de precisión, el cual
cuenta con un oscilador a cristal con compensación de temperaturas. Posee
además una batería auxiliar que le permitirá mantener la hora sin perderla.
Las características del módulo a continuación:
Tabla 9 [12]
Voltaje de alimentación 3.3 – 5V
Exactitud ±2ppm
Rango de trabajo 0ºC – 40ºC
Reloj DS3231
Memoria EEPROM I2C
Ilustración 12
19
6.1.2. Esquema de conexiones
Ilustración 13
Ilustración 14
20
Resumen de las conexiones indicadas en la Ilustración 14:
Tabla 10
Componente PIN Función Arduino
Reloj (DS3231) SCL Sincronización con Arduino SCL
SDA Transmisión de datos SDA
Sensor Aire (DHT11) DATA Envío de datos desde sensor D9
Sensor Tierra 1 (YL-69) AO Envío de datos desde sensor A1
Sensor Tierra 2 (YL-69) AO Envío de datos desde sensor A2
Relés IN1 Envío señal desde Arduino D31
IN2 Envío señal desde Arduino D32
Ethernet/SD SCK Sincronización de transmisión D13
MISO Envío de datos desde shield D12
MOSI Envío de datos desde Arduino D11
SS ETHERNET Slave Select para Ethernet D10
SS SD Slave Select para SD D4
21
6.1.3. Problemas encontrados
Un problema detectado durante las pruebas, ha sido la existencia de una
incompatibilidad entre el pin seleccionado inicialmente para conectar el sensor
DHT11 y la placa Ethernet. El servidor Ethernet testeado durante las pruebas
funcionaba con normalidad, y los sensores también cuando fueron testeados
funcionaban sin problemas. Durante una de las pruebas realizadas se
conectaron todos los sensores en las posiciones inicialmente designadas, pero
sin llevar a cabo ninguna configuración de los mismos, ni tampoco ninguna
interactuación con los mismos, pero en dichas pruebas el servidor Ethernet
dejaba de responder a las peticiones de los clientes. Se procedió a aislar uno a
uno los componentes, hasta determinar que el fallo venía producido por el sensor
DHT11, el cual se encontraba conectado en el D10. En su lugar, se probó a
conectar en el pin D9, dándose por resuelto el problema de bloqueo. Esto se
debía a que la placa Ethernet utiliza los pines 10-13 para comunicarse con la
placa Arduino.
Aunque inicialmente los pines escogidos para los relés eran el D11 y D12,
durante el montaje, y por el problema detectado con la placa Ethernet, se eligió
conectarlos a los pines 31 y 32.
A continuación imágenes del conexionado real:
Ilustración 15
22
6.2. Desarrollo Software
6.2.1. Entorno de desarrollo
Se ha decidido utilizar como entorno de desarrollo Visual Code Studio, ya que
este incluye soporte para Arduino y es una interfaz más cómoda que la propia
IDE de Arduino. Aun así, hemos instalado igualmente la IDE para evitar posibles
problemas de drivers.
El entorno Visual Code Studio tiene la capacidad de implementar plugins para
un desarrollo software más fácil y rápido.
En dicho entorno hemos procedido a instalar las librerías que hemos ido
necesitando para realizar los test y para el desarrollo propio del software.
6.2.2. Fichero de configuración
Se ha optado por un fichero de configuración tipo JSON. La estructura de
dicho fichero se basa en un objeto inicial que contiene un array de objetos, uno
por cada zona de riego.
Aunque inicialmente el fichero de configuración se diseñó con el nombre de
los campos en español, para ahorrar posibles problemas futuros con el mapa de
caracteres se ha optado finalmente por traducirlos al inglés.
Podemos observar que se ha escogido una estructura en la que los programas
están recogidos como un conjunto de objetos también, y en la que cada sensor
tiene asignada una estructura con dos atributos cada uno.
La estructura del fichero es la que sigue:
{ "zones": [
{
"name": "zona 1",
"program": [ {"hour": 8, "minute": 0},
{"hour": 11, "minute": 0},
{"hour": 20, "minute": 20},
{"hour": 22, "minute": 30}],
"duration": 10,
"repetition": {"on": false, "frequency": 0},
"air": {"max": 80, "min": 40},
"land": {"max": 800, "min": 500},
"temp": {"max": 40,"min": 5},
"on": true},
{…}]}
Código 1
23
(https://www.arduino.cc/en/Reference/SPI). La librería SD.h utiliza los
protocolos de comunicación SPI, es por ello que se ha de incluir la librería
SPI.h en dicho módulo.
Cuando se abre un fichero utilizando la librería, se crea un puntero de
memoria que apunta al inicio del fichero. Dicho puntero se va actualizando a
medida que avanzamos en la lectura del fichero. En el caso de la escritura,
el puntero apunta al final del fichero abierto, lo que permite continuar
agregando líneas al final del mismo.
Se realizaron pruebas de lectura y escritura de ficheros en una tarjeta SD.
Todas las pruebas fueron bien. Tal y como se ha indicado, al escribir en un
fichero existente se incrementaba, no se creaba de nuevo, por lo que en
nuestro proyecto tendremos que borrar el fichero de configuración antes de
guardar la nueva configuración. También se ha detectado que la librería no
maneja ficheros con extensiones superiores a cuatro caracteres, por lo que
el nombre de los ficheros de configuración y web (inicialmente config.json e
index.html) pasará a tener extensión .txt y .htm respectivamente.
- Módulo Servidor. Se ha utilizado la librería Ethernet.h versión 1.1.2,
desarrollada por Arduino (https://www.arduino.cc/en/Reference/Ethernet).
Las funciones de comunicación utilizando el componente Ethernet utiliza la
librería Ethernet.h mencionada anteriormente. Además de esta, dado que el
componente utiliza el protocolo de comunicaciones SPI con la placa arduino,
se ha de incluir en dicho módulo el uso de la librería SPI.h.
La librería Ethernet establece un buffer de comunicación entre el cliente
conectado y el servidor (arduino), el cual se utiliza tanto para leer datos
enviados desde el cliente (peticiones) o para enviar datos desde el servidor
(respuestas).
Se realizaron pruebas de crear un servidor web que atendiera a las
peticiones lanzadas desde un navegador. Posteriormente, tras finalizar las
pruebas de todos los módulos se procedió a realizar pruebas de enviar una
web alojada en una tarjeta micro-SD. Para ello se utilizó las librerías del
módulo SD mencionadas anteriormente. Durante las pruebas, se encontró la
incidencia indicada con anterioridad con la conexión del pin D10 del sensor
de DHT11, la cual se solucionó reasignando al pin D9.
- Módulo Sensores. Se ha utilizado la librería DHT.h versión 1.3.0,
desarrollada por Adafruit (https://github.com/adafruit/DHT-sensor-library). Se
realizaron pruebas de lecturas para los diferentes sensores, cambiando las
condiciones en las que se encontraban. Se detectó en concreto que para el
módulo DTH las mediciones en intervalos inferiores a dos segundos dejan
de ser muy precisas, por lo que se aconseja que como mínimo el intervalo
de lectura sea de dos segundos.
- Módulo Relés. Se utilizó la librería SPI.h versión 1.0.0, desarrollada por
Arduino (https://www.arduino.cc/en/Reference/SPI). Se realizaron pruebas
de activar y desactivar ambos relés de forma separada y conjunta. Todas las
pruebas fueron correctas.
Además de las librerías mencionadas anteriormente para los diferentes
módulos también se ha utilizado la librería ArduinoJson.h versión 5.13.1,
desarrollada por Benoit Blanchon (https://github.com/bblanchon/ArduinoJson).
Con dicha librería, unida a las librerías del módulo SD para la lectura y escritura
24
de ficheros, se realizaron pruebas para la carga de datos desde un fichero con
estructura JSON, y el guardado del mismo en un fichero del mismo tipo.
Dicha librería crea un buffer donde se captura la estructura JSON procedente
de un buffer de lectura de un fichero directamente, o de una cadena de
caracteres. La librería permite crear diferente tipos de objetos (arrays u objetos)
que a través de punteros de memoria apuntan al dato en concreto que queremos
acceder, o a los contenidos dentro de este encadenando una sucesión de objetos
y arrrays.
Aunque se valoró la utilización de la librería EasyWebServer, desarrollada por
Kalle Lundberg (https://github.com/llelundberg/EasyWebServer) para el manejo
de las peticiones HTTP al servidor finalmente se decidió a programar una
solución particular dado que la mencionada librería no gestionaba las peticiones
del tipo POST y PUT generadas por los formularios web.
Se ha incluido la librería TaskScheduler versión 2.6.1, desarrollada por Anatoli
Arkhipenko (https://github.com/arkhipenko/TaskScheduler) para crear y
gestionar dos tareas de las tres tareas repetitivas (checkProgram y regAlarm).
25
Por otra parte, dado que los riegos se realizan en espacios como poco de un
minuto, la temporización de la comprobación del programa de riego se ha
estimado conveniente fijarla en espacios de un minuto. Dicha función comprueba
tanto si ha de iniciar el riego como si ha de parar.
Ilustración 19
Dentro del bucle loop, una vez comprobada la necesidad de ejecutar o no las
tareas asignadas nos encontramos con la función que recoge las peticiones de
conexión al servidor. En caso de recibir una petición, se le dará respuesta dentro
de la función listenClient, la cual determinará si se trata de una función POST,
derivándola a la función postRequest donde se realiza una limpieza de la cadena
recibida antes de su tratamiento como datos a almacenar.
Se ha diseñado diferentes respuestas para el resto de peticiones que lleguen
al servidor, las cuales servirán para cubrir todas las necesidades de nuestro
prototipo:
- Web de configuración de la hora y programación de las zonas
(readFileEthernet(WEBPAGE)).
- Lectura de sensores (readJSONRead).
26
- Lectura de datos de configuración de fecha (readJSONDate).
- Lectura de datos de configuración de zonas (readFileEthernet(CONFIG)).
- Leer el fichero de registro de alarmas (readFileEthernet(LOG)).
- Eliminar el fichero de registro de alarmas (removeFile(LOG)).
La gestión de activar y desactivar las zonas directamente desde la web no se
ha gestionado a parte debido a que no entrañaba complejidad, por lo que se
gestionan las llamadas a las diferentes funciones directamente en la propia
función listenClient.
Tras la breve explicación de la estructura del programa, pasaré a comentar
las partes del código más importantes de los diferentes módulos.
void setup()
{
Serial.begin(115200);
27
La función inizialitationScheduler posee el siguiente código:
void inizialitationScheduler ()
{
runner.init();
runner.addTask(taskCheck);
runner.addTask(taskSensors);
taskCheck.enable();
taskSensors.enable();
}
Código 3
Para la definición de las tareas es necesario asignarle cada cuanto tiempo han
de ejecutarse (expresado en milisegundos), cuantas veces se ejecuta (en
nuestro caso por siempre, pero podríamos pasar un valor entero si solo
quisiéramos que se ejecutara un determinado número de veces), y por último la
dirección de la función que se ha de ejecutar. Dichas funciones han de ser del
tipo void y no recibir parámetros tampoco, en nuestro caso no suponía ningún
problema.
A continuación (Código 5) la función loadJson(CONFIG), que carga los datos
almacenados en el fichero de configuración en la estructura de datos actual para
llevar a cabo las tareas de riego pertinente:
void loadJson(String filename)
{
fichero = SD.open(filename);
DynamicJsonBuffer jsonBuffer;
int i = 0;
for (auto &zone : filezones)
{
char *aux = zone["name"];
String aux2(aux);
zones[i].name = aux2;
JsonArray &programs = zone["program"];
int j = 0;
for (auto &program : programs)
{
zones[i].program[j].hour = program["hour"];
zones[i].program[j].minute = program["minute"];
j++;
}
zones[i].duration = zone["duration"];
JsonObject &repetition = zone["repetition"];
zones[i].repetition.on = repetition["on"];
zones[i].repetition.frequency = repetition["frequency"];
JsonObject &air = zone["air"];
zones[i].air.max = air["max"];
28
zones[i].air.min = air["min"];
JsonObject &land = zone["land"];
zones[i].land.max = land["max"];
zones[i].land.min = land["min"];
JsonObject &temp = zone["temp"];
zones[i].temp.max = temp["max"];
zones[i].temp.min = temp["min"];
zones[i].on = zone["on"];
i++;
}
fichero.close();
jsonBuffer.clear();
}
Código 5
29
Para poder acceder al valor del objeto se ha de invocar a la variable seguida
del nombre del atributo que queremos acceder entre corchetes, como si de una
posición de un array se tratara. Si el valor al que estamos accediendo es un valor
que podemos almacenar directamente en la estructura procedemos a ello, pero
en cambio si se trata de un objeto JSON, o de un array de objetos creamos una
nueva variable del tipo JsonObject o JsonArray para poder acceder a todo el
contenido de dicho campo. En nuestro caso esto fue necesario para poder
acceder a las programaciones configuradas, así como a los valores límites de
los diferentes sensores.
Una vez concluida la función procedemos a limpiar el buffer. Este paso en
teoría no es necesario, aunque nos encontramos con problemas de
desbordamiento de memoria durante el desarrollo y gracias a estas limpiezas
logramos solucionarlos. Durante la fase de desarrollo se probó a utilizar buffer
estáticos, persistiendo el problema e incluso agravándose algunas veces. Los
problemas no se presentaban en esta función sino en la de lectura de los datos
desde el servidor para proceder a grabarlos en el fichero, por ello, la función de
recogida de datos desde el servidor presenta ciertas modificaciones que
veremos posteriormente.
char aux[70];
date.toCharArray(aux, 70);
return aux;
}
Código 7
Como se puede apreciar en el código, la función tras recibir los datos en forma
de variable DateTime, accede a cada uno de los parámetros que nos interesa
con un método específico que tiene dicha clase. Monta una cadena con la
estructura JSON y la devuelve para que sea devuelta a la web directamente.
30
6.2.4.3. Módulo SD
Las funciones propias del módulo SD se han incorporado dentro de otras
funciones, como la mencionada anteriormente (Código 5). También en la de
almacenamiento de las alertas en el fichero log, y borrado del mismo desde el
servidor web. Para la lectura de la web, así como respuesta a la solicitud de los
datos de configuración para mostrarlos vía web, o a la visualización del log de
alertas también se ha utilizado código correspondiente a este módulo.
Las funciones son bastantes estándares, por lo que únicamente analizaremos
dos funciones a modo de muestra.
Lectura de un fichero para servirlo vía web:
void readFileEthernet(String filename, EthernetClient *client)
{
fichero = SD.open(filename);
if (fichero)
{
while (fichero.available())
{
client->write(fichero.read());
}
fichero.close();
}
else
{
char *error = "ERROR abriendo fichero ";
Serial.print(error);
Serial.println(filename);
client->write(error);
}
}
Código 8
En este caso (Código 8), a la función se le pasa el nombre del fichero que ha
de mostrar y el puntero al cliente Ethernet al que le ha de servir los datos leídos.
Se procede a la escritura directamente en el la función de envío al cliente del
buffer de lectura del fichero en cuestión. En caso de no encontrarse el fichero, o
no poder acceder a él la función también devuelve un error que será mostrado
por pantalla y a través del puerto serie.
Escritura del fichero log:
void writeLog(String filename, String alarm) //SD: Escritura de fichero de registro
{
fichero = SD.open(filename, FILE_WRITE);
if (fichero) {
fichero.println(alarm);
fichero.close();
} else {
Serial.println("ERROR abriendo " + filename);
}
}
Código 9
31
6.2.4.4. Módulo Servidor
En el desarrollo de este módulo se ha incluido el desarrollo de la página web
a través de la cual se podrá ver y modificar la configuración del programador.
Para el desarrollo de la web se ha utilizado HTML y JavaScript. Se ha respetado
el diseño original, y por lo tanto se ha incluido una parte para modificar las zonas
programables, otra para la hora del reloj; y por último una zona donde aparecen
representas las lecturas de humedad y temperatura ambientales.
Además, se han agregado dos botones para poder visualizar los datos del log,
y proceder a borrarlo, además de otros dos botones para poder activar y
desactivar las zonas de riego manualmente, para poder facilitar así los posibles
trabajos de mantenimiento que se deban de hacer en los riegos y no tener que
modificar la programación para ello. A continuación una captura de la web final:
Ilustración 20
32
Tal y como se observa, tras la carga de la estructura de la web (en blanco) se
procede a lanzar una petición “GET” al servidor, en concreto a la dirección
“/read”, esto provocará que el servidor al recibir dicha petición ejecute la función
getJSONRead(), la cual devuelve una cadena formada tras la lectura de los
sensores (readSensors()):
char *getJSONRead()
{
readSensors();
String r = "{\"temp\":\"";
r += read.temp;
r += "ºC\",\"hum\":\"";
r += read.hum;
r += "%\"}";
char aux[40];
r.toCharArray(aux, 40);
return aux;
}
Código 11
33
Tal y como vemos en la función postRequest (Código 12) se procede a limpiar
la cadena recibida desde el cliente web con los datos que se han de almacenar
tanto en el reloj (DS3231) como en el fichero de configuración de las zonas de
riego. Posteriormente se llama a la función específica para almacenar dicha
información. A continuación el código correspondiente a la configuración de las
zonas:
void requestZonesJson(String data)
{
Serial.println("** REQUEST ZONES **");
DynamicJsonBuffer jsonBuffer;
JsonObject &root = jsonBuffer.parseObject(data);
int i = 0;
String a = "";
switch (data[2])
{
case '1':
i = 0;
a = "1";
break;
case '2':
i = 1;
a = "2";
break;
}
jsonBuffer.clear();
saveJson(CONFIG);
if (i == 1)
software_Reset();
}
Código 13
34
Como se ha mencionado anteriormente, se producían desbordamientos de
memoria al recibir los datos de configuración de la zona. Es por ello que
finalmente se ha optado por una solución más creativa, generándose dos
peticiones del tipo POST que contienen la información de cada una de las zonas.
En ambas peticiones se almacena la información en la estructura de datos del
programa (zones) y se procede a salvarla en el fichero de configuración. Para
hacer efectiva la configuración se procede a resetear el arduino mediante la
función software_Reset() (Código 13). Esto es debido a que entendemos que
tras la nueva configuración para las zonas es conveniente “resetear” las posibles
alertas que quedan almacenadas en el software, dado que ya no corresponden
con la configuración actualmente vigente.
35
void regAlarm()
{
readSensors();
for (int i = 0; i < 2; i++)
{
if (zones[i].on)
{
if (read.temp >= zones[i].temp.max)
{
String alarm = "<br>";
alarm += getStringDate();
alarm += ", TEMPERATURA: ";
alarm += read.temp;
alarm += ", ZONA: ";
alarm += zones[i].name;
alarm += ", Se aumenta el tiempo para el siguiente riego.";
alarm += "</br>";
alarms[i].increment = INCREMENT;
writeLog(LOG, alarm);
}
…
}
…
}
}
Código 15
36
activateZone(R2PIN);
}
if (program.hour == now.hour() && ((zones[i].duration * alarms[i].increment) +
program.minute) == now.minute()) {
Serial.print("** Apagando ");
Serial.print(zones[i].name);
Serial.println(" **");
if (i == 0)
deactivateZone(R1PIN);
if (i == 1)
deactivateZone(R2PIN);
alarms[i].increment = 1;
} } }
else {
if ((now.minute() - zones[i].program[0].minute) % zones[i].repetition.frequency == 0) {
if (alarms[i].cancel) {
alarms[i].cancel = false;
return;
}
Serial.print("** Encenciendo ");
Serial.print(zones[i].name);
Serial.println(" **");
if (i == 0)
activateZone(R1PIN);
if (i == 1)
activateZone(R2PIN);
}
if ((now.minute() - zones[i].program[0].minute - (zones[i].duration * alarms[i].increment)) %
zones[i].repetition.frequency == 0) {
Serial.print("** Apagando ");
Serial.print(zones[i].name);
Serial.println(" **");
if (i == 0)
deactivateZone(R1PIN);
if (i == 1)
deactivateZone(R2PIN);
alarms[i].increment = 1;
} } } } }
Código 16
37
cancelado o aumentado) se produce una nueva alarma será el siguiente riego
programado el que se vea afectado.
struct date
{
int hour;
int minute;
int day;
int month;
int year;
} date;
struct program_t
{
int hour;
int minute;
};
struct sensor_t
{
int max;
int min;
};
struct alarm_t
{
bool cancel = false;
int increment = 1;
};
alarm_t alarms[2];
struct test_t
{
bool on = false;
};
test_t test[2];
Código 17
38
al doble (increment) el valor por defecto es 1, y es dentro de la función regAlarm
donde se modifica el valor por INCREMENT, el cual está definido al inicio de las
líneas del programa como 2.
La estructura date_t, se utiliza para ser el paso intermedio entre la hora
configurada desde la web a la almacenada en el componente reloj del dispositivo.
Existen varias funciones definidas que interactúan con el reloj que se han
utilizado en las pruebas y que también utilizan dicha estructura.
Por último, la estructura test_t, se utiliza para registrar el estado actual de la
zona (encendida o apagada) en caso de que se haya activado con el botón para
la activación/desactivación manual agregado en la página web.
39
(con soporte PWM para los diferentes timers), en ningún caso se logró
obtener valores de lectura válidos, todos daban como resultado NaN (Not a
Number). Finalmente, se optó por incorporar la librería TaskScheduler, el
cual es un programador de tareas y nos permite hacer un pooling de tareas
sin tener que controlar el número de ciclos que pasen con una variable y
resetearla en cada llamada a la función.
40
7. Conclusiones
El proyecto ha presentado bastantes dificultades en el desarrollo de cada uno
de los objetivos marcados, y se ha tenido que replantear el enfoque de alguno
de ellos. Entre otros, la utilización de los programadores de tareas en lugar de
timer e interrupciones. Si bien el resultado obtenido es el mismo, la temporización
de los eventos, no se consigue un óptimo rendimiento del dispositivo de esta
forma, dado que en la realidad se aplica un polling.
El conocimiento adquirido a lo largo de todo el grado de ingeniería informática
lo he podido poner en práctica en este proyecto, sobre todo la asignatura de
Sistemas Empotrados, en la cual se veían muchas de las técnicas empleadas en
el desarrollo de este trabajo.
En conclusión, y teniendo en cuenta la relación de objetivos descrita al inicio
del proyecto, se dan por conseguidos todos ellos; se ha obtenido un prototipo
totalmente funcional, el cual cumple los requerimientos de sensorización,
automatización y accesibilidad a la configuración que se marcaron.
41
8. Bibliografía
[1] https://es.wikipedia.org/wiki/Riego
[2] https://es.wikipedia.org/wiki/Riego_por_aspersi%C3%B3n
[3] https://es.wikipedia.org/wiki/Riego_por_goteo
[4] https://es.wikipedia.org/wiki/Hidropon%C3%ADa
[5] https://www.arduino.cc/en/Guide/Introduction
[6] https://es.wikipedia.org/wiki/Arduino
[7] https://www.arduino.cc/en/Products/Compare
[8] https://aprendiendoarduino.wordpress.com/tag/w5100/
[9] https://www.arduino.cc/en/Reference/SPI
[10] https://learn.adafruit.com/dht/overview
[11] http://www.electronicoscaldas.com/sensores-de-humedad-lluvia-
inundacion/461-sensor-de-humedad-en-suelo-yl-69.html
[12] https://www.geekfactory.mx/tienda/modulos-para-desarrollo/ds3231-
modulo-reloj-en-tiempo-real/
[13] https://aprendiendoarduino.wordpress.com/2014/11/18/tema-6-
comunicaciones-con-arduino-4/
42