Documentos de Académico
Documentos de Profesional
Documentos de Cultura
#include "board.h"
#include "Inicializacion.h"
#include "Librerias_Proyecto.h"
/* Definición de constantes */
#define tmuestreo0 1000 // Tiempo de muestreo de la temperatura (en ms)
#define tmuestreo1 10 // Tiempo de muestreo del nivel de agua (en ms)
#define encendido 0
#define apagado 1
#define timeairon 300000 // Tiempo en el cual el aireador se mantiene encendido
#define timeairoff 1200000 // Tiempo en el cual el aireador se mantiene apagado
#define timeledon 1000 // Tiempo en el cual el LED amarillo se mantiene encendido (mientras parpadea)
#define timeledoff 1000 // Tiempo en el cual el LED amarillo se mantiene apagado (mientras parpadea)
#define timebuzzeron 1000 // Tiempo en el cual el buzzer se mantiene encendido (mientras chilla intermitentemente)
#define timebuzzeroff 5000 // Tiempo en el cual el buzzer se mantiene apagado (mientras chilla intermitentemente)
#define ciclocorto 20000 // Duración del ciclo corto
#define ciclomedio 30000 // Duración del ciclo medio
#define ciclolargo 40000 // Duración del ciclo largo
#define umbralmax 1800 // Umbral de tensión (en mV) del sensor de nivel de agua a partir del cual el tubo está lleno
#define umbralmin 800 // Umbral de tensión (en mV) del sensor de nivel de agua a partir del cual el tubo está vacío
#define offsetvaciado 60000 // Tiempo extra de vaciado del tubo
#define tmanguera 180000 // Tiempo de espera para saber si la manguera ha sido conectada
#define tsolnutritiva 60000 // Tiempo de espera para que el usuario agregue la solución nutritiva
/* Definimos los pines del módulo relay como constantes de acuerdo al dispositivo que controlan */
#define pinvalvula1 9 // Pin correspondiente a la electroválvula 1 (P0.9)
#define pinvalvula2 7 // Pin correspondiente a la electroválvula 2 (P0.7)
#define pinbomba1 6 // Pin correspondiente a la bomba de agua 1 (P0.6)
#define pinaireador 18 // Pin correspondiente al aireador (P0.18)
#define pinverde 17 // Pin correspondiente al LED verde (P0.17)
#define pinamarillo 15 // Pin correspondiente al LED amarillo (P0.15)
#define pinbomba2 16 // Pin correspondiente a la bomba de agua 2 (P0.16)
#define pinbuzzer 12 // Pin correspondiente al buzzer (P2.12)
void LeerNivAgua(void);
void CicloDeRiego(void);
void DiagramaLlenado(void);
void EstadoInicial(void);
void Aireador(void);
void ParpadeoLED(void);
void Buzzer(void);
void LeerTemperatura(void);
int main(void) {
/* Rutinas de inicialización */
InitHardware1();
InitPinsGPIO();
InitADC();
ConfigUART();
EstadoInicial();
while(1) {
/* Rutinas que implementan los diagramas de estado para atender a todos los componentes del proyecto */
LeerTemperatura();
LeerNivAgua();
DiagramaLlenado();
}
return 0 ;
}
void DiagramaLlenado(void){
ParpadeoLED();
Buzzer();
switch(esttanque){
case 0:{
/* En el estado 0, el tubo está vaciándose (luego de haber finalizado el ciclo), y cuando el sensor de nivel agua baja
del umbralmin, se inicia una demora (programable con la constante offsetvaciado) para permitir que el tubo siga vaciándose un
tiempo más, pasando al estado 1 */
if(volpromagua > umbralmin){
esttanque = 0;
}
else {
esttanque = 1;
tvaciado = offsetvaciado; // Tiempo extra de vaciado del tubo después de haber detectado el umbralmin
Chip_UART_SendBlocking(LPC_UART3, "*b65*", 5); // Enviamos a través de la app el nivel del tubo (65%)
}
}
break;
case 1:{
/* En el estado 1, el tubo sigue vaciándose luego de haberse iniciado el offset de vaciado. Una vez finalizado el
tiempo, leemos el estado del sensor de nivel bajo. Si este sensor marca un 0, pasamos al estado 2 y volvemos a llenar el tubo, y
el ciclo comienza nuevamente. Si marca un 1, llenamos el tanque, pasando al estado 3. */
if(tvaciado == 0){
AlternarPin(0,pinvalvula2); // Cerrar electroválvula 2 (de vaciado de tubos)
AlternarPin(0,pinbomba2); // Desactivar bomba de agua 2
Chip_UART_SendBlocking(LPC_UART3, "*b30*", 5); // Enviamos a través de la app el nivel del tubo (30%)
sbajo = Chip_GPIO_ReadPortBit(LPC_GPIO, 0, 3); // Leemos el estado del sensor de nivel bajo
/* Luego de haber conmutado la electroválvula 2 y la bomba de agua 2, comienza una demora de 300 ms
para que se elimine el ruido producido por dicha conmutación */
tdelay = 300;
while(tdelay > 0){
esttanque = 1;
}
/* Finalizada esa demora, evaluamos el sensor de nivel bajo */
if(sbajo == 0){
esttanque = 2;
AlternarPin(0,pinbomba1); // Activar bomba de agua 1 (de llenado de tubo)
}
else {
esttanque = 3;
salto = 1;
t1 = tmanguera; // Tiempo de espera para saber si la manguera está conectada
AlternarPin(0,pinvalvula1); // Abrir electroválvula 1 (para el llenado del tanque)
/* Habilitamos las interrupciones por flanco descendente del sensor de nivel alto, limpiando
previamente las interrupciones pendientes */
Chip_GPIOINT_ClearIntStatus(LPC_GPIOINT, GPIOINT_PORT0, 1 << 2);
Chip_GPIOINT_SetIntFalling(LPC_GPIOINT, GPIOINT_PORT0, 1 << 2);
/* Enviar el dato del nivel inferior del tanque (11 litros) */
Chip_UART_SendBlocking(LPC_UART3, "*B11*", 5);
}
}
else esttanque = 1;
}
break;
case 2:{
/* En el estado 2, el tubo está llenándose, luego de que se completara el llenado del tanque y se agregara la
solución nutritiva. Cuando el tubo finalmente se llena, pasamos al estado 6 (estado final). */
if(volpromagua > umbralmax){
esttanque = 6;
AlternarPin(0,pinbomba1); // Desactivar bomba de agua 1
AlternarPin(0,pinverde); // Encender LED verde
flag_led = 0; // El LED amarillo deja de parpadear
/* Enviamos a través de la app el nivel del tubo (100%) */
Chip_UART_SendBlocking(LPC_UART3, "*b100*", 6);
}
else {
esttanque = 2;
}
}
break;
case 3:{
/* En este estado, el tanque empieza a llenarse, con la electroválvula 1 activada. Una vez que el tanque
completa su llenado y recibimos la correspondiente interrupción del sensor de nivel alto, saltamos al estado 5, y le indicamos al
usuario que debe agregar la solución nutritiva. Si la manguera no está conectada, saltamos al estado 4 y le avisamos al usuario
que debe conectar la manguera */
if(salto == 0){
salto = 1;
// Deshabilitamos las interrupciones por flanco descendente del sensor de nivel alto
Chip_GPIOINT_SetIntFalling(LPC_GPIOINT, GPIOINT_PORT0, 0 << 2);
esttanque = 5;
AlternarPin(0,pinvalvula1); // Cerrar electroválvula 1 (para el llenado del tanque)
/* Indicar al usuario que debe agregar solución nutritiva al tanque */
Chip_UART_SendBlocking(LPC_UART3, "*MAgregar solucion nutritiva\r\n*", 31);
t2 = tsolnutritiva; // Tiempo de espera de 60 s para que el usuario agregue la solución nutritiva
Chip_UART_SendBlocking(LPC_UART3, "*B30*", 5); // Enviar el dato del nivel superior del tanque (30
litros)
}
else {
if(t1 == 0){
esttanque = 4;
t1 = tmanguera;
/* Avisar al usuario que la manguera no está conectada */
Chip_UART_SendBlocking(LPC_UART3, "*MLa manguera no esta conectada\r\n*", 34);
flag_buzzer = 1; // Empieza a chillar el buzzer
AlternarPin(0,pinvalvula1); // Apagamos momentáneamente la electroválvula 1
}
else esttanque = 3;
}
}
break;
case 4:{
/* Una vez que la manguera fue conectada, el usuario debe mandar un mensaje de aviso a través de la app
* para luego volver al estado 4 */
if(t1 == 0){
esttanque = 4;
t1 = tmanguera;
/* Avisar al usuario que la manguera no está conectada */
Chip_UART_SendBlocking(LPC_UART3, "*MLa manguera no esta conectada\r\n*", 34);
}
else {
/* Si se recibió un "c" como caracter desde la app, encendemos nuevamente la electroválvula 1 y el
buzzer deja de chillar */
if(strcmp(rxbuf,"c") == 0){
memset(rxbuf,0,1); // Reiniciamos la variable rxbuf con un caracter 0
esttanque = 3;
flag_buzzer = 0; // El buzzer deja de chillar
AlternarPin(0,pinvalvula1); // Volvemos a encender la electroválvula 1
}
else esttanque = 4;
}
}
break;
case 5:{
/* Una vez que la solución nutritiva fue agregada, el usuario debe mandar un mensaje de confirmación a
* través de la app para luego pasar al estado 2 */
if(t2 == 0){
esttanque = 5;
t2 = tsolnutritiva;
/* Indicar al usuario que debe agregar solución nutritiva al tanque */
Chip_UART_SendBlocking(LPC_UART3, "*MAgregar solucion nutritiva\r\n*", 31);
}
else {
if(strcmp(rxbuf,"a") == 0){
memset(rxbuf,0,1); // Reiniciamos la variable rxbuf con un caracter 0
esttanque = 2;
AlternarPin(0,pinbomba1); // Activar bomba de agua 1
AlternarPin(0,pinaireador); // Activar aireador
tairon = timeairon; // Tiempo en el cual el aireador se mantiene encendido (5 minutos)
estair = encendido;
}
else esttanque = 5;
}
}
break;
case 6:{
/* Estado final. Se selecciona el ciclo de riego de acuerdo a la temperatura */
CicloDeRiego();
Aireador();
}
break;
}
}
/* Toma de muestras de la temperatura. Se toma 1 muestra por segundo (tiempo de muestreo de 1 s) y se promedian 60 muestras por
minuto */
void LeerTemperatura(void){
if(cant0<60){
if(tiempo0 == 0){
cant0=LeerDato(voltemp,cant0,ADC_CH0);
tiempo0 = tmuestreo0;
}
}
else {
volpromtemp = PromedioMuestras(voltemp,cant0);
cant0=0;
tprom = volpromtemp/10; // Temperatura promedio
EnviarDato(tprom); // Enviamos el dato de temperatura a través de la app
}
}
/* Toma de muestras del nivel de agua del tubo. Se toma 1 muestra cada 10 ms (tiempo de muestreo de 10 ms) y se promedian las
últimas 30 muestras (o sea, se promedia cada 0.3 s) */
void LeerNivAgua(void){
if(cant1<30){
if(tiempo1 == 0){
cant1=LeerDato(volagua,cant1,ADC_CH1);
tiempo1 = tmuestreo1;
}
}
else {
volpromagua = PromedioMuestras(volagua,cant1);
cant1=0;
}
}
void CicloDeRiego(void){
/* Se elige alguno de los ciclos de riego en función de la temperatura. Tenemos 3 ciclos: el ciclo corto (para
temperaturas superiores a 20°C), el ciclo medio (para temperaturas de entre 10 y 20°C) y el ciclo largo (para temperaturas
inferiores a 10°C */
switch(ciclo){
case 1: {
if(tprom < 10){
tciclo = ciclolargo; // Tiempo de riego (invierno)
ciclo = 2;
}
else{
if(tprom > 20){
tciclo = ciclocorto; // Tiempo de riego (verano)
ciclo = 2;
}
else{
tciclo = ciclomedio; // Tiempo de riego (media estación)
ciclo = 2;
}
}
}
break;
case 2:{
if(tciclo == 0){
ciclo = 1;
esttanque = 0;
AlternarPin(0,pinvalvula2); // Vaciar tubo hasta que el nivel de agua llegue a su valor óptimo
AlternarPin(0,pinbomba2); // Activar bomba de agua 2
AlternarPin(0,pinverde); // Apagar LED verde
flag_led = 1; // Comienza a parpadear el LED amarillo
}
else ciclo = 2;
}
break;
}
}
/* Diagrama de estados que controla el aireador, manteniéndolo encendido durante 5 minutos y apagado durante 20 minutos */
void Aireador(void){
switch(estair){
case encendido:{
if(tairon == 0){
estair = apagado;
tairoff = timeairoff; // Tiempo en el cual el aireador se mantiene apagado (20 minutos)
AlternarPin(0,pinaireador); // Apagar aireador
}
else estair = encendido;
}
break;
case apagado:{
if(tairoff == 0){
estair = encendido;
tairon = timeairon; // Tiempo en el cual el aireador se mantiene encendido (5 minutos)
AlternarPin(0,pinaireador); // Encender aireador
}
else estair = apagado;
}
break;
}
}
/* Esta función se utiliza para hacer que el LED amarillo titile. Consta de un diagrama de estados con 2 estados:
* En el estado 1, el LED amarillo permanece apagado, y se enciende cuando comienza el ciclo de llenado, pasando
* al estado 2. En los estados 2 y 3, el LED parpadea periódicamente, manteniéndose encendido en el estado 2 y apagado en el
* estado 3. El LED deja de parpadear cuando se acaba el ciclo de llenado, volviendo al estado 1. */
void ParpadeoLED(void){
switch(estled){
case 1:{
if(flag_led){
estled = 2;
// Tiempo en el cual el LED amarillo se mantiene encendido mientras parpadea (1 segundo)
tledon = timeledon;
AlternarPin(0,pinamarillo); // El LED amarillo comienza a parpadear
}
else estled = 1;
}
break;
case 2:{
if(flag_led){
estled = 2;
if(tledon == 0){
estled = 3;
// Tiempo en el cual el LED amarillo se mantiene apagado mientras parpadea (1 s)
tledoff = timeledoff;
AlternarPin(0,pinamarillo); // El LED amarillo se apaga (mientras parpadea)
}
}
else {
estled = 1;
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 15); // Apagar el LED amarillo
}
}
break;
case 3:{
if(flag_led){
estled = 3;
if(tledoff == 0){
estled = 2;
// Tiempo en el cual el LED amarillo se mantiene encendido mientras parpadea (1 s)
tledon = timeledon;
AlternarPin(0,pinamarillo); // El LED amarillo se enciende (mientras parpadea)
}
}
else {
estled = 1;
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 15); // Apagar el LED amarillo
}
}
break;
}
}
/* Esta función se utiliza para hacer que el buzzer chille intermitentemente. Consta de un diagrama de estados con
* 2 estados: En el estado 1, el buzzer permanece apagado, y comienza a chillar cuando la manguera no está conectada (luego
* del tiempo de espera de 60 s) pasando al estado 2. En los estados 2 y 3, el buzzer chilla de forma intermitente,
* manteniéndose encendido durante 1 segundo (en el estado 2) y apagado durante 5 segundos (en el estado 3). El buzzer deja
* de chillar cuando se conecta la manguera, volviendo al estado 1. */
void Buzzer(void){
switch(estbuzzer){
case 1:{
if(flag_buzzer){
estbuzzer = 2;
tbuzzeron = timebuzzeron; // Tiempo en el cual el buzzer se mantiene encendido mientras chilla (1 s)
AlternarPin(2,pinbuzzer); // El buzzer comienza a chillar
}
else estbuzzer = 1;
}
break;
case 2:{
if(flag_buzzer){
estbuzzer = 2;
if(tbuzzeron == 0){
estbuzzer = 3;
tbuzzeroff = timebuzzeroff; // Tiempo en el cual el buzzer se mantiene apagado (5 s)
AlternarPin(2,pinbuzzer); // El buzzer se apaga por 5 segundos
}
}
else {
estbuzzer = 1;
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 2, 12); // El buzzer deja de chillar
}
}
break;
case 3:{
if(flag_buzzer){
estbuzzer = 3;
if(tbuzzeroff == 0){
estbuzzer = 2;
tbuzzeron = timebuzzeron; // Tiempo en el cual el buzzer se mantiene encendido (1 s)
AlternarPin(2,pinbuzzer); // El buzzer se enciende por un segundo
}
}
else {
estbuzzer = 1;
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 2, 12); // El buzzer deja de chillar
}
}
break;
}
}
/* Esta función nos permite ingresar al diagrama de estados principal según las condiciones iniciales del programa */
void EstadoInicial(void){
uint16_t adc_value1, volaguainit; // Variables locales para tomar muestra inicial del nivel del agua del tubo
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 18);
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 17);
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 15);
Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 16);
/* Primero medimos el nivel de agua del tubo. Si excede el nivel máximo, significa que el tubo está lleno; por lo tanto,
comenzamos a vaciarlo, ingresando al estado 0. Si el nivel de agua está por debajo del umbral máximo, evaluamos el sensor de
nivel bajo del tanque. Si marca un 1, ingresamos al estado 3, comenzando así con el llenado del tanque; mientras que si marca un
0, pasamos al estado 2 para comenzar con el llenado del tubo */
/* Rutina de atención a las interrupciones del pin del sensor de nivel alto */
void EINT3_IRQHandler(void){
nbytes = Chip_UART_Read(LPC_UART3, rxbuf, 1); // Lee datos a través del periférico UART3 (de a un
// Interrupción asociada al tiempo de espera para que el usuario agregue la solución nutritiva
if(t2){
t2--;
}
// Interrupción asociada al tiempo en el cual el LED amarillo se mantiene encendido (mientras parpadea)
if(tledon){
tledon--;
}
// Interrupción asociada al tiempo en el cual el LED amarillo se mantiene apagado (mientras parpadea)
if(tledoff){
tledoff--;
}
#ifndef INICIALIZACION_H_
#define INICIALIZACION_H_
#include "board.h"
#endif /* INICIALIZACION_H_ */
#include "Inicializacion.h"
Board_Init(); // Configura e inicializa todos los bloques requeridos y funciones relacionadas con el hardware de la placa
SysTick_Config(SystemCoreClock/1000); // Configura el temporizador del sistema SysTick para que genere interrupciones cada 1
ms
}
/* Inicializa los pines GPIO y configura las interrupciones de los pines de entrada */
void InitPinsGPIO(void){
ADC_CLOCK_SETUP_T ADCSetup; // Variable tipo estructura que contiene parámetros del ADC (rate, precisión)
/* Configura el tamaño de la palabra de datos, la cantidad de bits de parada y los bits de paridad
* - UART_LCR_WLEN8 - Tamaño de la palabra de datos (8 bits)
* - UART_LCR_SBS_1BIT - Selección del bit de parada (1 bit de parada)
* - UART_LCR_PARITY_EN - Habilitación de paridad (bit de paridad habilitado)
* - UART_LCR_PARITY_EVEN - Selección de paridad (paridad par) */
#ifndef LIBRERIAS_PROYECTO_H_
#define LIBRERIAS_PROYECTO_H_
#include "board.h"
#include "string.h"
#include "stdlib.h"
/* Definición de constantes */
#define alto 1
#define bajo 0
/* Prototipos de funciones */
void AlternarPin(int,int);
float PromedioMuestras(uint16_t [], uint16_t);
void EnviarDato(uint8_t);
uint8_t LeerDato(uint16_t [],uint8_t,ADC_CHANNEL_T);
#endif /* LIBRERIAS_PROYECTO_H_ */
Librerias_Proyecto.c
/*
* Librerias_Proyecto.c
*
* Created on: 16 feb. 2021
* Author: Usuario
*/
#include "Librerias_Proyecto.h"
/* Declaración de variables */
uint32_t suma;
float pr;
uint8_t i;
char temp[3] = "*T";
char tempf[6];
char tchar[3];
/* Cambia el estado lógico del pin (si hay un 1, pone un 0, y si hay un 0 pone un 1) */
void AlternarPin(int port, int pin){
_Bool pinstate;
switch(pinstate){
case alto:{
Chip_GPIO_SetPinOutLow(LPC_GPIO, port, pin); // El pin se pone en 0
pinstate = bajo;
}
break;
case bajo:{
Chip_GPIO_SetPinOutHigh(LPC_GPIO, port, pin); // El pin se pone en 1
pinstate = alto;
}
break;
}
}
/* Envía el dato de temperatura a la app a través de la UART3 (la cual se comunica con el módulo Bluetooth */
void EnviarDato(uint8_t tprom){
/* Armamos el string para enviar a través de la app, con el siguiente formato: *T<Dato de temperatura>* */
strcat(tempf,temp);
itoa(tprom,tchar,10); // Se convierte el dato de temperatura (entero) en un string (cadena de caracteres)
strcat(tempf,tchar);
strncat(tempf,temp,1);
memset(tempf,0,6); /* Se resetea el string tempf para que contenga sólo ceros y luego pueda ser rellenado con un nuevo
valor de temp. */
}
/* Esta función lee los resultados de la conversión más reciente y los guarda en un vector */
uint8_t LeerDato(uint16_t vol[], uint8_t cant, ADC_CHANNEL_T channel){
uint16_t adc_value;