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).

Árbol de proyecto en TIA Portal mostrando 'Program blocks' con Main [OB1] y el diálogo 'Add new block' con los cuatro tipos de bloques: Organization Block (OB) en violeta, Function Block (FB) en azul, Function (FC) en verde y Data Block (DB) en azul oscuro.
Figura 10: Los cuatro tipos de bloques de programa en TIA Portal – OB, FB, FC y DB – visibles en el árbol de proyecto y en el diálogo de creación de bloques. Fuente: PLCsteps.com / Licencia: Uso educativo – todos los derechos del autor.
🐍 Simulación de FB y FC en Python (reutilización y estado)
# 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.

Tendencias Actuales 2026

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.

Aplicaciones con Raspberry Pi

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.

Aplicaciones con Arduino

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.

💼 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?

Tabla 1: Comparativa entre FB y FC
CaracterísticaFB (Function Block)FC (Function)
Memoria (estado interno)Sí (variables internas persistentes)No (solo variables temporales)
Instancias múltiplesSí, cada instancia tiene su propia copia de datosNo, se llama directamente
Bloques de datos asociadosRequiere un DB de instancia (en algunos PLC)No necesita DB
Uso típicoControl de dispositivos, temporizadores, contadoresCálculos, conversiones, lógica combinacional

📚 Recursos Técnicos y Normativos 2026

Normativas y Estándares

Recursos Raspberry Pi y Python