Enhance PLC algorithm documentation for lighting control

- Updated data structures for input and output to align with redesign recommendations
- Clarified function block logic for fb_lightControl and fb_switch
- Improved command handling and priority logic in the fb_lightControl implementation
- Added detailed comments for better understanding of the algorithm flow

This update ensures consistency with the redesign documentation and improves clarity for future development.
This commit is contained in:
2026-02-07 22:11:26 +02:00
parent 83fee6c24d
commit c2f6655aae

View File

@@ -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