// plc_macros.ems // EtherasMicroScript - Standard PLC-style MACRO library (macro/inline expansion) // Notes: // - Requires MACRO support with auto-renaming of private vars starting with '_'. // - Timer IDs (tonId) must be unique per instance (pass explicit strings). // - Use OUT params for outputs / latched memories. // ------------------------------ // Latches // ------------------------------ // SR: Set-dominant latch (if both true => SET wins) MACRO SR(setCond, resetCond, OUT q) { IF resetCond { q = 0 } IF setCond { q = 1 } } // RS: Reset-dominant latch (if both true => RESET wins) MACRO RS(setCond, resetCond, OUT q) { IF setCond { q = 1 } IF resetCond { q = 0 } } // ------------------------------ // Comparators / Analog helpers // ------------------------------ // Hysteresis / Schmitt trigger // - Turns ON when value < onTh // - Turns OFF when value > offTh // - Keeps previous state otherwise (q is the memory) MACRO HYST(value, onTh, offTh, OUT q) { IF value < onTh { q = 1 } IF value > offTh { q = 0 } } // Deadband around a center: output 1 if above (center+db), 0 if below (center-db) MACRO DEADBAND(value, center, db, OUT q) { IF value > (center + db) { q = 1 } IF value < (center - db) { q = 0 } } // ------------------------------ // Edge helpers // ------------------------------ // Rising edge output (wrapper) MACRO R_TRIG(sig, OUT q) { q = P_EDGE(sig) } // Falling edge output (no N_EDGE required) MACRO F_TRIG(sig, OUT q) { q = 0 IF _prev == 1 AND sig == 0 { q = 1 } _prev = sig } // ------------------------------ // Timers // ------------------------------ // TP: Pulse timer (one-shot) // - On rising edge of trig: q=1 for ms, then returns to 0 // - Uses TON internally; pass a unique tonId per instance MACRO TP(tonId, trig, ms, OUT q) { IF P_EDGE(trig) { _active = 1 T_RESET(tonId) } IF _active { q = 1 IF TON(tonId, 1, ms) { _active = 0 q = 0 } } ELSE { q = 0 } } // TON_B: TON with explicit Q output (handy if your TON returns Q but you want a named OUT) MACRO TON_B(tonId, cond, ms, OUT q) { q = TON(tonId, cond, ms) } // TOF_B: TOF with explicit Q output MACRO TOF_B(tofId, cond, ms, OUT q) { q = TOF(tofId, cond, ms) } // ------------------------------ // Input conditioning // ------------------------------ // DEBOUNCE: stable-change debounce // - outSig changes only if inSig stays different for ms // - pass unique tonId per instance MACRO DEBOUNCE(tonId, inSig, ms, OUT outSig) { IF inSig != outSig { IF TON(tonId, 1, ms) { outSig = inSig T_RESET(tonId) } } ELSE { T_RESET(tonId) } } // ON_DELAY: output turns ON only after input has been ON for ms; turns OFF immediately MACRO ON_DELAY(tonId, inSig, ms, OUT q) { IF inSig { IF TON(tonId, 1, ms) { q = 1 } } ELSE { q = 0 T_RESET(tonId) } } // OFF_DELAY: output turns OFF only after input has been OFF for ms; turns ON immediately MACRO OFF_DELAY(tonId, inSig, ms, OUT q) { IF inSig { q = 1 T_RESET(tonId) } ELSE { IF TON(tonId, 1, ms) { q = 0 } } } // ------------------------------ // Counters // ------------------------------ // CTU: Count Up // - increments cv on rising edge of edgeSig // - resets cv to 0 when resetSig true // - q=1 when cv >= preset MACRO CTU(edgeSig, resetSig, preset, OUT cv, OUT q) { IF resetSig { cv = 0 } IF P_EDGE(edgeSig) { cv = cv + 1 } IF cv >= preset { q = 1 } ELSE { q = 0 } } // CTD: Count Down // - loads cv=preset when loadSig true // - decrements cv on rising edge of edgeSig (down to 0) // - q=1 when cv <= 0 MACRO CTD(edgeSig, loadSig, preset, OUT cv, OUT q) { IF loadSig { cv = preset } IF P_EDGE(edgeSig) AND cv > 0 { cv = cv - 1 } IF cv <= 0 { q = 1 } ELSE { q = 0 } } // CTUD: Count Up/Down (simple) // - upEdge increments, downEdge decrements // - reset sets cv=0 // - q=1 when cv >= preset MACRO CTUD(upEdge, downEdge, resetSig, preset, OUT cv, OUT q) { IF resetSig { cv = 0 } IF P_EDGE(upEdge) { cv = cv + 1 } IF P_EDGE(downEdge) AND cv > 0 { cv = cv - 1 } IF cv >= preset { q = 1 } ELSE { q = 0 } } // ------------------------------ // Misc common PLC patterns // ------------------------------ // ONCE: run action once when cond becomes true (q is a one-cycle pulse) MACRO ONCE(cond, OUT q) { q = 0 IF cond AND !_done { q = 1 _done = 1 } IF !cond { _done = 0 } } // FAULT_LATCH: latch a fault until reset input // - faultSet sets latch // - faultReset clears latch MACRO FAULT_LATCH(faultSet, faultReset, OUT q) { SR(faultSet, faultReset, q) }