sistema de riego automatizado
DESCRIPTION
Manual para crear Sistema de Riego AutomatizadoTRANSCRIPT
SISTEMA DE RIEGO AUTOMATIZADO PARA IMPLEMENTACIÓN EN
LOS HUERTOS URBANOS INTELIGENTES
ELEMENTOS UTILIZADOS:
Arduino MEGA 2560
Electroválvula
Sensor Higrómetro
Sensor de Temperatura LM35
Sensor Electrodo PH
Módulo de 4 Relés
Microcontrolador PIC18f4550
Tarjeta Master-Prog
Módulo Convertidor Serial a TTL
Fuente de Voltaje
Tarjeta USBP1x
SOFTWARE UTILIZADO:
Microsoft Visual Studio – C#
Compilador PIC C
Programador Arduino
Virtual Serial Port Driver
En primer lugar, se creó una interfaz en C# para la comunicación entre la tableta del controlador y
una computadora laptop.
Esta pantalla muestra en tiempo real:
los valores de los 6 sensores de humedad (higrómetros)
los valores de los 6 sensores de temperatura LM35
el dato actual recibido por parte del PIC
el valor enviado al controlador para determinar si alguna electro válvula abre o cierra
el valor actual del timer del controlador (junto con la opción de poderlo modificar para
calibrarlo en caso de algun desfazamiento)
una gráfica donde se muestran las últimas variaciones en los valores de humedad y
temperatura
Además, cada valor recibido de los sensores de humedad y temperatura es “depositado” en un
cuadro de texto lo que permite su identificación, y junto a ellos, la posibilidad de ajustar los límites
tanto superior como inferior (superior refiriendose a un exceso de humedad o temperatura, e
inversamente, inferior refiriendose a un valor de humedad bajo y una temperatura baja) con lo cual
se pretende determinar si es necesario abrir o cerrar la válvula de alguna cama de cultivo.
A continuación se presenta el código en C# de este programa:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;
using System.IO.Ports; using System.Threading; using System.Text.RegularExpressions; namespace Aspersor { public partial class Form1 : Form { char cara; double conv = 0; string Recibirdato; string BufferEnvio="0"; string BufferIn; string sensor; char index; char index2; public Form1() { InitializeComponent(); if (!serialPort1.IsOpen) { try { serialPort1.Open(); } catch (System.Exception ex) { MessageBox.Show(ex.ToString()); } serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(Recibir); } } private void Recibir(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { Recibirdato = ""; Thread.Sleep(250); Recibirdato += serialPort1.ReadExisting(); if (serialPort1.BytesToRead > 0) Recibirdato += serialPort1.ReadExisting(); string linea; linea = Recibirdato; this.Invoke(new EventHandler(actualizar)); } private void actualizar(object sender, EventArgs e) { //int IniAN0 = Recibirdato.IndexOf("W"); //int FinAN0 = Recibirdato.IndexOf("X"); int Tamanio = Recibirdato.Length; cara = Recibirdato[Recibirdato.Length - 1]; try { index = Recibirdato[0]; index2 = Recibirdato[1]; } catch (Exception ex)
{ index = 'C'; index2 = 'C'; } label8.Text = index2.ToString()+Recibirdato.Length.ToString() + Recibirdato; if (Recibirdato.Length > 8) { Recibirdato = ""; } Recibirdato = Regex.Replace(Recibirdato,"[^0-9]+",string.Empty); // /*if (Recibirdato.Length >= 1) { int temp = Convert.ToInt32(Recibirdato); switch (temp) { case 0: BufferEnvio = "0"; break; case 1: BufferEnvio = "1"; break; case 2: BufferEnvio = "2"; break; case 3: BufferEnvio = "3"; break; case 4: BufferEnvio = "4"; break; case 5: BufferEnvio = "5"; break; case 6: BufferEnvio = "6"; break; case 7: BufferEnvio = "7"; break; case 8: BufferEnvio = "8"; break; case 9: BufferEnvio = "9"; break; default: break; } sensor = "Sensor 1"; temp = 0; }*/ switch (index) { case 'A': try
{ conv = (Convert.ToDouble(Recibirdato) * 5.7) / 10.24; //conv = Convert.ToDouble(Recibirdato); tbxSensorT3.Text = conv.ToString(); if (Convert.ToInt16(conv) < numTmin3.Value) { tbxSensorT3.BackColor = Color.LightBlue; BufferEnvio = "0"; } else if (Convert.ToInt16(conv) > numTmax3.Value) { tbxSensorT3.BackColor = Color.OrangeRed; BufferEnvio = "1"; } else { tbxSensorT3.BackColor = Color.YellowGreen; BufferEnvio = "0"; } } catch(Exception ex) { } break; default: break; case 'T': switch (index2) { case 'X': lblHora.Text = Recibirdato; break; case 'C': tbxSensorT1.Text = Recibirdato; if (Convert.ToInt16(Recibirdato) < numTmin1.Value) { tbxSensorT1.BackColor = Color.LightBlue; } else if (Convert.ToInt16(Recibirdato) > numTmax1.Value) { tbxSensorT1.BackColor = Color.OrangeRed; } else { tbxSensorT1.BackColor = Color.YellowGreen; } break; case 'A': conv = (Convert.ToDouble(Recibirdato) * 5.7) / 10.24; //conv = Convert.ToDouble(Recibirdato); tbxSensorT3.Text = conv.ToString(); if (Convert.ToInt16(conv) < numTmin3.Value) { tbxSensorT3.BackColor = Color.LightBlue; BufferEnvio = "0"; } else if (Convert.ToInt16(conv) > numTmax3.Value)
{ tbxSensorT3.BackColor = Color.OrangeRed; BufferEnvio = "1"; } else { tbxSensorT3.BackColor = Color.YellowGreen; BufferEnvio = "0"; } break; default: break; } break; /*case 'H': switch (index2) { case 'C': tbxSensorH1.Text = Recibirdato; /*if (Convert.ToInt16(Recibirdato) < numTmin1.Value) { tbxSensorT1.BackColor = Color.LightBlue; } else if (Convert.ToInt16(Recibirdato) > numTmax1.Value) { tbxSensorT1.BackColor = Color.OrangeRed; } else { tbxSensorT1.BackColor = Color.YellowGreen; } break; case 'A': conv = (Convert.ToDouble(Recibirdato) * .57) / 10.24; //conv = Convert.ToDouble(Recibirdato); tbxSensorH3.Text = conv.ToString(); if (Convert.ToInt16(conv) < numHmin3.Value) { tbxSensorH3.BackColor = Color.LightBlue; BufferEnvio = "2"; } else if (Convert.ToInt16(conv) > numHmax3.Value) { tbxSensorH3.BackColor = Color.OrangeRed; BufferEnvio = "3"; } else { tbxSensorH3.BackColor = Color.YellowGreen; BufferEnvio = "2"; } break; default: break; } break; default: break;*/
} Monitor.Text = ""; Monitor.Text += Recibirdato+"\r\n"+BufferEnvio+" --> "+index2; chart1.Series["Temperatura"].Points.Clear(); chart1.Series["Temperatura"].Points.AddXY(""+sensor+"", ""+Recibirdato+""); chart1.Update(); //serialPort1.DiscardOutBuffer(); //serialPort1.Write(BufferEnvio); Recibirdato = ""; } private void btnSalir_Click(object sender, EventArgs e) { Close(); } private void btnSendTime_Click(object sender, EventArgs e) { string hora, minu, segu; if(numHora.Value.ToString().Length<2) { hora="0"+numHora.Value.ToString(); } else { hora = numHora.Value.ToString(); } if (numMinu.Value.ToString().Length < 2) { minu = "0" + numMinu.Value.ToString(); } else { minu = numMinu.Value.ToString(); } if (numSegu.Value.ToString().Length < 2) { segu = "0" + numSegu.Value.ToString(); } else { segu = numSegu.Value.ToString(); } lblHoraEnvio.Text = hora+":"+minu+":"+segu; BufferEnvio = "XX"+hora+minu+segu; serialPort1.DiscardOutBuffer(); serialPort1.Write(BufferEnvio); } } }
Se utilizó el software Virtual Serial Port Driver para emular los puertos COM al momento de las simulaciones con Proteus para la correcta comunicación y recepción de variables.
Después, a cada sensor de temperatura y de humedad, se les soldaron alrededor de 7 metros de cable UTP (para cubrir la distancia del centro de la cama de cultivo a la baquelita donde estaba instalado el controlador) y se aislaron contra el agua algunas partes expuestas. Además, se tuvo que instalar una baquela con capacitores para reducir las interferencias causadas principalmente por las distancias tan largas de los cables. Se utilizó una tableta Arduino Mega 2560 para realizar las primeras demostraciones del sistema de
riego, debido a su sencilla y rápida configuración tanto de puertos de salidas y entradas como de
comunicación serial. A esta tableta se le conectó el módulo de 4 relés para controlar las
electroválvulas instaladas en las camas de cultivo y la baquela de capacitores de los sensores.
Durante las primeras pruebas, se emplearon únicamente un sensor de humedad y uno de
temperatura. El sensor de humedad determinaba, a partir de cierto valor de voltaje, si la tierra bajo
riego había alcanzado un valor de humedad aceptable (lo que cerraba el paso de agua), y en caso
contrario, si los valores de humedad eran muy bajos (lo cual permitía el paso de agua para el riego).
Se sercioró que el valor de temperatura estuviese registrando los cambios correspondientes a lo
largo del riego.
Después, mediante el uso de temporizadoes, se programó el controlador Arduino para que a lo largo
del día se hicieran dos sesiones de riego en las camas de cultivo, una a las 7 de la mañana, y otra a
las 9 de la noche. Debido a las variaciones en la presión hidráulica, se consideró evitar que más de
una electroválvula se abriesen al mismo tiempo, para lo cual se les dio a cada una un tiempo de 5
minutos para abrirse, y cerrarse al término de este tiempo o al momento en que el sensor
higrométrico lo determinase, esto con la finalidad de asegurar que cada cultivo recibiera la
suficiente cantidad de agua.
Además, para prevenir casos de falla en el suministro eléctrico de CFE, se consideró que el valor de
las variables del temporizador se almacenasen en la memoria EEPROM del controlador. Si bien, esto
no previene los desfazamientos, al menos los podría reducir a valores más aceptables (variaciones
en minutos en lugar de horas).
A continuación se presenta el código escrito para el Arduino MEGA 2560:
#include <EEPROM.h> // avr-libc library includes #include <avr/io.h> #include <avr/interrupt.h> #define VALVPIN1 2 #define VALVPIN2 3 #define VALVPIN3 4 #define VALVPIN4 5 int sf=0; int ss=1; int sm=2; int sh=3;
int flag; int seconds=0; int minutes=0; int hours=7; String envio; void setup() { pinMode(VALVPIN1, OUTPUT); pinMode(VALVPIN2, OUTPUT); pinMode(VALVPIN3, OUTPUT); pinMode(VALVPIN4, OUTPUT); digitalWrite(VALVPIN1,HIGH); digitalWrite(VALVPIN2,HIGH); digitalWrite(VALVPIN3,HIGH); digitalWrite(VALVPIN4,HIGH); flag = EEPROM.read(sf); if(flag==0) { seconds = EEPROM.read(ss); minutes = EEPROM.read(sm); hours = EEPROM.read(sh); } // initialize Timer1 cli(); // disable global interrupts TCCR1A = 0; // set entire TCCR1A register to 0 TCCR1B = 0; // same for TCCR1B // set compare match register to desired timer count: OCR1A = 15624; // turn on CTC mode: TCCR1B |= (1 << WGM12); // Set CS10 and CS12 bits for 1024 prescaler: TCCR1B |= (1 << CS10); TCCR1B |= (1 << CS12); // enable timer compare interrupt: TIMSK1 |= (1 << OCIE1A); // enable global interrupts: sei(); Serial.begin(9600); } void loop()
{ Serial.println(minutes); //RIEGO DE LA MAÑANA if((minutes>=0 && minutes<5) && hours==7) { digitalWrite(VALVPIN1,LOW); } if((minutes>=5 && minutes<10) && hours==7) { digitalWrite(VALVPIN2,LOW); digitalWrite(VALVPIN1,HIGH); } if((minutes>=10 && minutes<15) && hours==7) { digitalWrite(VALVPIN3,LOW); digitalWrite(VALVPIN2,HIGH); } if((minutes>=15 && minutes<20) && hours==7) { digitalWrite(VALVPIN4,LOW); digitalWrite(VALVPIN3,HIGH); } if(minutes>=20 && hours==7) { digitalWrite(VALVPIN4,HIGH); } //RIEGO DE LA NOCHE if((minutes>=0 && minutes<5) && hours==21) { digitalWrite(VALVPIN1,LOW); } if((minutes>=5 && minutes<10) && hours==21) { digitalWrite(VALVPIN2,LOW); digitalWrite(VALVPIN1,HIGH); } if((minutes>=10 && minutes<15) && hours==21) { digitalWrite(VALVPIN3,LOW);
digitalWrite(VALVPIN2,HIGH); } if((minutes>=15 && minutes<20) && hours==21) { digitalWrite(VALVPIN4,LOW); digitalWrite(VALVPIN3,HIGH); } if(minutes>=20 && hours==21) { digitalWrite(VALVPIN4,HIGH); } } ISR(TIMER1_COMPA_vect) { seconds++; if (seconds >= 60) { seconds = 0; minutes++; } if(minutes >= 60) { minutes=0; hours++; } if(hours >= 24) { hours=0; } EEPROM.write(ss, seconds); EEPROM.write(sm, minutes); EEPROM.write(sh, hours); EEPROM.write(sf, 0); //digitalWrite(LEDPIN, !digitalRead(LEDPIN));*/ } En la etapa final del proyecto, se sustituyó el controlador Arduino por el microcontrolador
PIC18f4550. Las funciones principales que realizaba el anterior controlador se mantuvieron y se
agregaron las funciones de comunicación RS232, aunque el módulo Serial a TTL no quedó instalado
al final debido a que no era necesario para las funciones que realiza actualmente el controlador,
esto es, la comunicación con una tableta Raspberry la cual será proporcionada después por la
carrera de Ingeniería en Sistemas.
Se empleó también una tarjeta PICKIT para la instalación del controlador debido a que esta ya
contaba con los pines necesarios para la conexión con el módulo de 4 relés, el convertidor Serial a
TTL y la baquela con transistores.
A continuación se presenta el código escrito para controlador PIC 18f4550:
#include <18f4550.h> #device adc=8 #use delay(clock=48M) #fuses HS #fuses HSPLL #fuses PLL5 #fuses CPUDIV1 #fuses PUT #fuses NOWRT #fuses NOWDT #fuses NOBROWNOUT #fuses NOPROTECT #fuses NODEBUG #include <LCD_D.c> #use rs232(baud=9600, parity=N, xmit=pin_c6, rcv=pin_c7, bits=8) #define vv1 PIN_B3 //////VALVULA/RELAY 1 #define vv2 PIN_B4 //////VALVULA/RELAY 2 #define vv3 PIN_B5 //////VALVULA/RELAY 3 #define vv4 PIN_B6 //////VALVULA/RELAY 4 int8 hum1=0,hum2=0,hum3=0,hum4=0; int seg=0,min=0,hor=18; char ind; int8 cont=0; int8 e1=0,e2=0,e3=0,e4=0,e5=0,e6=0,e7=0,e8=0;
#int_TIMER1 //Interrupción Timer1 void temp1s(void) //Función { /////////TIEMPO//////////// if(cont>=25) { seg++; cont=0; /*printf("TX%u:%u:%u",hor,min,seg); ind='A'; /*set_adc_channel(0); hum1=read_adc(); printf("H%c%u",ind,hum1); /*ind='B'; set_adc_channel(1); hum2=read_adc(); printf("%c%u",ind,hum2); ind='C'; set_adc_channel(2); hum3=read_adc(); printf("%c%u",ind,hum3); ind='D'; set_adc_channel(3); hum4=read_adc(); printf("%c%u",ind,hum4);*/ } if(seg>=60) { min++; seg=0; } if(min>=60) { hor++; min=0; } if(hor>=24) { hor=0; } ////////RUTINAS DIARIAS////////// if((hor==7 && (min>=0 && min<5))||(hor==21 && (min>=0 && min<5))) { output_LOW(vv1); } if((hor==7 && (min>=5 && min<10))||(hor==21 && (min>=5 && min<10))) { output_HIGH(vv1);
output_LOW(vv2); } if((hor==7 && (min>=10 && min<15))||(hor==21 && (min>=10 && min<15))) { output_HIGH(vv2); output_LOW(vv3); } if((hor==7 && (min>=15 && min<20))||(hor==21 && (min>=15 && min<20))) { output_HIGH(vv3); output_LOW(vv4); } if((hor==7 && min>20)||(hor==21 && min>20)) { output_HIGH(vv4); } set_timer1 (5536); //recarga del TMR1 cont++; } void main() { port_b_pullups(TRUE); //setup_adc_ports(AN0); setup_adc(ADC_CLOCK_INTERNAL); enable_interrupts(INT_RDA); setup_timer_1 (T1_INTERNAL |T1_DIV_BY_8); set_timer1(5536); enable_interrupts(INT_TIMER1); enable_interrupts(global); //habilita interrupción general while(1) { /*set_timer1(15500); enable_interrupts(INT_TIMER1);*/ ind='A'; hum1=194; } } Finalmente, es necesario mencionar los objetivos que aún quedan pendientes por completar:
Crear una baquelita capaz de albergar al menos 3 microcontroladores con la finalidad de que en un futuro se necesiten ampliar el número de sensores para obtener valores más precisos sobre la eficiencia del riego. Un solo microcontrolador PIC18f4550 consta de 13 entradas ADC, lo que en consecuencia limita a 13 el número de sensores disponibles. Si se considera que cada una de las seis camas utiliza 2 sensores de húmedad y temperatura,
límita a solo poder instalar un único sensor de PH. Se sugiere un protocolo de esclavo-maestro para los microcontroladores.
Probar, programar e instalar el sensor de PH en cada una de las camas.
Se sugiere rediseñar la estructura de las camas de cultivos actuales debido a que no cuentan con un espacio para albergar lo cables (los cuales estan expuestos a la intemperie). También se puede considerar que el nuevo diseño sea cóncavo en la base para que, mediante escurrimiento del agua, se concentre en un solo punto el valor de PH a medir.
Es necesario agregar en la brevedad posible la comunicación WiFi debido, en primer lugar,
a que el microcontrolador por sí solo no puede evitar el desfasamiento en los
temporizadores programados. Cuando la comunicación se encuentre disponible, el valor del
temporizador podría a llegar a actualizarse constantemente para evitar este problema.
Para la fertilización de los cultivos, aún falta la adaptación de los depósitos del fertilizante.
Ya se cuenta con la tubería de entrada, como se puede apreciar en la siguiente imagen.
En este punto se
sugiere instalar el
sensor de PH para
una medición más
precisa de la variable.