Demo SCADA · Sena Soluciones Integrales · Comodoro Rivadavia
| Desde | Alarma | Prioridad | Estado |
|---|
La lógica está escrita en Structured Text según la norma IEC 61131-3 y corre en un PLC OpenPLC o en cualquier equipo con CODESYS. Esta pantalla ejecuta la misma secuencia cada 100 ms, igual que el ciclo de scan del PLC.
El SCADA lee y escribe los registros Modbus del PLC: nivel, estado, palabra de alarmas, horas, arranques y puntos de consigna. Los mismos datos pueden ir a n8n para mandar avisos por WhatsApp o email y armar reportes diarios.
(* ===================================================================== Sena Soluciones Integrales - Demo SCADA Control de nivel de tanque elevado TK-01 con dos bombas alternadas Lenguaje: Structured Text (IEC 61131-3) - compatible OpenPLC / CODESYS --------------------------------------------------------------------- Secuencia (modo AUTO): 1. Nivel <= SP_ARRANQUE -> arranca la bomba lider. 2. Nivel <= SP_REFUERZO durante 10 s -> arranca tambien la bomba de refuerzo (la otra). 3. Nivel >= SP_PARADA -> paran ambas. Al terminar cada ciclo se alterna la bomba lider (desgaste parejo). 4. Si la lider esta en falla, arranca la otra automaticamente. Protecciones (AUTO y MANUAL): - Paro de emergencia, cisterna sin agua (marcha en seco), nivel muy alto (rebalse) y falla de sensor detienen las bombas. - Falla termica o falta de confirmacion de contactor (3 s) bloquea la bomba hasta RESET. Escala LT-01: 4-20 mA en entrada 0-20 mA -> 0..65535 4 mA = 13107 (0 %), 20 mA = 65535 (100 %), < 3,6 mA = falla sensor. ===================================================================== *) PROGRAM ControlTanque VAR (* ---------- Entradas digitales ---------- *) PE_OK AT %IX0.0 : BOOL; (* Paro de emergencia, NC: TRUE = OK *) SEL_AUTO AT %IX0.1 : BOOL; (* Selector: TRUE = AUTO, FALSE = MANUAL *) MAN_P1 AT %IX0.2 : BOOL; (* Marcha manual bomba 1 *) MAN_P2 AT %IX0.3 : BOOL; (* Marcha manual bomba 2 *) TERM_P1 AT %IX0.4 : BOOL; (* Termico bomba 1: TRUE = disparado *) TERM_P2 AT %IX0.5 : BOOL; (* Termico bomba 2: TRUE = disparado *) LSL_CIST AT %IX0.6 : BOOL; (* Flotante cisterna: TRUE = hay agua *) PB_RESET AT %IX0.7 : BOOL; (* Pulsador reset / reconocer alarmas *) CONF_P1 AT %IX1.0 : BOOL; (* Contacto auxiliar contactor K1 *) CONF_P2 AT %IX1.1 : BOOL; (* Contacto auxiliar contactor K2 *) (* ---------- Entrada analogica ---------- *) LT01_RAW AT %IW0 : UINT; (* Transmisor de nivel TK-01 *) (* ---------- Salidas ---------- *) K1_P1 AT %QX0.0 : BOOL; (* Contactor bomba 1 *) K2_P2 AT %QX0.1 : BOOL; (* Contactor bomba 2 *) BALIZA AT %QX0.2 : BOOL; (* Baliza de alarma *) BOCINA AT %QX0.3 : BOOL; (* Bocina (hasta reconocer) *) (* ---------- Registros para el SCADA (Modbus holding regs) ---------- *) NIVEL_X10 AT %MW0 : INT; (* Nivel en % x 10 (ej. 735 = 73,5 %) *) ESTADO AT %MW1 : WORD; (* bit0 P1 marcha, bit1 P2 marcha, bit2 AUTO, bit3 ciclo, bit4 refuerzo, bits8-9 lider *) ALARMAS AT %MW2 : WORD; (* Palabra de alarmas, ver ALM_* *) HORAS_P1 AT %MW3 : INT; (* Horas de marcha bomba 1 *) HORAS_P2 AT %MW4 : INT; (* Horas de marcha bomba 2 *) ARRANQ_P1 AT %MW5 : INT; (* Cantidad de arranques bomba 1 *) ARRANQ_P2 AT %MW6 : INT; (* Cantidad de arranques bomba 2 *) SP_ARRANQUE AT %MW10 : INT := 40; (* % - arranca lider *) SP_REFUERZO AT %MW11 : INT := 25; (* % - arranca refuerzo *) SP_PARADA AT %MW12 : INT := 90; (* % - paran ambas *) SP_ALTO AT %MW13 : INT := 95; (* % - alarma rebalse, corta bombas *) SP_BAJO AT %MW14 : INT := 15; (* % - alarma nivel bajo *) RESET_SCADA AT %MW15 : INT; (* Escribir 1 desde el SCADA = reset *) END_VAR VAR Nivel : REAL; FallaSensor : BOOL; Permisivo : BOOL; Ciclo : BOOL; Refuerzo : BOOL; Lider : INT := 1; LiderEf : INT; Disp1, Disp2 : BOOL; Pedido1, Pedido2 : BOOL; FallaK1, FallaK2 : BOOL; (* Falla de confirmacion, enclavada *) Reset : BOOL; AlmAct : WORD; (* Alarmas activas en este scan *) AlmEncl : WORD; (* Alarmas enclavadas hasta reset *) AlmNoRec : BOOL; (* Hay alarma nueva sin reconocer *) SegP1, SegP2 : INT; (* Segundos acumulados (para horas) *) T_Refuerzo : TON; T_ConfK1 : TON; T_ConfK2 : TON; T_Seg : TON; F_FinCiclo : F_TRIG; R_MarchaP1 : R_TRIG; R_MarchaP2 : R_TRIG; R_AlmNueva : R_TRIG; END_VAR VAR CONSTANT ALM_PARO : WORD := 1; (* bit0 Paro de emergencia *) ALM_CIST : WORD := 2; (* bit1 Cisterna sin agua *) ALM_SENSOR : WORD := 4; (* bit2 Falla transmisor LT-01 *) ALM_ALTO : WORD := 8; (* bit3 Nivel muy alto *) ALM_BAJO : WORD := 16; (* bit4 Nivel bajo *) ALM_P1 : WORD := 32; (* bit5 Falla bomba 1 *) ALM_P2 : WORD := 64; (* bit6 Falla bomba 2 *) END_VAR (* ---------- 1. Escalado y diagnostico del transmisor ---------- *) FallaSensor := LT01_RAW < 11796; (* < 3,6 mA *) IF FallaSensor THEN Nivel := 0.0; ELSE Nivel := (UINT_TO_REAL(LT01_RAW) - 13107.0) * 100.0 / 52428.0; IF Nivel < 0.0 THEN Nivel := 0.0; END_IF; IF Nivel > 100.0 THEN Nivel := 100.0; END_IF; END_IF; NIVEL_X10 := REAL_TO_INT(Nivel * 10.0); Reset := PB_RESET OR (RESET_SCADA = 1); RESET_SCADA := 0; (* ---------- 2. Disponibilidad de bombas ---------- *) T_ConfK1(IN := K1_P1 AND NOT CONF_P1, PT := T#3s); T_ConfK2(IN := K2_P2 AND NOT CONF_P2, PT := T#3s); IF T_ConfK1.Q THEN FallaK1 := TRUE; END_IF; IF T_ConfK2.Q THEN FallaK2 := TRUE; END_IF; IF Reset AND NOT TERM_P1 THEN FallaK1 := FALSE; END_IF; IF Reset AND NOT TERM_P2 THEN FallaK2 := FALSE; END_IF; Disp1 := NOT TERM_P1 AND NOT FallaK1; Disp2 := NOT TERM_P2 AND NOT FallaK2; (* ---------- 3. Permisivos (valen en AUTO y en MANUAL) ---------- *) Permisivo := PE_OK AND LSL_CIST AND NOT FallaSensor AND (Nivel < INT_TO_REAL(SP_ALTO)); (* ---------- 4. Secuencia automatica con histeresis ---------- *) IF Nivel <= INT_TO_REAL(SP_ARRANQUE) THEN Ciclo := TRUE; END_IF; IF Nivel >= INT_TO_REAL(SP_PARADA) OR NOT Permisivo OR NOT SEL_AUTO THEN Ciclo := FALSE; END_IF; T_Refuerzo(IN := Ciclo AND Nivel <= INT_TO_REAL(SP_REFUERZO), PT := T#10s); IF T_Refuerzo.Q THEN Refuerzo := TRUE; END_IF; IF NOT Ciclo THEN Refuerzo := FALSE; END_IF; (* Alternancia: al terminar cada ciclo cambia la bomba lider *) F_FinCiclo(CLK := Ciclo); IF F_FinCiclo.Q THEN Lider := 3 - Lider; END_IF; (* Si la lider no esta disponible, toma la otra *) LiderEf := Lider; IF Lider = 1 AND NOT Disp1 THEN LiderEf := 2; END_IF; IF Lider = 2 AND NOT Disp2 THEN LiderEf := 1; END_IF; IF SEL_AUTO THEN Pedido1 := (Ciclo AND LiderEf = 1) OR (Refuerzo AND LiderEf = 2); Pedido2 := (Ciclo AND LiderEf = 2) OR (Refuerzo AND LiderEf = 1); ELSE Pedido1 := MAN_P1; Pedido2 := MAN_P2; END_IF; (* ---------- 5. Salidas a contactores ---------- *) K1_P1 := Pedido1 AND Disp1 AND Permisivo; K2_P2 := Pedido2 AND Disp2 AND Permisivo; (* ---------- 6. Alarmas: activas, enclavadas y reconocimiento ---------- *) AlmAct := 0; IF NOT PE_OK THEN AlmAct := AlmAct OR ALM_PARO; END_IF; IF NOT LSL_CIST THEN AlmAct := AlmAct OR ALM_CIST; END_IF; IF FallaSensor THEN AlmAct := AlmAct OR ALM_SENSOR; END_IF; IF NOT FallaSensor AND Nivel >= INT_TO_REAL(SP_ALTO) THEN AlmAct := AlmAct OR ALM_ALTO; END_IF; IF NOT FallaSensor AND Nivel <= INT_TO_REAL(SP_BAJO) THEN AlmAct := AlmAct OR ALM_BAJO; END_IF; IF NOT Disp1 THEN AlmAct := AlmAct OR ALM_P1; END_IF; IF NOT Disp2 THEN AlmAct := AlmAct OR ALM_P2; END_IF; R_AlmNueva(CLK := (AlmAct AND NOT AlmEncl) <> 0); IF R_AlmNueva.Q THEN AlmNoRec := TRUE; END_IF; AlmEncl := AlmEncl OR AlmAct; IF Reset THEN AlmEncl := AlmAct; (* quedan solo las que siguen activas *) AlmNoRec := FALSE; END_IF; ALARMAS := AlmEncl; BALIZA := AlmEncl <> 0; BOCINA := AlmNoRec; (* ---------- 7. Contadores de horas y arranques ---------- *) R_MarchaP1(CLK := K1_P1); R_MarchaP2(CLK := K2_P2); IF R_MarchaP1.Q THEN ARRANQ_P1 := ARRANQ_P1 + 1; END_IF; IF R_MarchaP2.Q THEN ARRANQ_P2 := ARRANQ_P2 + 1; END_IF; T_Seg(IN := NOT T_Seg.Q, PT := T#1s); IF T_Seg.Q THEN IF K1_P1 THEN SegP1 := SegP1 + 1; END_IF; IF K2_P2 THEN SegP2 := SegP2 + 1; END_IF; IF SegP1 >= 3600 THEN SegP1 := 0; HORAS_P1 := HORAS_P1 + 1; END_IF; IF SegP2 >= 3600 THEN SegP2 := 0; HORAS_P2 := HORAS_P2 + 1; END_IF; END_IF; (* ---------- 8. Estado para el SCADA ---------- *) ESTADO := 0; IF K1_P1 THEN ESTADO := ESTADO OR 1; END_IF; IF K2_P2 THEN ESTADO := ESTADO OR 2; END_IF; IF SEL_AUTO THEN ESTADO := ESTADO OR 4; END_IF; IF Ciclo THEN ESTADO := ESTADO OR 8; END_IF; IF Refuerzo THEN ESTADO := ESTADO OR 16; END_IF; ESTADO := ESTADO OR INT_TO_WORD(LiderEf * 256); END_PROGRAM CONFIGURATION Config0 RESOURCE Res0 ON PLC TASK TaskMain(INTERVAL := T#100ms, PRIORITY := 0); PROGRAM Inst0 WITH TaskMain : ControlTanque; END_RESOURCE END_CONFIGURATION
| Tag | Dirección | Tipo | Descripción |
|---|