//INGENIERÍA ELECTRÓNICA
//PROYECTO FINAL PROGRAMACIÓN II
//Cobro de peaje y autorización de paso en una carretera a desnivel.
//Simulaciones en Wokwi, la bascula y sensor de distanci seran simuladas.
//Tutorial: Ejemplo control olla de cocción
//https://www.youtube.com/watch?v=QoSvxlLGwJE&list=PL5xKb5iBNpYOBRIcWG71TxFdGM0f6B42d&index=1
//Tutorial: Sensores de distancia
//https://www.youtube.com/watch?v=IF1eN0WK3bU&t=103s
//Tutorial sobre Celdas de Carga
//https://naylampmechatronics.com/blog/25_tutorial-trasmisor-de-celda-de-carga-hx711-balanza-digital.html
//Tutorial sobre Servos
//https://naylampmechatronics.com/blog/33_tutorial-uso-de-servomotores-con-arduino.html
//Tutorial: Como funciona un ServoMotor
//https://www.youtube.com/watch?v=llNoXMyzjYw
//Descripción:
//Para este proyecto se requiere el diseño de un sistema de cobro de peaje en un paso vehicular a desnivel.
//El sistema debe contar con mecanismos para la detección de los vehículos que pasan por el peaje, hacer la medición (longitud)
//y pesaje del vehículo, y permitir o negar el paso del vehículo por el sistema de desnivel.
#include <LiquidCrystal_I2C.h> //Debe descargar la Libreria que controla el I2C
#include <Wire.h> //I2C
#include <Servo.h> //Libreria servomotor
#include "HX711.h" //Libreria celda de peso
LiquidCrystal_I2C lcd(0x27, 16, 2); //0x20 o 0x27
HX711 balanza; //Crea un objeto HX711 llamado Balanza
Servo ServoMotor_1; //Crea un objeto servo llamado servoMotor 1
Servo ServoMotor_2; //Crea un objeto servo llamado servoMotor 2
const int buzer = 41; //Constante define a buzzer
const int DOUT = A1; //Constante define salida celda de carga
const int CLK = A0; //Constante define CLK celda de carga
#define FC_1 23 //Pin 31 se conecta al final de carrera 1
#define FC_2 25 //Pin 32 se conecta al final de carrera 2
#define sensor_1 27 //Pin 22 se conecta el sensor de presencia 1
#define sensor_2 29 //Pin 23 se conecta el sensor de presencia 2
#define sensor_3 31 //Pin 24 se conecta el sensor de presencia 3
#define sensor_4 33 //Pin 25 se conecta el sensor de presencia 4
#define led_1 35 //Pin 37 se conecta led indicador verde
#define led_2 37 //Pin 38 se conecta led indicador amarillo
#define led_3 39 //Pin 39 se conecta led indicador rojo
#define IN_1 43 //Pin 28 se conecta activacion del motor ascensor
#define IN_2 45 //Pin 29 se conecta activacion del motor ascensor
#define EN_1 5 //Pin 5 se conecta activacion del motor ascensor
#define pulsa_2 6 //Pin 6 se conecta el pulsador 2 de NO pago
#define pulsa_1 7 //Pin 7 se conecta el pulsador 1 de pago
#define echo_1 8 //Pin Echo sensor 1
#define trigger_1 9 //Pin Trigger sensor 1
#define echo_2 10 //Pin Echo sensor 2
#define trigger_2 11 //Pin Trigger sensor 2
long distancia_sensores = 900; //Distancia en la cual se encuentran separados los dos sensores
int duracion_1 = 0; //Tiempo de regreso
int duracion_2 = 0; //Tiempo de regreso
int distancia_1 = 0; //Distancia en centímetros
int distancia_2 = 0; //Distancia en centímetros
long distancia_vehiculo; //Variable que posee la distancia simulada del carro
float peso_vehiculo; //Variable que posee el peso del vehiculo
long costo_peaje; //Variable que posee el valor del peaje
int valor_Kilo = 80; //Establecemos el valor del valor de kilo para el peaje
int valor_Cm = 50; //Establecemos el valor de centimetro para el peaje
byte angulo_1 = 0; //Angulo inicial del servomotor 1
byte angulo_2 = 0; //Angulo inicial del servomotor 2
byte etapa = 0; //Etapas del proceso
byte status_motor = 0; //Estados del proceso para activar motor a la izquiera o derecha.
bool status_led1, status_led2, status_led3; //Estados del proceso para activar led 1 o led 2
bool status_S1, status_S2, status_S3, status_S4; //Estados del proceso para las entradas de sensores, logica invertida
bool status_P1, status_P2; //Estados del proceso para las entradas de los pulsadores
bool status_FC1, status_FC2; //Estados del proceso para las entradas de los finales de carrera
int valor = 200; //Valor PWM Motor
unsigned long tiempo_inicio = 0; //Tiempo de inicio para temporizar
unsigned long tiempo = 0; //Variable que registra el tiempo
byte LCD = 0; //Etapas de los mensajes
byte Last_LCD; //Actualizacion de mensajes
String linea_1; //Mensaje de la linea 1 LCD
String linea_2; //Mensaje de la linea 2 LCD
//*************************************************************************
void setup() {
pinMode(IN_1, OUTPUT); //Sentido antihorario
pinMode(IN_2, OUTPUT); //Senrtido horario
pinMode(EN_1, OUTPUT); //Habilitacion motor - Señal PWM
pinMode(led_1, OUTPUT); //Indicador Verde
pinMode(led_2, OUTPUT); //Indicador Amarillo
pinMode(led_3, OUTPUT); //Indicador Rojo
pinMode(trigger_1, OUTPUT); //Disparo Sensor ultrasonico 1
pinMode(trigger_2, OUTPUT); //Disparo Sensor ultrasonico 2
pinMode(echo_1, INPUT); //Echo como pin de entrada
pinMode(echo_2, INPUT); //Echo como pin de entrada
balanza.begin(DOUT, CLK); //Inicializa el HX711
balanza.set_scale(420.f); //Establece el valor de la escala, que es el factor de conversión para convertir valor de lectura en un valor con unidades de peso
lcd.init(); //Inicializa LCD
Wire.begin(); //Inicializa I2C
lcd.backlight(); //Iluminacion LCD
lcd.clear(); //Limpia pantalla
ServoMotor_1.attach(2); //Asociamos el servo a la patilla 2 del Arduino PWM
ServoMotor_1.write(0); //Desplazamos a la posición 0º
ServoMotor_2.attach(3); //Asociamos el servo a la patilla 3 del Arduino PWM
ServoMotor_2.write(0); //Desplazamos a la posición 0º
digitalWrite(trigger_1, LOW); //Trigger en cero
digitalWrite(trigger_2, LOW); //Trigger en cero
}
void loop() {
//**************************** ENTRADAS ************************************
status_S1 = digitalRead(sensor_1); //Sensor 1 - Zona de llegada
status_S2 = digitalRead(sensor_2); //Sensor 2 - Medicion, Pesaje, Pago
status_S3 = digitalRead(sensor_3); //Sensor 3 - 2 piso
status_S4 = digitalRead(sensor_4); //Sensor 4 - 1 piso
status_P1 = digitalRead(pulsa_1); //Pulsador 1 de aceptar pago
status_P2 = digitalRead(pulsa_2); //Pulsador 2 de rechazar pago
status_FC1 = digitalRead(FC_1); //Final de carrera 1 localizada arriba del ascensor
status_FC2 = digitalRead(FC_2); //Final de carrera 2 localizada abajo del ascensor
//**************************** PROCESO *************************************
switch (etapa) {
case 0:
LCD = 0; //Mensaje 0 LCD
status_led1 = 0; //Led verde
status_led2 = 0; //Led Amarillo
status_led3 = 1; //Led Rojo
angulo_1 = 0; //Talanquera 1 cerrada
angulo_2 = 0; //Talanquera 2 cerrada
status_motor = 0; //Motor detenido
if (status_S1 == 1 && status_S2 == 0) { //Pregunta si esta activo sensor de presnecia en zona de llegada
tiempo_inicio = millis();
etapa = 1;
}
break;
case 1:
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
tiempo_inicio = millis();
etapa = 2;
}
break;
case 2:
LCD = 2; //Mensaje 2 LCD
angulo_1 = 90; //Se abre talanquera 1
tiempo = millis() - tiempo_inicio;
if (status_S2 == 1 && status_FC2 == 1) { //Pregunta si esta activo sensor presencia en zona de medicion, pesaje y pago
tiempo_inicio = millis();
etapa = 3;
} else if (tiempo >= 50000) { //Tiempo de ingreso de 1.5 min
etapa = 30;
}
break;
case 3:
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
tiempo_inicio = millis();
etapa = 4;
}
break;
case 4:
LCD = 4; //Mensaje 4 LCD
angulo_1 = 0; //Se cierra talanquera 1
calcular_peso(); //Subrutina que calcula el peso del carro
calcular_distancia_1(); //Subrutina que calcula la distancia del carro
calcular_distancia_2(); //Subrutina que calcula la distancia del carro
distancia_vehiculo = distancia_sensores - (distancia_1 + distancia_2);
calcular_costo(); //Subrutina que calcula el precia a pagar del peaje
tiempo = millis() - tiempo_inicio;
if (status_P1 == 1 && status_S2 == 1 && status_FC2 == 1 && status_FC1 == 0) { //Pregunta si pulso el Pulsador de pago peaje
tiempo_inicio = millis();
etapa = 10;
} else if (status_P2 == 1 && status_S2 == 1 && status_FC2 == 1 && status_FC1 == 0) { //Pregunta si pulso el Pulsador de NO pago peaje
tiempo_inicio = millis();
etapa = 20;
} else if (tiempo >= 50000) { //Tiempo de pago de 1.5 min
etapa = 31;
} else if (peso_vehiculo < 0 || distancia_vehiculo <= 0 || costo_peaje <= 0) {
etapa = 34;
}
break;
case 10:
LCD = 10;
tiempo = millis() - tiempo_inicio;
if (tiempo >= 10) {
etapa = 11;
}
break;
case 11:
status_motor = 1; //Motor en sentido antihorario subiendo
if (status_FC1 == 1 && status_FC2 == 0 && status_S2 == 1) { //Pregunta si se activo el final de carrera de subida
tiempo_inicio = millis();
etapa = 12;
}
break;
case 12:
status_led2 = 1; //Activa led amarillo
status_led3 = 0; //Apaga led Rojo de indica detenerse
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
tiempo_inicio = millis();
etapa = 13;
}
break;
case 13:
LCD = 12; //Mensaje 12 LCD
status_led1 = 1; //Activa led Verde que indica avanzar
status_led2 = 0; //Apaga led Amarillo
status_led3 = 0; //Apaga led Rojo de indica detenerse
status_motor = 0; //Motor detenido
tiempo = millis() - tiempo_inicio;
if (status_FC1 == 1 && status_S2 == 0 && status_S3 == 1) { //Pregunta si el sensor de presencia de zona de pago 2 piso se activo
etapa = 14;
} else if (tiempo >= 50000) { //Tiempo de pago de 1.5 min
etapa = 32;
}
break;
case 14:
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
etapa = 15;
}
break;
case 15:
LCD = 14; //Mensaje 14 LCD
status_motor = 2; //Motor sentido horario bajando
status_led1 = 0;
status_led2 = 1;
if (status_FC1 == 0 && status_FC2 == 1 && status_S2 == 0 && status_S3 == 0) { //Pregunta si el final de carrera abajo se activo
tiempo_inicio = millis();
etapa = 16;
}
break;
case 16:
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
etapa = 0;
}
break;
case 20:
LCD = 20;
tiempo = millis() - tiempo_inicio;
if (tiempo >= 10) {
tiempo_inicio = millis();
etapa = 21;
}
break;
case 21:
angulo_2 = 90; //Talanquera 2 se abre
tiempo = millis() - tiempo_inicio;
if (status_FC1 == 0 && status_FC2 == 1 && status_S2 == 0 && status_S4 == 1) { //Pregunta si se activo sensor de presencia de zona No pago
tiempo_inicio = millis();
etapa = 22;
} else if (tiempo >= 50000) { //Tiempo de pago de 1.5 min
etapa = 33;
}
break;
case 22:
tiempo = millis() - tiempo_inicio;
if (tiempo >= 500) {
etapa = 0;
}
break;
case 30:
LCD = 30;
buzzer();
angulo_1 = 0; //Talanquera 1 cerrada
if (status_FC2 == 1 && status_S1 == 0 && status_S2 == 0) { //Pregunta si no hay ningun carro presente en esa zona
etapa = 0;
}
break;
case 31:
LCD = 31;
buzzer();
if (status_P1 == 1 && status_S2 == 1 && status_FC1 == 0 && status_FC2 == 1) { //Pregunta si pulso el Pulsador de pago peaje
tiempo_inicio = millis();
etapa = 10;
} else if (status_P2 == 1 && status_S2 == 1 && status_FC1 == 0 && status_FC2 == 1) { //Pregunta si pulso el Pulsador de NO pago peaje
tiempo_inicio = millis();
etapa = 20;
}
break;
case 32:
LCD = 32;
buzzer();
if (status_S2 == 0 && status_S3 == 1) { //Pregunta si el sensor de presencia de zona de pago 2 piso se activo
etapa = 14;
}
break;
case 33:
LCD = 33;
buzzer();
if (status_FC2 == 1 && status_S2 == 0 && status_S4 == 1) { //Pregunta si se activo sensor de presencia de zona No pago
etapa = 21;
}
break;
case 34:
LCD = 34; //Bucle de error, reiniciar
buzzer();
break;
}
//******************************* SALIDAS *********************************
digitalWrite(led_1, status_led1); //Led indicador Verde
digitalWrite(led_2, status_led2); //Led indicador Amarillo
digitalWrite(led_3, status_led3); //Led indicador Rojo
ServoMotor_1.write(angulo_1); //Escribe el angulo del servomotor 1
ServoMotor_2.write(angulo_2); //Escribe el angulo del servomotor 2
analogWrite(EN_1, valor); //Enable Modulo L293N
if (status_motor == 0) { //Motor detenido
digitalWrite(IN_1, 0);
digitalWrite(IN_2, 0);
} else if (status_motor == 1) { //Motor sentido Antihorario
digitalWrite(IN_1, 1);
digitalWrite(IN_2, 0);
} else if (status_motor == 2) { //Motor sentido Horario
digitalWrite(IN_1, 0);
digitalWrite(IN_2, 1);
}
if (Last_LCD != LCD) { //Actualiza mensaje LCD
lcd.clear();
Last_LCD = LCD;
}
switch (LCD) {
case 0:
linea_1 = "Esperando";
linea_2 = "vehiculo...";
break;
case 2:
linea_1 = "Vehiculo en";
linea_2 = "zona de llegada";
break;
case 4:
linea_1 = "Costo: ";
linea_1 += "$";
linea_1 += costo_peaje;
linea_1 += " ";
linea_2 = "L:";
linea_2 += distancia_vehiculo;
linea_2 += "Cm";
linea_2 += " ";
linea_2 += "P:";
linea_2 += peso_vehiculo;
linea_2 += "Kg";
linea_2 += " ";
break;
case 10:
linea_1 = "Pasando a zona";
linea_2 = "de paso pago";
break;
case 12:
linea_1 = "Es seguro";
linea_2 = "avanzar";
break;
case 14:
linea_1 = "Ascensor esta";
linea_2 = "bajando...";
break;
case 20:
linea_1 = "Usuario NO";
linea_2 = "realiza pago";
break;
case 30:
linea_1 = "Alarma 1";
linea_2 = "Tiempo Ingreso";
break;
case 31:
linea_1 = "Alarma 2";
linea_2 = "Tiempo Pago";
break;
case 32:
linea_1 = "Alarma 3";
linea_2 = "Tiempo de Paso";
break;
case 33:
linea_1 = "Alarma 4";
linea_2 = "Tiempo de Paso";
break;
case 34:
linea_1 = "Alarma 5";
linea_2 = "Error Reiniciar";
break;
}
lcd.setCursor(0, 0);
lcd.print(linea_1);
lcd.setCursor(0, 1);
lcd.print(linea_2);
}
//***************************** SUBRUTINAS ********************************
void buzzer() {
tone(buzer, 600, 200); //Tonos de frecuencia buzer
}
void calcular_distancia_1() {
digitalWrite(trigger_1, HIGH); //Trigger en alto
delayMicroseconds(10); //Se envía un pulso de 10us
digitalWrite(trigger_1, LOW); //Trigger en bajo
duracion_1 = pulseIn(echo_1, HIGH); //Duracion del pulso
distancia_1 = (duracion_1 / 2) / 29; //velocidad del sonido = 1/29 cm/us
}
void calcular_distancia_2() {
digitalWrite(trigger_2, HIGH); //Trigger en alto
delayMicroseconds(10); //Se envía un pulso de 10us
digitalWrite(trigger_2, LOW); //Trigger en bajo
duracion_2 = pulseIn(echo_2, HIGH); //Duracion del pulso
distancia_2 = (duracion_2 / 2) / 29; //velocidad del sonido = 1/29 cm/us
}
void calcular_costo() {
costo_peaje = ((valor_Kilo * peso_vehiculo) + (valor_Cm * distancia_vehiculo));
}
void calcular_peso() {
peso_vehiculo = balanza.get_units(10); //Devuelve el valor actual restando el peso de tara. Equivalente a (read_average() - OFFSET).
}