Ir al contenido

← Controlar una salida digital del PLC desde Node-RED (rpiplc)

Mezclado industrial (HMI táctil)TouchBerry PiGPIOControl

Controlar una salida digital del PLC desde Node-RED (rpiplc) — ejemplo completo

Activa un relé desde un dashboard de Node-RED con rpiplc-digital-write, enclavamientos por software y apagado automático de seguridad en un Raspberry PLC.

Programa completo y ejecutable para el TouchBerry Pi (digital-output-q0-rpiplc-node-red.js): incluye cabecera de conexionado, requisitos y notas de integración.

Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales

Vista de solo lectura.

/*
 * COMPLETE EXAMPLE — Digital output Q0 from Node-RED (rpiplc-digital-write)
 *
 * Hardware:  TouchBerry Pi (Raspberry Pi + Industrial Shields PLC, touch screen)
 * Based on:  industrial mixing project (Node-RED touch HMI)
 *
 * Requirements:
 *   - rpiplc node package for Node-RED (Industrial Shields), which gives
 *     direct access to the PLC I/O: rpiplc-digital-write, etc.
 *   - Q0.0 wired to the mixing machine contactor (with its thermal
 *     protection and hardware emergency stop downstream).
 *
 * Architecture:
 *   The "Force mixing" button on the dashboard does not drive the relay
 *   directly: it goes through an interlock function that checks the
 *   'materials OK' LED (fed by MQTT) and the administrator session.
 *   Only then is output Q0 written, with an automatic safety
 *   shutdown in case the stop flow never arrives.
 *
 * Flow wiring:
 *
 *   [mqtt in planta/Gk190-1/ready] ──> [function 1: storeReady]
 *
 *   [ui-button "Start"] ─┐
 *   [ui-button "Stop"]  ─┴─> [function 2: interlockQ0] ──> [rpiplc-digital-write]
 *                                       │
 *                                       └──(output 2)──> [ui-notification "Blocked"]
 *
 *   [function 2 also] ──(trigger)──> the delayed shutdown is done with
 *   a deferred node.send() inside the function itself (see code).
 *
 * Node configuration:
 *   - rpiplc-digital-write: pin Q0.0; the value arrives in msg.payload (0/1).
 *   - ui-button "Start": payload = "ON" / ui-button "Stop": payload = "OFF".
 */

// ============================================================
// function 1: storeReady
// ------------------------------------------------------------
// Stores in the global context the last 'materials OK' state
// received over MQTT, so the interlock can check it.
// ============================================================
global.set("materialsOk", msg.payload == "1");
return msg;                            // continues to the dashboard LED


// ============================================================
// function 2: interlockQ0
// ------------------------------------------------------------
// Input: msg.payload = "ON" | "OFF" (dashboard buttons)
// Output 1 -> rpiplc-digital-write (msg.payload = 1 | 0)
// Output 2 -> ui-notification (command rejected)
//
// Interlock rules to switch on:
//   1. Materials OK (MQTT ready topic == "1")
//   2. Active administrator session for the manual override
// Stop (OFF) is ALWAYS accepted, unconditionally.
// ============================================================
const MAX_RUN_TIME_MS = 30 * 60 * 1000;   // safety shutdown: 30 min

if (msg.payload === "OFF") {
    // Stop is never blocked
    const t = context.get("shutdownTimer");
    if (t) { clearTimeout(t); context.set("shutdownTimer", null); }
    global.set("q0State", false);
    msg.payload = 0;                       // rpiplc-digital-write: Q0.0 = 0
    return [msg, null];
}

if (msg.payload === "ON") {
    // Interlock 1: materials ready
    if (!global.get("materialsOk")) {
        msg.payload = "Blocked: materials not OK";
        return [null, msg];
    }
    // Interlock 2: valid administrator session
    const last    = global.get("lastAdminLoginDate");
    const timeout = global.get("adminLoginTimeout") || 600000;
    if (!(last && (new Date() - new Date(last)) < timeout)) {
        msg.payload = "Blocked: log in as administrator";
        return [null, msg];
    }

    // All good: switch Q0 on and arm the safety shutdown
    global.set("q0State", true);

    const prevTimer = context.get("shutdownTimer");
    if (prevTimer) clearTimeout(prevTimer);
    const timer = setTimeout(() => {
        global.set("q0State", false);
        context.set("shutdownTimer", null);
        node.warn("Q0 safety shutdown after maximum run time");
        node.send([{ payload: 0 }, null]);   // forces Q0.0 = 0
    }, MAX_RUN_TIME_MS);
    context.set("shutdownTimer", timer);

    msg.payload = 1;                       // rpiplc-digital-write: Q0.0 = 1
    return [msg, null];
}

return [null, null];                       // unknown payload: ignore
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales