From 9e56b359227967d9788054f37c3e64f94f8da5cf Mon Sep 17 00:00:00 2001 From: nearxos Date: Sat, 7 Feb 2026 22:00:01 +0200 Subject: [PATCH] Refactor project structure and enhance documentation - Improved organization of project files and directories - Updated documentation index in docs/README.md for better navigation - Enhanced clarity in naming conventions and design descriptions Co-authored-by: Cursor --- docs/codesys/plc-algorithm-design.md | 786 +++++++++++++++++++++++++++ 1 file changed, 786 insertions(+) create mode 100644 docs/codesys/plc-algorithm-design.md diff --git a/docs/codesys/plc-algorithm-design.md b/docs/codesys/plc-algorithm-design.md new file mode 100644 index 0000000..b83e3cf --- /dev/null +++ b/docs/codesys/plc-algorithm-design.md @@ -0,0 +1,786 @@ +# PLC Algorithm Design - Lighting and Water Boiler Control + +## Overview + +This document defines the PLC algorithms for the home automation system: +1. **Lighting Control** - Improved logic with command-based control for Home Assistant and toggle-based for Zigbee switches +2. **Water Boiler Control** - Simple ON/OFF with safety features (time limit, emergency stop) + +## System Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ CODESYS PLC (Raspberry Pi) │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ +│ │ NVL_Receiver │ │ MainTask │ │ NVL_Sender │ │ +│ │ (from Node-RED)│ │ (4ms cycle) │ │ (to Node-RED) │ │ +│ └────────┬────────┘ └────────┬────────┘ └──────────┬──────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▲ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ PLC_PRG │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │ +│ │ │ Lights │ │ Boiler │ │ Safety_Monitor │ │ │ +│ │ │ Program │ │ Program │ │ (future) │ │ │ +│ │ └──────┬──────┘ └──────┬──────┘ └─────────────────────────┘ │ │ +│ └─────────┼────────────────┼──────────────────────────────────────┘ │ +│ │ │ │ +│ ▼ ▼ │ +│ ┌─────────────────────────────────────────────────────────────────┐ │ +│ │ EtherCAT I/O │ │ +│ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │ +│ │ │ EL1809 │ │ Output Module │ │ │ +│ │ │ 16x DI 24V │ │ 16x DO (Relays) │ │ │ +│ │ │ (Switches) │ │ (Lights + Boiler) │ │ │ +│ │ └──────────────┘ └──────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────┘ + │ │ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ Physical │ │ Physical │ + │ Switches │ │ Relays │ + └─────────────────┘ └─────────────────┘ +``` + +--- + +## Part 1: Lighting Control Algorithm + +### 1.1 Design Goals + +- **Command-based control** for Home Assistant (explicit ON/OFF) +- **Toggle-based control** for Zigbee switches (edge detection) +- **State synchronization** between PLC and Home Assistant +- **Relay status feedback** for actual state monitoring + +### 1.2 Data Structures + +#### Input Structure (from Node-RED/Home Assistant) + +```iec +TYPE struct_light_commands : +STRUCT + // Home Assistant Commands (momentary pulses) + ha_on: BOOL; // HA command: Turn ON (edge detected) + ha_off: BOOL; // HA command: Turn OFF (edge detected) + + // Zigbee Switch Input (toggle trigger) + zigbee_toggle: BOOL; // Zigbee button press (edge detected) + + // 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; +END_STRUCT +END_TYPE +``` + +#### Output Structure (to Node-RED/Home Assistant) + +```iec +TYPE struct_light_status : +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; +END_STRUCT +END_TYPE +``` + +### 1.3 Function Block: fb_lightControl + +Individual light control with command/toggle support. + +```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 + + // Status feedback + relay_feedback: BOOL; // Actual relay state (from EtherCAT) +END_VAR +VAR_OUTPUT + output: BOOL; // Control output to relay + status: BOOL; // Actual status (for feedback) +END_VAR +VAR + // Edge detection + 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 + +```iec +// ===================================================== +// fb_lightControl - Implementation +// ===================================================== +// 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 +// ===================================================== + +// Edge detection for all 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); + +// State machine with priority handling +IF r_trig_all_off.Q THEN + // Priority 1: Global OFF (highest priority) + 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; + +// Status feedback from actual relay +status := relay_feedback; +``` + +### 1.4 Function Block: fb_roomLights + +Room-level lighting control with 6 lights per room. + +```iec +FUNCTION_BLOCK fb_roomLights +VAR_INPUT + commands: struct_room_commands; + relay_feedback: ARRAY[1..6] OF BOOL; // Actual relay states +END_VAR +VAR_OUTPUT + outputs: ARRAY[1..6] OF BOOL; // Control outputs + status: struct_room_status; +END_VAR +VAR + lights: ARRAY[1..6] OF fb_lightControl; + i: INT; +END_VAR +``` + +#### Algorithm Logic + +```iec +// ===================================================== +// fb_roomLights - Implementation +// ===================================================== + +// 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] +); +outputs[1] := lights[1].output; +status.l_1.state := lights[1].output; +status.l_1.relay_status := lights[1].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] +); +outputs[2] := lights[2].output; +status.l_2.state := lights[2].output; +status.l_2.relay_status := lights[2].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] +); +outputs[3] := lights[3].output; +status.l_3.state := lights[3].output; +status.l_3.relay_status := lights[3].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] +); +outputs[4] := lights[4].output; +status.l_4.state := lights[4].output; +status.l_4.relay_status := lights[4].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] +); +outputs[5] := lights[5].output; +status.l_5.state := lights[5].output; +status.l_5.relay_status := lights[5].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] +); +outputs[6] := lights[6].output; +status.l_6.state := lights[6].output; +status.l_6.relay_status := lights[6].status; +``` + +--- + +## Part 2: Water Boiler Control Algorithm + +### 2.1 Design Goals + +Based on your requirements: +- **Simple ON/OFF control** via relay output only +- **Maximum heating time limit** (safety feature) +- **Emergency stop button** support +- **State monitoring and feedback** + +### 2.2 Data Structures + +#### Boiler Commands (from Node-RED/Home Assistant) + +```iec +TYPE struct_boiler_commands : +STRUCT + // Control Commands + ha_on: BOOL; // Home Assistant: Turn ON + ha_off: BOOL; // Home Assistant: Turn OFF + schedule_on: BOOL; // Scheduled ON (from automation) + schedule_off: BOOL; // Scheduled OFF (from automation) + + // Safety Input + emergency_stop: BOOL; // Emergency stop (physical button or HA) + + // Configuration (can be set via Node-RED) + max_on_time_minutes: INT; // Maximum ON time in minutes (default: 480 = 8 hours) +END_STRUCT +END_TYPE +``` + +#### Boiler Status (to Node-RED/Home Assistant) + +```iec +TYPE struct_boiler_status : +STRUCT + // State + state: BOOL; // Current boiler state (ON/OFF) + relay_output: BOOL; // Actual relay output state + + // Runtime Info + on_time_minutes: INT; // Current ON time in minutes + remaining_minutes: INT; // Remaining time before auto-shutoff + + // Safety Status + emergency_active: BOOL; // Emergency stop is active + time_limit_reached: BOOL; // Max time limit was reached + + // Error Handling + error_state: BOOL; // Error detected + error_code: INT; // Error code (0=none, 1=emergency, 2=time limit) +END_STRUCT +END_TYPE +``` + +### 2.3 Function Block: fb_waterBoiler + +Simple ON/OFF boiler control with time limit and emergency stop. + +```iec +FUNCTION_BLOCK fb_waterBoiler +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 +``` + +### 2.4 Algorithm Logic + +```iec +// ===================================================== +// fb_waterBoiler - 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; +``` + +### 2.5 State Diagram + +``` + ┌─────────────────────────────────────────┐ + │ EMERGENCY STOP │ + │ (highest priority) │ + │ Sets: emergency_active = TRUE │ + │ error_code = 1 │ + │ relay_control = FALSE │ + └─────────────────────────────────────────┘ + │ + ┌──────────────────┴──────────────────┐ + ▼ │ + ┌───────────┐ │ + │ │ │ + START ───►│ OFF │◄──────────────────────────────┤ + │ │ │ + └─────┬─────┘ │ + │ │ + │ ON Command │ + │ (ha_on OR schedule_on) │ + │ AND NOT emergency_stop │ + │ AND NOT time_limit_reached │ + ▼ │ + ┌───────────┐ │ + │ │ Time Limit │ + │ ON │─────Exceeded──────────────────┘ + │ │ (on_timer.Q) + └─────┬─────┘ + │ + │ OFF Command + │ (ha_off OR schedule_off) + │ + └──────────────────────────────────────►OFF +``` + +--- + +## Part 3: Main Program Integration + +### 3.1 PLC_PRG Structure + +```iec +PROGRAM PLC_PRG +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; + + // ===================================================== + // WATER BOILER CONTROL + // ===================================================== + waterBoiler: fb_waterBoiler; + + // ===================================================== + // GLOBAL COMMANDS + // ===================================================== + global_all_lights_off: BOOL; + global_all_lights_on: BOOL; +END_VAR +``` + +### 3.2 Main Program Logic + +```iec +// ===================================================== +// PLC_PRG - Main Program Implementation +// ===================================================== + +// ===================================================== +// SECTION 1: LIGHTING CONTROL +// ===================================================== + +// Master Bedroom +masterBedroom( + commands := NVL_Receiver.masterBedroom, + relay_feedback := EtherCAT_Outputs.masterBedroom_relay +); +NVL_Sender.l_masterBedroom := masterBedroom.status; +EtherCAT_Outputs.masterBedroom := masterBedroom.outputs; + +// ... (repeat for all rooms) + +// ===================================================== +// SECTION 2: WATER BOILER CONTROL +// ===================================================== + +waterBoiler( + ha_on := NVL_Receiver.boiler.ha_on, + ha_off := NVL_Receiver.boiler.ha_off, + schedule_on := NVL_Receiver.boiler.schedule_on, + schedule_off := NVL_Receiver.boiler.schedule_off, + emergency_stop := NVL_Receiver.boiler.emergency_stop + OR DI_Emergency_Stop, // Physical button OR remote + max_on_time := T#8H +); + +// Map outputs +EtherCAT_Outputs.boiler_relay := waterBoiler.relay_control; + +// Status to Node-RED +NVL_Sender.boiler_status.state := waterBoiler.state; +NVL_Sender.boiler_status.relay_output := waterBoiler.relay_control; +NVL_Sender.boiler_status.on_time_minutes := DINT_TO_INT(waterBoiler.on_time_seconds / 60); +NVL_Sender.boiler_status.remaining_minutes := DINT_TO_INT(waterBoiler.remaining_seconds / 60); +NVL_Sender.boiler_status.emergency_active := waterBoiler.emergency_active; +NVL_Sender.boiler_status.time_limit_reached := waterBoiler.time_limit_reached; +NVL_Sender.boiler_status.error_state := waterBoiler.error_state; +NVL_Sender.boiler_status.error_code := waterBoiler.error_code; +``` + +--- + +## Part 4: I/O Mapping + +### 4.1 EtherCAT Digital Outputs + +| Address | Function | Description | +|---------|----------|-------------| +| 16#1A00 - 16#1A0E | Lighting | Room light relays (existing) | +| 16#1A0F | Boiler | Water boiler relay (new) | + +### 4.2 EtherCAT Digital Inputs (if using physical emergency stop) + +| Address | Function | Description | +|---------|----------|-------------| +| 16#1900 - 16#190F | Switches | Room physical switches (existing) | +| TBD | Emergency | Boiler emergency stop button (optional) | + +--- + +## Part 5: Network Variables + +### 5.1 NVL_Sender (PLC → Node-RED) + +```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; + + // Boiler status (NEW) + boiler_status: struct_boiler_status; +END_VAR +``` + +### 5.2 NVL_Receiver (Node-RED → PLC) + +```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; + + // Boiler commands (NEW) + boiler: struct_boiler_commands; +END_VAR +``` + +--- + +## Part 6: Error Codes Reference + +### Boiler Error Codes + +| Code | Name | Description | Action | +|------|------|-------------|--------| +| 0 | No Error | Normal operation | None | +| 1 | Emergency Stop | Emergency stop activated | Release emergency stop, then send ON command | +| 2 | Time Limit | Maximum ON time exceeded | Send OFF command, then ON again to reset | + +--- + +## Part 7: Testing Checklist + +### Lighting Tests + +- [ ] HA ON command turns light ON +- [ ] HA OFF command turns light OFF +- [ ] Zigbee toggle switches light state +- [ ] All-off command turns all lights OFF +- [ ] All-on command turns all lights ON +- [ ] Relay status feedback is accurate +- [ ] Multiple commands in sequence work correctly + +### Boiler Tests + +- [ ] HA ON command starts boiler +- [ ] HA OFF command stops boiler +- [ ] Schedule ON command works +- [ ] Schedule OFF command works +- [ ] Emergency stop immediately stops boiler +- [ ] Emergency stop prevents ON commands +- [ ] Releasing emergency stop allows normal operation +- [ ] Time limit auto-shutoff works (test with short time) +- [ ] ON time counter is accurate +- [ ] Remaining time is accurate +- [ ] Error codes are set correctly +- [ ] Status feedback to HA is correct + +--- + +## Implementation Notes + +1. **Backward Compatibility**: The new lighting structure can coexist with existing code during migration. + +2. **Emergency Stop**: Can be either a physical button (EtherCAT input) or a virtual command from Home Assistant/Node-RED. + +3. **Time Limit Configuration**: Default is 8 hours but can be changed via `max_on_time` parameter or Node-RED configuration. + +4. **Task Configuration**: Run on EtherCAT_Task (4ms cycle) for responsive control. + +--- + +**Document Version**: 1.0 +**Created**: 2026-02-07 +**Status**: Design Complete - Ready for Implementation