diff --git a/docs/codesys/plc-algorithm-design.md b/docs/codesys/plc-algorithm-design.md index b83e3cf..5eadc56 100644 --- a/docs/codesys/plc-algorithm-design.md +++ b/docs/codesys/plc-algorithm-design.md @@ -49,6 +49,8 @@ This document defines the PLC algorithms for the home automation system: ## Part 1: Lighting Control Algorithm +**Alignment**: This section follows the structures and logic from **`docs/redesign/fb_switch-redesign-recommendation.md`**: flat `struct_switches` / `struct_lights`, `fb_lightControl` with HA ON/OFF + Zigbee toggle only, and `fb_switch` applying global commands by overwriting outputs. + ### 1.1 Design Goals - **Command-based control** for Home Assistant (explicit ON/OFF) @@ -56,165 +58,151 @@ This document defines the PLC algorithms for the home automation system: - **State synchronization** between PLC and Home Assistant - **Relay status feedback** for actual state monitoring -### 1.2 Data Structures +### 1.2 Data Structures (per Redesign Recommendation) -#### Input Structure (from Node-RED/Home Assistant) +#### Input: struct_switches (flat, for Node-RED/JSON compatibility) ```iec -TYPE struct_light_commands : +TYPE struct_switches : STRUCT - // Home Assistant Commands (momentary pulses) - ha_on: BOOL; // HA command: Turn ON (edge detected) - ha_off: BOOL; // HA command: Turn OFF (edge detected) + // Home Assistant Commands (set/reset) + ha_l1_on: BOOL; // HA command: Light 1 ON + ha_l1_off: BOOL; // HA command: Light 1 OFF + ha_l2_on: BOOL; + ha_l2_off: BOOL; + ha_l3_on: BOOL; + ha_l3_off: BOOL; + ha_l4_on: BOOL; + ha_l4_off: BOOL; + ha_l5_on: BOOL; + ha_l5_off: BOOL; + ha_l6_on: BOOL; + ha_l6_off: BOOL; - // Zigbee Switch Input (toggle trigger) - zigbee_toggle: BOOL; // Zigbee button press (edge detected) + // Zigbee Switch Inputs (edge detection for toggle) + zigbee_sw1: BOOL; // Zigbee switch 1 (edge detected) + zigbee_sw2: BOOL; + zigbee_sw3: BOOL; + zigbee_sw4: BOOL; + zigbee_sw5: BOOL; + zigbee_sw6: BOOL; // Global Commands - all_on: BOOL; // Global: All lights ON - all_off: BOOL; // Global: All lights OFF -END_STRUCT -END_TYPE - -TYPE struct_room_commands : -STRUCT - l_1: struct_light_commands; - l_2: struct_light_commands; - l_3: struct_light_commands; - l_4: struct_light_commands; - l_5: struct_light_commands; - l_6: struct_light_commands; - - // Room-level global commands - room_all_on: BOOL; - room_all_off: BOOL; + ha_all_on: BOOL; // HA: All lights ON + ha_all_off: BOOL; // HA: All lights OFF END_STRUCT END_TYPE ``` -#### Output Structure (to Node-RED/Home Assistant) +#### Output: struct_lights (flat, control + status feedback) ```iec -TYPE struct_light_status : +TYPE struct_lights : STRUCT - state: BOOL; // Control output (to relay) - relay_status: BOOL; // Actual relay state (read back) -END_STRUCT -END_TYPE - -TYPE struct_room_status : -STRUCT - l_1: struct_light_status; - l_2: struct_light_status; - l_3: struct_light_status; - l_4: struct_light_status; - l_5: struct_light_status; - l_6: struct_light_status; + l_1: BOOL; // Light 1 control output + l_2: BOOL; // Light 2 control output + l_3: BOOL; // Light 3 control output + l_4: BOOL; // Light 4 control output + l_5: BOOL; // Light 5 control output + l_6: BOOL; // Light 6 control output + + // Status feedback (read from actual relay outputs) + l_1_status: BOOL; // Actual relay 1 state + l_2_status: BOOL; // Actual relay 2 state + l_3_status: BOOL; // Actual relay 3 state + l_4_status: BOOL; // Actual relay 4 state + l_5_status: BOOL; // Actual relay 5 state + l_6_status: BOOL; // Actual relay 6 state END_STRUCT END_TYPE ``` -### 1.3 Function Block: fb_lightControl +### 1.3 Function Block: fb_lightControl (per Redesign) -Individual light control with command/toggle support. +Individual light control: HA ON/OFF + Zigbee toggle only. No all_on/all_off inside this FB. ```iec FUNCTION_BLOCK fb_lightControl VAR_INPUT - // Commands - ha_on: BOOL; // Home Assistant ON command - ha_off: BOOL; // Home Assistant OFF command - zigbee_toggle: BOOL; // Zigbee toggle trigger - all_on: BOOL; // Global all-on command - all_off: BOOL; // Global all-off command + // Home Assistant Commands + ha_on: BOOL; // HA command: Turn ON + ha_off: BOOL; // HA command: Turn OFF - // Status feedback - relay_feedback: BOOL; // Actual relay state (from EtherCAT) + // Zigbee Switch Input + zigbee_sw: BOOL; // Zigbee switch (edge detected) + + // Status Feedback + relay_status: BOOL; // Actual relay state (read from EtherCAT) END_VAR VAR_OUTPUT - output: BOOL; // Control output to relay - status: BOOL; // Actual status (for feedback) + light_output: BOOL; // Control output to relay + light_status: BOOL; // Status to send back (actual relay state) END_VAR VAR - // Edge detection + // Edge triggers r_trig_ha_on: R_TRIG; r_trig_ha_off: R_TRIG; r_trig_zigbee: R_TRIG; - r_trig_all_on: R_TRIG; - r_trig_all_off: R_TRIG; // Internal state light_state: BOOL := FALSE; END_VAR ``` -#### Algorithm Logic +#### Algorithm Logic (Priority: HA OFF > HA ON > Zigbee toggle) ```iec // ===================================================== -// fb_lightControl - Implementation +// fb_lightControl - Implementation (per redesign) // ===================================================== // Priority (highest to lowest): -// 1. Global all_off - Forces all lights OFF -// 2. Global all_on - Forces all lights ON -// 3. HA OFF command - Explicit OFF -// 4. HA ON command - Explicit ON -// 5. Zigbee toggle - Toggle current state +// 1. HA OFF command - Always turns light OFF +// 2. HA ON command - Always turns light ON +// 3. Zigbee toggle - Toggles current state +// 4. Maintain current state // ===================================================== -// Edge detection for all commands +// Edge detection for commands r_trig_ha_on(CLK := ha_on); r_trig_ha_off(CLK := ha_off); -r_trig_zigbee(CLK := zigbee_toggle); -r_trig_all_on(CLK := all_on); -r_trig_all_off(CLK := all_off); +r_trig_zigbee(CLK := zigbee_sw); -// State machine with priority handling -IF r_trig_all_off.Q THEN - // Priority 1: Global OFF (highest priority) +// State machine +IF r_trig_ha_off.Q THEN light_state := FALSE; - -ELSIF r_trig_all_on.Q THEN - // Priority 2: Global ON - light_state := TRUE; - -ELSIF r_trig_ha_off.Q THEN - // Priority 3: HA OFF command - light_state := FALSE; - ELSIF r_trig_ha_on.Q THEN - // Priority 4: HA ON command light_state := TRUE; - ELSIF r_trig_zigbee.Q THEN - // Priority 5: Zigbee toggle light_state := NOT light_state; END_IF; // Output assignment -output := light_state; +light_output := light_state; -// Status feedback from actual relay -status := relay_feedback; +// Status feedback (read from actual relay) +light_status := relay_status; ``` -### 1.4 Function Block: fb_roomLights +### 1.4 Function Block: fb_switch (per Redesign) -Room-level lighting control with 6 lights per room. +Room-level block: 6× fb_lightControl; global commands applied by overwriting outputs. ```iec -FUNCTION_BLOCK fb_roomLights +FUNCTION_BLOCK fb_switch VAR_INPUT - commands: struct_room_commands; - relay_feedback: ARRAY[1..6] OF BOOL; // Actual relay states + switches: struct_switches; + relay_status: struct_lights; // Read from EtherCAT outputs (actual relay states) END_VAR VAR_OUTPUT - outputs: ARRAY[1..6] OF BOOL; // Control outputs - status: struct_room_status; + lights: struct_lights; END_VAR VAR - lights: ARRAY[1..6] OF fb_lightControl; - i: INT; + l1: fb_lightControl; + l2: fb_lightControl; + l3: fb_lightControl; + l4: fb_lightControl; + l5: fb_lightControl; + l6: fb_lightControl; END_VAR ``` @@ -222,86 +210,85 @@ END_VAR ```iec // ===================================================== -// fb_roomLights - Implementation +// fb_switch - Implementation (per redesign) // ===================================================== -// Light 1 -lights[1]( - ha_on := commands.l_1.ha_on, - ha_off := commands.l_1.ha_off, - zigbee_toggle := commands.l_1.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_1.all_on, - all_off := commands.room_all_off OR commands.l_1.all_off, - relay_feedback := relay_feedback[1] +// Light 1 Control +l1( + ha_on := switches.ha_l1_on, + ha_off := switches.ha_l1_off, + zigbee_sw := switches.zigbee_sw1, + relay_status := relay_status.l_1_status ); -outputs[1] := lights[1].output; -status.l_1.state := lights[1].output; -status.l_1.relay_status := lights[1].status; +lights.l_1 := l1.light_output; +lights.l_1_status := l1.light_status; -// Light 2 -lights[2]( - ha_on := commands.l_2.ha_on, - ha_off := commands.l_2.ha_off, - zigbee_toggle := commands.l_2.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_2.all_on, - all_off := commands.room_all_off OR commands.l_2.all_off, - relay_feedback := relay_feedback[2] +// Light 2 Control +l2( + ha_on := switches.ha_l2_on, + ha_off := switches.ha_l2_off, + zigbee_sw := switches.zigbee_sw2, + relay_status := relay_status.l_2_status ); -outputs[2] := lights[2].output; -status.l_2.state := lights[2].output; -status.l_2.relay_status := lights[2].status; +lights.l_2 := l2.light_output; +lights.l_2_status := l2.light_status; -// Light 3 -lights[3]( - ha_on := commands.l_3.ha_on, - ha_off := commands.l_3.ha_off, - zigbee_toggle := commands.l_3.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_3.all_on, - all_off := commands.room_all_off OR commands.l_3.all_off, - relay_feedback := relay_feedback[3] +// Light 3 Control +l3( + ha_on := switches.ha_l3_on, + ha_off := switches.ha_l3_off, + zigbee_sw := switches.zigbee_sw3, + relay_status := relay_status.l_3_status ); -outputs[3] := lights[3].output; -status.l_3.state := lights[3].output; -status.l_3.relay_status := lights[3].status; +lights.l_3 := l3.light_output; +lights.l_3_status := l3.light_status; -// Light 4 -lights[4]( - ha_on := commands.l_4.ha_on, - ha_off := commands.l_4.ha_off, - zigbee_toggle := commands.l_4.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_4.all_on, - all_off := commands.room_all_off OR commands.l_4.all_off, - relay_feedback := relay_feedback[4] +// Light 4 Control +l4( + ha_on := switches.ha_l4_on, + ha_off := switches.ha_l4_off, + zigbee_sw := switches.zigbee_sw4, + relay_status := relay_status.l_4_status ); -outputs[4] := lights[4].output; -status.l_4.state := lights[4].output; -status.l_4.relay_status := lights[4].status; +lights.l_4 := l4.light_output; +lights.l_4_status := l4.light_status; -// Light 5 -lights[5]( - ha_on := commands.l_5.ha_on, - ha_off := commands.l_5.ha_off, - zigbee_toggle := commands.l_5.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_5.all_on, - all_off := commands.room_all_off OR commands.l_5.all_off, - relay_feedback := relay_feedback[5] +// Light 5 Control +l5( + ha_on := switches.ha_l5_on, + ha_off := switches.ha_l5_off, + zigbee_sw := switches.zigbee_sw5, + relay_status := relay_status.l_5_status ); -outputs[5] := lights[5].output; -status.l_5.state := lights[5].output; -status.l_5.relay_status := lights[5].status; +lights.l_5 := l5.light_output; +lights.l_5_status := l5.light_status; -// Light 6 -lights[6]( - ha_on := commands.l_6.ha_on, - ha_off := commands.l_6.ha_off, - zigbee_toggle := commands.l_6.zigbee_toggle, - all_on := commands.room_all_on OR commands.l_6.all_on, - all_off := commands.room_all_off OR commands.l_6.all_off, - relay_feedback := relay_feedback[6] +// Light 6 Control +l6( + ha_on := switches.ha_l6_on, + ha_off := switches.ha_l6_off, + zigbee_sw := switches.zigbee_sw6, + relay_status := relay_status.l_6_status ); -outputs[6] := lights[6].output; -status.l_6.state := lights[6].output; -status.l_6.relay_status := lights[6].status; +lights.l_6 := l6.light_output; +lights.l_6_status := l6.light_status; + +// Global Commands (overwrite outputs per redesign) +IF switches.ha_all_off THEN + lights.l_1 := FALSE; + lights.l_2 := FALSE; + lights.l_3 := FALSE; + lights.l_4 := FALSE; + lights.l_5 := FALSE; + lights.l_6 := FALSE; +ELSIF switches.ha_all_on THEN + lights.l_1 := TRUE; + lights.l_2 := TRUE; + lights.l_3 := TRUE; + lights.l_4 := TRUE; + lights.l_5 := TRUE; + lights.l_6 := TRUE; +END_IF; ``` --- @@ -570,23 +557,23 @@ VAR // ===================================================== // LIGHTING CONTROL // ===================================================== - // Room instances - masterBedroom: fb_roomLights; - masterBathroom: fb_roomLights; - bedroom_1: fb_roomLights; - bedroom_2: fb_roomLights; - bathroom: fb_roomLights; - guest_wc: fb_roomLights; - kitchen: fb_roomLights; - pantry: fb_roomLights; - livingRoom: fb_roomLights; - diningRoom: fb_roomLights; - entrance: fb_roomLights; - hallway: fb_roomLights; - veranda: fb_roomLights; - front: fb_roomLights; - back: fb_roomLights; - side: fb_roomLights; + // Room instances (fb_switch per redesign) + masterBedroom: fb_switch; + masterBathroom: fb_switch; + bedroom_1: fb_switch; + bedroom_2: fb_switch; + bathroom: fb_switch; + guest_wc: fb_switch; + kitchen: fb_switch; + pantry: fb_switch; + livingRoom: fb_switch; + diningRoom: fb_switch; + entrance: fb_switch; + hallway: fb_switch; + veranda: fb_switch; + front: fb_switch; + back: fb_switch; + side: fb_switch; // ===================================================== // WATER BOILER CONTROL @@ -612,15 +599,15 @@ END_VAR // SECTION 1: LIGHTING CONTROL // ===================================================== -// Master Bedroom +// Master Bedroom (fb_switch: switches + relay_status in, lights out) masterBedroom( - commands := NVL_Receiver.masterBedroom, - relay_feedback := EtherCAT_Outputs.masterBedroom_relay + switches := NVL_Receiver.masterBedroom, + relay_status := EtherCAT_RelayFeedback.masterBedroom // Actual relay states from I/O ); -NVL_Sender.l_masterBedroom := masterBedroom.status; -EtherCAT_Outputs.masterBedroom := masterBedroom.outputs; - -// ... (repeat for all rooms) +NVL_Sender.l_masterBedroom := masterBedroom.lights; +EtherCAT_Outputs.masterBedroom_l1 := masterBedroom.lights.l_1; +EtherCAT_Outputs.masterBedroom_l2 := masterBedroom.lights.l_2; +// ... l_3..l_6 and repeat for all rooms // ===================================================== // SECTION 2: WATER BOILER CONTROL @@ -676,23 +663,23 @@ NVL_Sender.boiler_status.error_code := waterBoiler.error_code; ```iec VAR_GLOBAL - // Lighting status (existing rooms) - l_masterBedroom: struct_room_status; - l_masterBathroom: struct_room_status; - l_bedroom_1: struct_room_status; - l_bedroom_2: struct_room_status; - l_bathroom: struct_room_status; - l_guestWc: struct_room_status; - l_kitchen: struct_room_status; - l_pantry: struct_room_status; - l_livingRoom: struct_room_status; - l_dinningRoom: struct_room_status; - l_entrance: struct_room_status; - l_hallway: struct_room_status; - l_outVeranda: struct_room_status; - l_outFront: struct_room_status; - l_outBack: struct_room_status; - l_outSide: struct_room_status; + // Lighting status (struct_lights per room, per redesign) + l_masterBedroom: struct_lights; + l_masterBathroom: struct_lights; + l_bedroom_1: struct_lights; + l_bedroom_2: struct_lights; + l_bathroom: struct_lights; + l_guestWc: struct_lights; + l_kitchen: struct_lights; + l_pantry: struct_lights; + l_livingRoom: struct_lights; + l_dinningRoom: struct_lights; + l_entrance: struct_lights; + l_hallway: struct_lights; + l_outVeranda: struct_lights; + l_outFront: struct_lights; + l_outBack: struct_lights; + l_outSide: struct_lights; // Boiler status (NEW) boiler_status: struct_boiler_status; @@ -703,23 +690,23 @@ END_VAR ```iec VAR_GLOBAL - // Lighting commands (existing rooms) - masterBedroom: struct_room_commands; - masterBathroom: struct_room_commands; - bedroom_1: struct_room_commands; - bedroom_2: struct_room_commands; - bathroom: struct_room_commands; - guestWc: struct_room_commands; - kitchen: struct_room_commands; - pantry: struct_room_commands; - livingRoom: struct_room_commands; - dinningRoom: struct_room_commands; - entrance: struct_room_commands; - hallway: struct_room_commands; - outVeranda: struct_room_commands; - outFront: struct_room_commands; - outBack: struct_room_commands; - outSide: struct_room_commands; + // Lighting commands (struct_switches per room, per redesign) + masterBedroom: struct_switches; + masterBathroom: struct_switches; + bedroom_1: struct_switches; + bedroom_2: struct_switches; + bathroom: struct_switches; + guestWc: struct_switches; + kitchen: struct_switches; + pantry: struct_switches; + livingRoom: struct_switches; + dinningRoom: struct_switches; + entrance: struct_switches; + hallway: struct_switches; + outVeranda: struct_switches; + outFront: struct_switches; + outBack: struct_switches; + outSide: struct_switches; // Boiler commands (NEW) boiler: struct_boiler_commands; @@ -771,7 +758,9 @@ END_VAR ## Implementation Notes -1. **Backward Compatibility**: The new lighting structure can coexist with existing code during migration. +1. **Redesign alignment**: Lighting control (Part 1) follows **`docs/redesign/fb_switch-redesign-recommendation.md`**: flat `struct_switches` / `struct_lights`, `fb_lightControl` (HA ON/OFF + Zigbee toggle, no all_on/all_off), `fb_switch` with global commands applied by overwriting outputs. Node-RED should send `ha_l1_on`/`ha_l1_off` and `zigbee_sw1` (edge) per the redesign. + +2. **Backward Compatibility**: The new lighting structure replaces the old toggle-only logic; Node-RED and HA need to be updated to the new command format. 2. **Emergency Stop**: Can be either a physical button (EtherCAT input) or a virtual command from Home Assistant/Node-RED. @@ -781,6 +770,7 @@ END_VAR --- -**Document Version**: 1.0 +**Document Version**: 1.1 **Created**: 2026-02-07 +**Updated**: Aligned Part 1 (Lighting) with `docs/redesign/fb_switch-redesign-recommendation.md`. **Status**: Design Complete - Ready for Implementation