Fundamentos de la programación estructurada en PLC
La programación estructurada es una metodología que organiza el código en módulos independientes, jerárquicos y reutilizables. En el ámbito de los autómatas programables (PLC), esta práctica es esencial para gestionar la creciente complejidad de los sistemas industriales, facilitando el desarrollo, mantenimiento, depuración y ampliación de los proyectos. La norma IEC 61131-3 proporciona los elementos necesarios para implementar esta estructura mediante bloques funcionales (FB), funciones (FC), subrutinas (SR) y una clara jerarquía de organización.
La idea central es descomponer un problema complejo en partes más pequeñas y manejables. Cada parte (módulo) tiene una responsabilidad bien definida y una interfaz clara. Esto no solo mejora la legibilidad, sino que también permite que diferentes programadores trabajen simultáneamente y que los módulos probados puedan reutilizarse en futuros proyectos.
Bloques funcionales (FB) y funciones (FC)
La unidad básica de modularidad en IEC 61131-3 son los bloques funcionales:
- FB (Function Block): Tiene memoria (instancia). Posee variables internas que conservan su valor entre llamadas. Ideal para modelar dispositivos físicos (motores, válvulas, temporizadores) o procesos con estado (contadores, máquinas de estado). Cada vez que se crea una instancia de un FB, se reserva un área de datos independiente.
- FC (Function): Sin memoria. El resultado depende solo de las entradas actuales. Se utiliza para operaciones matemáticas, conversiones, lógica combinacional o cualquier tarea que no requiera estado. No puede tener variables internas estáticas (aunque puede usar temporales).
Ejemplo práctico: Un FB para controlar un motor trifásico (con entradas: Marcha, Paro, Reset; salidas: Motor_ON, Error; y variables internas como estado actual y temporizadores). Una FC para calcular la velocidad a partir de un encoder (entrada: pulsos; salida: rpm).
Implementación en ST (texto estructurado):
// Definición de FB Motor (simplificado)
FUNCTION_BLOCK FB_Motor
VAR_INPUT
Marcha : BOOL;
Paro : BOOL;
END_VAR
VAR_OUTPUT
Motor_ON : BOOL;
END_VAR
VAR
estado : INT := 0; // 0=parado, 1=en marcha
END_VAR
// Lógica
IF Marcha AND NOT Paro THEN
estado := 1;
ELSIF Paro THEN
estado := 0;
END_IF;
Motor_ON := (estado = 1);
END_FUNCTION_BLOCK
// Llamada en programa principal
VAR
Motor1 : FB_Motor;
Motor2 : FB_Motor;
END_VAR
Motor1(Marcha:=Start1, Paro:=Stop1, Motor_ON=>Lamp1);
Motor2(Marcha:=Start2, Paro:=Stop2, Motor_ON=>Lamp2);
Subrutinas (SR) y su papel en la modularidad
Las subrutinas son segmentos de código que pueden ser invocados desde múltiples puntos del programa. Aunque en IEC 61131-3 las funciones (FC) ya cubren este propósito, algunos entornos (como Siemens) permiten subrutinas específicas (SBR) dentro de los bloques de organización. Su principal ventaja es evitar la duplicación de código: si una misma rutina (por ejemplo, un cálculo de diagnóstico o una secuencia de arranque) se necesita en varios lugares, se escribe una vez y se llama donde sea necesario.
Las subrutinas pueden ser locales (dentro de un bloque) o globales (accesibles desde todo el programa). Es importante documentar su propósito y los parámetros que esperan.
Jerarquía del programa: Main (OB1) y niveles de abstracción
La organización jerárquica típica de un proyecto PLC sigue una estructura de árbol:
- Nivel superior (OB1 / Main): Ciclo principal. Se encarga de la secuencia general, lectura de entradas, llamada a bloques de nivel medio y escritura de salidas.
- Nivel medio (FB/FC): Implementan la lógica de subsistemas (estaciones de trabajo, ejes de movimiento, comunicaciones).
- Nivel bajo (FB/FC auxiliares): Realizan tareas muy específicas (cálculo de PID, escalado, diagnóstico).
Esta jerarquía permite trabajar con distintos niveles de abstracción: desde una visión global hasta el detalle de un componente. Además, facilita el diagnóstico de fallos (se puede aislar el bloque problemático) y la ampliación (añadir nuevas estaciones sin afectar a las existentes).
# structured_plc.py - Simula bloques funcionales (FB) y funciones (FC) en Python
# Puede ejecutarse en Thonny y adaptarse a Raspberry Pi para control real
class FB_Motor:
"""FB para control de un motor (con estado interno)."""
def __init__(self, nombre):
self.nombre = nombre
self.estado = False # interno
self.contador_arranques = 0
def evaluar(self, marcha, paro):
"""Llamada en cada ciclo."""
if marcha and not paro:
if not self.estado:
self.contador_arranques += 1
self.estado = True
elif paro:
self.estado = False
return self.estado
def get_arranques(self):
return self.contador_arranques
class FC_Suma:
"""FC (sin estado) - simplemente suma dos números."""
@staticmethod
def ejecutar(a, b):
return a + b
# Programa principal (simula OB1)
if __name__ == "__main__":
# Instanciamos dos motores (cada uno con su propio estado)
motor_cinta = FB_Motor("Cinta")
motor_agitador = FB_Motor("Agitador")
# Simulamos entradas en varios ciclos
for ciclo in range(5):
print(f"\n--- Ciclo {ciclo+1} ---")
# Entradas (simuladas)
marcha_cinta = (ciclo == 0 or ciclo == 2)
paro_cinta = (ciclo == 3)
marcha_agitador = (ciclo >= 1)
paro_agitador = (ciclo == 4)
# Evaluar motores
est_cinta = motor_cinta.evaluar(marcha_cinta, paro_cinta)
est_agit = motor_agitador.evaluar(marcha_agitador, paro_agitador)
# Usar FC para calcular un total (por ejemplo, suma de arranques)
total_arranques = FC_Suma.ejecutar(motor_cinta.get_arranques(), motor_agitador.get_arranques())
print(f"Motor cinta: {'ON' if est_cinta else 'OFF'} (arranques: {motor_cinta.get_arranques()})")
print(f"Motor agitador: {'ON' if est_agit else 'OFF'} (arranques: {motor_agitador.get_arranques()})")
print(f"Total arranques (FC): {total_arranques}")
# Se observa que cada FB mantiene su contador independiente.
Este código implementa un FB con estado (contador de arranques) y una FC sin estado (suma). Demuestra la reutilización: dos instancias de FB_Motor con datos independientes. Se puede probar en Thonny y luego usar con GPIO para controlar motores reales.
Reutilización del código y buenas prácticas
La programación estructurada fomenta la reutilización. Un bloque bien diseñado puede ser utilizado en múltiples proyectos o múltiples veces dentro del mismo proyecto. Para maximizar este beneficio:
- Nombres descriptivos: El nombre del bloque debe reflejar su función (ej.
FB_ControlValvula,FC_ConvertirCelsius). - Interfaces claras: Definir entradas, salidas y parámetros de manera explícita. Evitar variables globales dentro de los bloques.
- Documentación: Comentar el propósito del bloque, el significado de cada entrada/salida y ejemplos de uso.
- Pruebas modulares: Probar cada bloque de forma independiente antes de integrarlo.
- Control de versiones: Mantener un repositorio de librerías de bloques reutilizables.
En entornos industriales, las empresas suelen desarrollar sus propias librerías de bloques para tareas comunes (control de motores, lecturas de sensores, comunicaciones), lo que acelera el desarrollo y aumenta la fiabilidad.
La programación estructurada evoluciona hacia la programación orientada a objetos (POO) en PLC. La nueva edición de IEC 61131-3 (prevista 2026) incluye conceptos como clases, herencia y métodos, permitiendo una encapsulación aún más potente. Los fabricantes ya ofrecen extensiones (como Siemens S7-1500 con IEC 61131-3 OOP) que facilitan la creación de bibliotecas reutilizables de alta calidad. Además, la integración con Git y CI/CD en entornos de desarrollo permite un control de versiones y pruebas automáticas de los módulos.
En Python, los conceptos de FB y FC se implementan naturalmente con clases (para FB) y funciones (para FC). Podemos crear una librería de control para GPIO que encapsule el comportamiento de un motor, un LED o un sensor. Cada instancia maneja su propio estado y pines. Esto es análogo a los FB en PLC y promueve la reutilización en proyectos de automatización con Raspberry Pi.
En Arduino, podemos simular FB mediante clases de C++. Por ejemplo, una clase Motor con métodos on(), off() y variables internas. El Arduino PLC IDE permite programar directamente con FB y FC siguiendo IEC 61131-3, aprovechando la modularidad en proyectos industriales con Arduino Opta o Portenta.
- ✅ Comprender activamente cada línea de código generada
- ✅ Personalizar la solución según tus necesidades específicas
- ✅ Iterar y refinar el código hasta dominar la lógica
- ✅ Desarrollar habilidades de comunicación técnica con IA
📋 Instrucciones de Uso:
- Copia el prompt completo del recuadro superior.
- Personaliza los campos entre corchetes [CAMPO] con tu información específica.
- Pega el prompt en una nueva conversación con tu IA favorita.
- Itera y refina: Pide aclaraciones, modificaciones o añade requisitos adicionales.
- Prueba el código en Thonny primero, luego en tu Raspberry Pi y ajusta según resultados.
💼 Caso Práctico Industrial
Contexto: En una planta de envasado, se tienen tres estaciones idénticas (llenado, tapado, etiquetado). Cada estación debe controlarse con la misma lógica (actuadores y sensores). El ingeniero decide crear un bloque funcional FB_Estacion que encapsule el control de una estación.
Pregunta: ¿Qué tipo de bloque debe usar y cómo se implementaría en el programa principal?
🍓 Raspberry Pi + Python
Contexto: En una Raspberry Pi, se quiere controlar dos LEDs (simulando motores) con la misma lógica: parpadeo con periodo configurable. Se crea una clase LEDParpadeante con método actualizar() que recibe el tiempo actual y activa el LED según su periodo.
Pregunta: ¿Qué ventaja aporta esta clase frente a un código repetitivo para cada LED?
🐍 Instancias de FB
Contexto: Se tiene la siguiente clase Python (simulando un FB):
class Contador:
def __init__(self):
self.valor = 0
def incrementar(self):
self.valor += 1
Se crean dos instancias: c1 = Contador() y c2 = Contador(). Se llama a c1.incrementar() tres veces y a c2.incrementar() una vez.
Pregunta: ¿Cuáles son los valores de c1.valor y c2.valor?
| Característica | FB (Function Block) | FC (Function) |
|---|---|---|
| Memoria (estado interno) | Sí (variables internas persistentes) | No (solo variables temporales) |
| Instancias múltiples | Sí, cada instancia tiene su propia copia de datos | No, se llama directamente |
| Bloques de datos asociados | Requiere un DB de instancia (en algunos PLC) | No necesita DB |
| Uso típico | Control de dispositivos, temporizadores, contadores | Cálculos, conversiones, lógica combinacional |
📚 Recursos Técnicos y Normativos 2026
Normativas y Estándares
- IEC 61131-3:2013 + enmiendas 2025 - Programmable controllers — Part 3: Programming languages
- PLCopen - Guías de programación estructurada