(* === 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;