- 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.
777 lines
27 KiB
Markdown
777 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
|
||
|
||
**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)
|
||
- **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 (per Redesign Recommendation)
|
||
|
||
#### Input: struct_switches (flat, for Node-RED/JSON compatibility)
|
||
|
||
```iec
|
||
TYPE struct_switches :
|
||
STRUCT
|
||
// 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 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
|
||
ha_all_on: BOOL; // HA: All lights ON
|
||
ha_all_off: BOOL; // HA: All lights OFF
|
||
END_STRUCT
|
||
END_TYPE
|
||
```
|
||
|
||
#### Output: struct_lights (flat, control + status feedback)
|
||
|
||
```iec
|
||
TYPE struct_lights :
|
||
STRUCT
|
||
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 (per Redesign)
|
||
|
||
Individual light control: HA ON/OFF + Zigbee toggle only. No all_on/all_off inside this FB.
|
||
|
||
```iec
|
||
FUNCTION_BLOCK fb_lightControl
|
||
VAR_INPUT
|
||
// Home Assistant Commands
|
||
ha_on: BOOL; // HA command: Turn ON
|
||
ha_off: BOOL; // HA command: Turn OFF
|
||
|
||
// 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
|
||
light_output: BOOL; // Control output to relay
|
||
light_status: BOOL; // Status to send back (actual relay state)
|
||
END_VAR
|
||
VAR
|
||
// Edge triggers
|
||
r_trig_ha_on: R_TRIG;
|
||
r_trig_ha_off: R_TRIG;
|
||
r_trig_zigbee: R_TRIG;
|
||
|
||
// Internal state
|
||
light_state: BOOL := FALSE;
|
||
END_VAR
|
||
```
|
||
|
||
#### Algorithm Logic (Priority: HA OFF > HA ON > Zigbee toggle)
|
||
|
||
```iec
|
||
// =====================================================
|
||
// fb_lightControl - Implementation (per redesign)
|
||
// =====================================================
|
||
// Priority (highest to lowest):
|
||
// 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 commands
|
||
r_trig_ha_on(CLK := ha_on);
|
||
r_trig_ha_off(CLK := ha_off);
|
||
r_trig_zigbee(CLK := zigbee_sw);
|
||
|
||
// State machine
|
||
IF r_trig_ha_off.Q THEN
|
||
light_state := FALSE;
|
||
ELSIF r_trig_ha_on.Q THEN
|
||
light_state := TRUE;
|
||
ELSIF r_trig_zigbee.Q THEN
|
||
light_state := NOT light_state;
|
||
END_IF;
|
||
|
||
// Output assignment
|
||
light_output := light_state;
|
||
|
||
// Status feedback (read from actual relay)
|
||
light_status := relay_status;
|
||
```
|
||
|
||
### 1.4 Function Block: fb_switch (per Redesign)
|
||
|
||
Room-level block: 6× fb_lightControl; global commands applied by overwriting outputs.
|
||
|
||
```iec
|
||
FUNCTION_BLOCK fb_switch
|
||
VAR_INPUT
|
||
switches: struct_switches;
|
||
relay_status: struct_lights; // Read from EtherCAT outputs (actual relay states)
|
||
END_VAR
|
||
VAR_OUTPUT
|
||
lights: struct_lights;
|
||
END_VAR
|
||
VAR
|
||
l1: fb_lightControl;
|
||
l2: fb_lightControl;
|
||
l3: fb_lightControl;
|
||
l4: fb_lightControl;
|
||
l5: fb_lightControl;
|
||
l6: fb_lightControl;
|
||
END_VAR
|
||
```
|
||
|
||
#### Algorithm Logic
|
||
|
||
```iec
|
||
// =====================================================
|
||
// fb_switch - Implementation (per redesign)
|
||
// =====================================================
|
||
|
||
// 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
|
||
);
|
||
lights.l_1 := l1.light_output;
|
||
lights.l_1_status := l1.light_status;
|
||
|
||
// 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
|
||
);
|
||
lights.l_2 := l2.light_output;
|
||
lights.l_2_status := l2.light_status;
|
||
|
||
// 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
|
||
);
|
||
lights.l_3 := l3.light_output;
|
||
lights.l_3_status := l3.light_status;
|
||
|
||
// 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
|
||
);
|
||
lights.l_4 := l4.light_output;
|
||
lights.l_4_status := l4.light_status;
|
||
|
||
// 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
|
||
);
|
||
lights.l_5 := l5.light_output;
|
||
lights.l_5_status := l5.light_status;
|
||
|
||
// 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
|
||
);
|
||
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;
|
||
```
|
||
|
||
---
|
||
|
||
## 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 (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
|
||
// =====================================================
|
||
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 (fb_switch: switches + relay_status in, lights out)
|
||
masterBedroom(
|
||
switches := NVL_Receiver.masterBedroom,
|
||
relay_status := EtherCAT_RelayFeedback.masterBedroom // Actual relay states from I/O
|
||
);
|
||
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
|
||
// =====================================================
|
||
|
||
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 (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;
|
||
END_VAR
|
||
```
|
||
|
||
### 5.2 NVL_Receiver (Node-RED → PLC)
|
||
|
||
```iec
|
||
VAR_GLOBAL
|
||
// 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;
|
||
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. **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.
|
||
|
||
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.1
|
||
**Created**: 2026-02-07
|
||
**Updated**: Aligned Part 1 (Lighting) with `docs/redesign/fb_switch-redesign-recommendation.md`.
|
||
**Status**: Design Complete - Ready for Implementation
|