Initial commit: CODESYS tree, integration docs, Node-RED scripts.

PLC application sources (NVL, rooms, boiler), HA/Node-RED integration guide, and NVL patch utilities.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-26 21:21:56 +03:00
commit d98c44bfa3
27 changed files with 3844 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
(* === DECLARATION === *)
FUNCTION_BLOCK fb_boiler
VAR_INPUT
// Control Commands
ha_on: BOOL; // Home Assistant ON command
ha_off: BOOL; // Home Assistant OFF command
schedule_on: BOOL; // Scheduled ON
schedule_off: BOOL; // Scheduled OFF
// Safety
emergency_stop: BOOL; // Emergency stop input
// Configuration
max_on_time: TIME := T#8H; // Maximum ON time (default 8 hours)
END_VAR
VAR_OUTPUT
// Outputs
relay_control: BOOL; // Control output to relay
// Status
state: BOOL; // Current state
on_time_seconds: DINT; // Current ON time in seconds
remaining_seconds: DINT; // Remaining time in seconds
// Safety Status
emergency_active: BOOL; // Emergency stop active
time_limit_reached: BOOL; // Time limit was reached
error_state: BOOL; // Error flag
error_code: INT; // Error code
END_VAR
VAR
// Internal State
internal_state: BOOL := FALSE;
last_state: BOOL := FALSE;
// Edge Detection
r_trig_ha_on: R_TRIG;
r_trig_ha_off: R_TRIG;
r_trig_schedule_on: R_TRIG;
r_trig_schedule_off: R_TRIG;
r_trig_emergency: R_TRIG;
f_trig_emergency: F_TRIG;
// Timers
on_timer: TON; // Counts ON time
// Time tracking
max_on_seconds: DINT;
END_VAR
(* === IMPLEMENTATION === *)
// =====================================================
// fb_boiler - Implementation
// =====================================================
// Priority (highest to lowest):
// 1. Emergency Stop - Immediate shutdown
// 2. Time Limit Exceeded - Auto shutdown
// 3. OFF Commands - Turn off
// 4. ON Commands - Turn on (if safe)
// =====================================================
// Calculate max time in seconds
max_on_seconds := TIME_TO_DINT(max_on_time) / 1000;
// Edge detection for commands
r_trig_ha_on(CLK := ha_on);
r_trig_ha_off(CLK := ha_off);
r_trig_schedule_on(CLK := schedule_on);
r_trig_schedule_off(CLK := schedule_off);
r_trig_emergency(CLK := emergency_stop);
f_trig_emergency(CLK := emergency_stop);
// =====================================================
// SAFETY CHECKS (highest priority)
// =====================================================
// Priority 1: Emergency Stop
IF emergency_stop THEN
internal_state := FALSE;
emergency_active := TRUE;
error_state := TRUE;
error_code := 1; // Emergency stop active
// Priority 2: Time Limit Check
ELSIF on_timer.Q THEN
internal_state := FALSE;
time_limit_reached := TRUE;
error_state := TRUE;
error_code := 2; // Time limit exceeded
// =====================================================
// CONTROL LOGIC (only when safe)
// =====================================================
ELSE
// Clear safety flags when emergency released
IF f_trig_emergency.Q THEN
emergency_active := FALSE;
error_state := FALSE;
error_code := 0;
END_IF;
// Clear time limit flag when boiler turned off
IF NOT internal_state THEN
time_limit_reached := FALSE;
IF error_code = 2 THEN
error_state := FALSE;
error_code := 0;
END_IF;
END_IF;
// Priority 3: OFF Commands (HA OFF or Schedule OFF)
IF r_trig_ha_off.Q OR r_trig_schedule_off.Q THEN
internal_state := FALSE;
// Priority 4: ON Commands (HA ON or Schedule ON)
ELSIF (r_trig_ha_on.Q OR r_trig_schedule_on.Q) AND NOT time_limit_reached THEN
internal_state := TRUE;
END_IF;
END_IF;
// =====================================================
// TIMER MANAGEMENT
// =====================================================
// ON timer - counts total ON time
on_timer(
IN := internal_state AND NOT emergency_active,
PT := max_on_time
);
// Calculate current ON time in seconds
IF internal_state THEN
on_time_seconds := TIME_TO_DINT(on_timer.ET) / 1000;
remaining_seconds := max_on_seconds - on_time_seconds;
IF remaining_seconds < 0 THEN
remaining_seconds := 0;
END_IF;
ELSE
on_time_seconds := 0;
remaining_seconds := max_on_seconds;
END_IF;
// =====================================================
// OUTPUT ASSIGNMENT
// =====================================================
// Relay control - only ON when state is ON and no emergency
relay_control := internal_state AND NOT emergency_active;
// State output
state := internal_state;