El objetivo del proyecto a realizar es automatizar el proceso de llenado de combustible de los vehículos de la empresa y la gestión de las revisiones periódicas de los vehículos.
A continuación se describirá todos los objetivos obligatorios que propone el cliente sobre el proyecto y son de obligado cumplimiento para su implementación con éxito.
Se va a desarrollar todas las ideas para optimizar el uso de la Brandolinera propuestas por mi
Cliente:
SisteinCategoría:
IOT y automatizaciónFecha:
Febrero de 2024Dirección:
Molina de seguraPara la topología del proyecto se ha propuesto un diseño sencillo el cual constará de un 1200 de siemens + un dispositivo IOT el cual repartira la información con el resto de usuarios tanto los operarios como los administradores. Para el HMI se ha decidido intentar usar al propio dispositivo IOT como un servidor web con el que se podrá acceder desde una pantalla conectada por HDMI o conectándose a la red que emitirá el propio dispositivo y conectandose mediante una app que simplemente será un iframe que mostraría la web. Depende del cliente se optará por uno u otro, incluso utilizar ambos. Adjunto un diagrama del funcionamiento
**NOTA: Francisca es la encargada de administración de revisar el diese y Pepe y Juan Diego son los encargados del almacén.
El material propuesto para el proyecto constará de:
Como este proyecto es propuesto y desarrollado por mi para iniciarme en el desarrollo IOT industrial quise hacer algo que no he visto en otros proyectos y es hacer una pantalla desarrollada con el software libre Fuxa el cual tiene un proyecto en Github y la verdad me parece un software muy potente. Espero que se siga desarrollando y que salga al mercado formalmente porque tiene un gran potencial de competir con Aveva e Ignition
Debido a decisiones monetarias el HMI en vez de poner un monitor táctil resultó más económico tanto en desarrollo como en precio de producto y fiabilidad poner una pantalla táctil de la marca Weintek
Para la programación del PLC se decidió que realizase las tareas OT más sencillas y que todo el tratamiento de datos lo ejecutase el dispositivo IOT que al usar JavaScript será más sencillo de programar y depurar ya que JavaScript es un lenguaje interpretado.
Para ello se creo un objeto tecnológico para poder aprovechar las entradas rápidas del 1200 y contar de forma sencilla los pulsos del caudalímetro. Para hacerlo primero hubo que configurar el hardware del PLC:
Configuración contador rápido (Función)
En el siguiente apartado se configura la entrada de PLC que vamos a evaluar
Configuración contador rápido (Configurar entrada PLC)
Aquí podemos encontrar el ID software del contador rápido
Configuración contador rápido (Lectura ID Hardware)
Por último deberemos crear en un DB con un tipo de dato «HSC_Count»
Crear dato estructurado para el contador rápido
Una vez terminada la configuración de hardware hay que agregar el objeto tecnológico al programa donde:
-HSC es el ID hardware que hemos obtenido anteriormente
-CTRL es el DB donde almacenaremos la información del contador rápido
Configuración contador rápido (Configurar objeto tecnológico)
Para la monitorización de la temperatura de la sala se agrego un normalizado y escalado:
Normalizar temperatura y humedad
Para preparar los datos a mostrar en pantalla se agrego un pequeño ajuste con el que mostrará toda la información en el HMI
#dummy_real := DINT_TO_REAL("Datos_Cont".Mi_HSC1.CurrentCount);
"HMI_DB".litros := #dummy_real / #pulsos_litro;
"IOT_DB".price_supply := "IOT_DB".Price * "HMI_DB".litros;
"HMI_DB".temperature := "temperatura";
"HMI_DB".humedad := "humedad";
"HMI_DB".pulsos := "Datos_Cont".Mi_HSC1.CurrentCount;
Para gestionar las diferentes situaciones se preparó una maquina de estados que gestiona cada una de las etapas por la que tiene que pasar en el proceso de surtir:
1º Inicio de sesión
2º Comprobar coche
3º Surtir
4º Inyección en base de datos
CASE "DB_Aux".SQL OF
// ██ ██████ ██ ███████
// ██ ██ ██ ██ ██
// ██ ██ ██ ██ █████
// ██ ██ ██ ██ ██
// ██ ██████ ███████ ███████
//
"Iddle": // Estado de espera
// Inicializa todas las variables a FALSE
"HMI_DB".CurrentWindow := 10;
"Datos_Cont".Mi_HSC1.EnCV := false;
"IOT_DB".Car_Fail := FALSE;
"IOT_DB".Car_Pass := FALSE;
"IOT_DB".LOG_IN_Fail := FALSE;
"IOT_DB".LOG_IN_Pass := FALSE;
"HMI_DB".ResetStateSupply := FALSE;
"IOT_DB".Check_log := FALSE;
"IOT_DB".Start_SQL := FALSE;
"IOT_DB".Stop_SQL := FALSE;
// Si hay un cambio de estado cambia el estado a #supply
IF "HMI_DB".int_Change_state = 1 THEN
"HMI_DB".aux_kilometro := "HMI_DB".kilometer;
"HMI_DB".aux_business := "HMI_DB".business;
"DB_Aux".SQL := "Check_Log";
"IOT_DB".Check_log := TRUE;
"HMI_DB".int_Change_state := 0;
END_IF;
// Si se detecta un aumento en los litros sin haber pasado por log in fuerza las variables de "Surtido no autorizado"
IF "HMI_DB".litros > 1 THEN
"HMI_DB".employee := 9999;
"HMI_DB".pass := 1596;
"HMI_DB".matricula := '00000000';
"HMI_DB".kilometer := 0;
"HMI_DB".business := FALSE;
"DB_Aux".SQL := "Check_Log";
END_IF;
// ███████ ██ ██ ███████ ██████ ██ ██ ██ ██████ ██████
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██ ███████ █████ ██ █████ ██ ██ ██ ██ ███
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██████ ██ ██ ███████ ██████ ██ ██ ███████ ███████ ██████ ██████
//
"Check_Log": // Estado de verificación de log
//Forzamos las variables para que mp se modifiquen en medio del surtido
"HMI_DB".kilometer := "HMI_DB".aux_kilometro;
"HMI_DB".business := "HMI_DB".aux_business;
// Si se resetea el estado de suministro, permanece en #Iddle
IF "HMI_DB".ResetStateSupply OR "HMI_DB".int_Change_state = 1 THEN
"HMI_DB".int_Change_state := 0;
"DB_Aux".SQL := "Iddle";
END_IF;
//En caso de fallar el inicio de sesion mostrar la pantalla 13 en el HMI
IF "IOT_DB".LOG_IN_Fail THEN
"HMI_DB".CurrentWindow := 13;
END_IF;
// Si el inicio de sesión fue exitoso, cambia el estado a #Check_Car
IF "IOT_DB".LOG_IN_Pass THEN
"DB_Aux".SQL := "Check_Car";
END_IF;
// ██████ ██ ██ ███████ ██████ ██ ██ ██████ █████ ██████
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██ ███████ █████ ██ █████ ██ ███████ ██████
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██████ ██ ██ ███████ ██████ ██ ██ ███████ ██████ ██ ██ ██ ██
//
"Check_Car": // Estado de verificación del coche
"HMI_DB".kilometer := "HMI_DB".aux_kilometro;
"HMI_DB".business := "HMI_DB".aux_business;
// Si se resetea el estado de suministro, vuelve a #Iddle
IF "HMI_DB".ResetStateSupply OR "HMI_DB".int_Change_state = 1 THEN
"HMI_DB".int_Change_state := 0;
"DB_Aux".SQL := "Iddle";
END_IF;
// Si el coche pasó la verificación, cambia el estado a #Supply
IF "IOT_DB".Car_Pass THEN
"DB_Aux".SQL := "Supply";
END_IF;
//En caso de de fallo de vehiculo llevar a la pantalla 14
IF "IOT_DB".Car_Fail THEN
"HMI_DB".CurrentWindow := 14;
END_IF;
// ███████ ██ ██ ██████ ██████ ██ ██ ██
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ███████ ██ ██ ██████ ██████ ██ ████
// ██ ██ ██ ██ ██ ██ ██
// ███████ ██████ ██ ██ ███████ ██
//
"Supply": // Estado de suministro
"HMI_DB".CurrentWindow := 11; //Cambiamos a la pantalla de surtido
//Forzamos las variables en pantalla a las que estaban previamente insertadas
"HMI_DB".kilometer := "HMI_DB".aux_kilometro;
"HMI_DB".business := "HMI_DB".aux_business;
// Activa el motor
"Arranque_motor" := TRUE;
// Si los litros actuales son iguales a los anteriores, activa un temporizador de 30 segundos
IF "DB_Aux".litros_before = "HMI_DB".litros AND "HMI_DB".litros > 0 THEN
"IEC_Timer_0_DB".TON(IN := TRUE,
PT := T#30s);
ELSE
"IEC_Timer_0_DB".TON(IN := false,
PT := T#30s);
// Actualiza el valor de litros antes con los litros actuales
"DB_Aux".litros_before := "HMI_DB".litros;
END_IF;
// Si hay un cambio de estado o el temporizador expira con litros mayores a 0, cambia el estado a #Insert
IF "HMI_DB".int_Change_state = 1 OR ("IEC_Timer_0_DB".Q AND "HMI_DB".litros > 0) THEN
"HMI_DB".int_Change_state := 0;
"IEC_Timer_0_DB".TON(IN := FALSE,
PT := T#30s);
"DB_Aux".SQL := "Insert";
END_IF;
// ██ ███ ██ ███████ ███████ ██████ ████████
// ██ ████ ██ ██ ██ ██ ██ ██
// ██ ██ ██ ██ ███████ █████ ██████ ██
// ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██ ██ ████ ███████ ███████ ██ ██ ██
//
"Insert": // Estado de inserción de datos
"HMI_DB".kilometer := "HMI_DB".aux_kilometro;
"HMI_DB".business := "HMI_DB".aux_business;
// Inicia la operación de SQL para insertar datos
"IOT_DB".Start_SQL := TRUE;
// Detiene el motor
"Arranque_motor" := FALSE;
// Suma los litros actuales a los litros totales
"IOT_DB".Litres := "IOT_DB".Litres + "HMI_DB".litros;
// Si se completó la operación de SQL, resetea el contador de alta velocidad y vuelve al estado #Iddle
IF "IOT_DB".Stop_SQL THEN
"Datos_Cont".Mi_HSC1.EnCV := TRUE;
"HMI_DB".employee := 0;
"HMI_DB".pass := 0;
"HMI_DB".matricula := '00000000';
"HMI_DB".kilometer := 0;
"Temporizador_BBDD".TON(IN:=TRUE,
PT:=T#500ms);
IF "Temporizador_BBDD".Q THEN
"DB_Aux".SQL := "Iddle";
"Temporizador_BBDD".TON(IN := FALSE,
PT := T#500ms);
END_IF;
END_IF;
END_CASE;
(En construcción)
(En construcción)
(En construcción)
Para la instalación del IOT se ha usado la configuración de Raspberry OS 64 bits. Una vez instalado se han ejecutado las líneas de comando
Primero actualizamos el sistema operativo a la última versión
sudo apt-get update
sudo apt-get upgrade
Instalamos el NodeRed, reservamos la memoria de 256 MB y habilitamos el servicio para que autoarranque al iniciar el sistema operativo
bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
node-red-pi --max-old-space-size=256
sudo systemctl enable nodered.service
Instalamos los nodos «node-red-contrib-excel», «node-red-contrib-s7», «node-red-dashboard», «node-red-mysql-r2» y «node-red-node-email»
npm install node-red-contrib-excel
npm install node-red-contrib-s7
npm install node-red-dashboard
npm install node-red-mysql-r2
npm install node-red-node-email