787 lines
27 KiB
Markdown
787 lines
27 KiB
Markdown
# 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
|