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 <cursoragent@cursor.com>
This commit is contained in:
786
docs/codesys/plc-algorithm-design.md
Normal file
786
docs/codesys/plc-algorithm-design.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user