Update PLC algorithm documentation for EL2809 integration and relay feedback options
- Revised documentation to reflect the use of the EL2809 output module, including updated channel descriptions and process image details. - Introduced Option C for relay feedback, clarifying the method of using output copies for feedback instead of hardware read-back. - Enhanced comments and explanations in the main program logic to improve understanding of the relay feedback implementation across multiple rooms. This update improves the clarity and accuracy of the documentation for the home automation system's PLC algorithms.
This commit is contained in:
@@ -50,9 +50,9 @@ This document defines the PLC algorithms for the home automation system:
|
|||||||
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
│ ┌─────────────────────────────────────────────────────────────────┐ │
|
||||||
│ │ EtherCAT I/O │ │
|
│ │ EtherCAT I/O │ │
|
||||||
│ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │
|
│ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │
|
||||||
│ │ │ EL1809 │ │ Output Module │ │ │
|
│ │ │ EL1809 │ │ EL2809 │ │ │
|
||||||
│ │ │ 16x DI 24V │ │ 16x DO (Relays) │ │ │
|
│ │ │ 16x DI 24V │ │ 16x DO 24V (Relays) │ │ │
|
||||||
│ │ │ (Switches) │ │ (Lights + Boiler) │ │ │
|
│ │ │ (Switches) │ │ Lights + Boiler │ │ │
|
||||||
│ │ └──────────────┘ └──────────────────────────────┘ │ │
|
│ │ └──────────────┘ └──────────────────────────────┘ │ │
|
||||||
│ └─────────────────────────────────────────────────────────────────┘ │
|
│ └─────────────────────────────────────────────────────────────────┘ │
|
||||||
└─────────────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
@@ -633,26 +633,42 @@ VAR
|
|||||||
END_VAR
|
END_VAR
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Option C (relay feedback):** In a GVL, define `EtherCAT_RelayFeedback` (one `struct_room_outs` per room) and `EtherCAT_Outputs` (outputs to EL2809). Initialize `EtherCAT_RelayFeedback` to zero so the first cycle has defined behavior. Each cycle, after each `fb_room` call, copy `EtherCAT_RelayFeedback.<room> := <room>.lights` (see Section 3.2).
|
||||||
|
|
||||||
### 3.2 Main Program Logic
|
### 3.2 Main Program Logic
|
||||||
|
|
||||||
```iec
|
```iec
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// PLC_App - Main Program Implementation
|
// PLC_App - Main Program Implementation (Option C: copy of output as feedback)
|
||||||
// =====================================================
|
// =====================================================
|
||||||
|
// EtherCAT_RelayFeedback is filled from the OUTPUT of each room (no hardware read-back).
|
||||||
|
// Initialize EtherCAT_RelayFeedback to zero at startup so first cycle has defined behavior.
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// SECTION 1: LIGHTING CONTROL
|
// SECTION 1: LIGHTING CONTROL
|
||||||
// =====================================================
|
// =====================================================
|
||||||
|
|
||||||
// Master Bedroom (fb_room: switches + relay_status in, lights out)
|
// Master Bedroom
|
||||||
masterBedroom(
|
masterBedroom(
|
||||||
switches := NVL_In.masterBedroom,
|
switches := NVL_In.masterBedroom,
|
||||||
relay_status := EtherCAT_RelayFeedback.masterBedroom // Actual relay states from I/O
|
relay_status := EtherCAT_RelayFeedback.masterBedroom // Option C: previous cycle output
|
||||||
);
|
);
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom := masterBedroom.lights; // Copy output for next cycle
|
||||||
NVL_Out.l_masterBedroom := masterBedroom.lights;
|
NVL_Out.l_masterBedroom := masterBedroom.lights;
|
||||||
EtherCAT_Outputs.masterBedroom_l1 := masterBedroom.lights.l_1;
|
EtherCAT_Outputs.masterBedroom_l1 := masterBedroom.lights.l_1;
|
||||||
EtherCAT_Outputs.masterBedroom_l2 := masterBedroom.lights.l_2;
|
EtherCAT_Outputs.masterBedroom_l2 := masterBedroom.lights.l_2;
|
||||||
// ... l_3..l_6 and repeat for all rooms
|
EtherCAT_Outputs.masterBedroom_l3 := masterBedroom.lights.l_3;
|
||||||
|
EtherCAT_Outputs.masterBedroom_l4 := masterBedroom.lights.l_4;
|
||||||
|
EtherCAT_Outputs.masterBedroom_l5 := masterBedroom.lights.l_5;
|
||||||
|
EtherCAT_Outputs.masterBedroom_l6 := masterBedroom.lights.l_6;
|
||||||
|
|
||||||
|
// Repeat same pattern for all other rooms:
|
||||||
|
// masterBathroom( switches := NVL_In.masterBathroom, relay_status := EtherCAT_RelayFeedback.masterBathroom );
|
||||||
|
// EtherCAT_RelayFeedback.masterBathroom := masterBathroom.lights;
|
||||||
|
// NVL_Out.l_masterBathroom := masterBathroom.lights;
|
||||||
|
// EtherCAT_Outputs.masterBathroom_l1 := masterBathroom.lights.l_1; ... l_2..l_6
|
||||||
|
// ... bedroom_1, bedroom_2, bathroom, guest_wc, kitchen, pantry, livingRoom, diningRoom,
|
||||||
|
// entrance, hallway, veranda, front, back, side (each: call fb_room, copy to EtherCAT_RelayFeedback, NVL_Out, EtherCAT_Outputs)
|
||||||
|
|
||||||
// =====================================================
|
// =====================================================
|
||||||
// SECTION 2: WATER BOILER CONTROL
|
// SECTION 2: WATER BOILER CONTROL
|
||||||
@@ -686,12 +702,16 @@ NVL_Out.boiler_status.error_code := boiler.error_code;
|
|||||||
|
|
||||||
## Part 4: I/O Mapping
|
## Part 4: I/O Mapping
|
||||||
|
|
||||||
### 4.1 EtherCAT Digital Outputs
|
### 4.1 EtherCAT Digital Outputs: EL2809
|
||||||
|
|
||||||
| Address | Function | Description |
|
**Module**: Beckhoff **EL2809** – 16-channel digital output, 24 V DC, 0.5 A per channel (overload/short-circuit protected).
|
||||||
|---------|----------|-------------|
|
|
||||||
| 16#1A00 - 16#1A0E | Lighting | Room light relays (existing) |
|
| Channel | Address (typical) | Function | Description |
|
||||||
| 16#1A0F | Boiler | Water boiler relay (new) |
|
|---------|--------------------|----------|-------------|
|
||||||
|
| 1–15 | 16#1A00 … 16#1A0E | Lighting | Room light relays |
|
||||||
|
| 16 | 16#1A0F | Boiler | Water boiler relay |
|
||||||
|
|
||||||
|
The EL2809 has a **16-bit process image** (one word) for the output commands. In CODESYS, the EtherCAT device will expose an output variable (e.g. `EL2809_Outputs` or similar) – map your lighting and boiler control bits to the corresponding channels.
|
||||||
|
|
||||||
### 4.2 EtherCAT Digital Inputs (if using physical emergency stop)
|
### 4.2 EtherCAT Digital Inputs (if using physical emergency stop)
|
||||||
|
|
||||||
@@ -700,6 +720,191 @@ NVL_Out.boiler_status.error_code := boiler.error_code;
|
|||||||
| 16#1900 - 16#190F | Switches | Room physical switches (existing) |
|
| 16#1900 - 16#190F | Switches | Room physical switches (existing) |
|
||||||
| TBD | Emergency | Boiler emergency stop button (optional) |
|
| TBD | Emergency | Boiler emergency stop button (optional) |
|
||||||
|
|
||||||
|
### 4.3 How relay feedback is obtained (with EL2809)
|
||||||
|
|
||||||
|
**Chosen for this project: Option C** — relay “feedback” is a **copy of the output** we write to the EL2809. No hardware read-back; HA and the PLC stay in sync with the commanded state. Relay/wiring faults are not detected.
|
||||||
|
|
||||||
|
Relay feedback is the value passed into `fb_room` as `relay_status` and used for `l_X_status` in the status sent to Node-RED/HA. With Option C it is the same as the control output (from the previous cycle), so the UI reflects what we commanded.
|
||||||
|
|
||||||
|
**Your hardware**: **EL2809** (16-channel DO). The EL2809 drives the outputs from the process image; whether it exposes a **read-back** (TxPDO / “Input” or “Status”) depends on the EtherCAT PDO configuration. In CODESYS, after scanning the EL2809, check the device’s process image for an **input** or **status** word (data from device → PLC). **EL2809 has no read-back (confirmed):** process image = 16 output bits only, no input. Use **Option C** (copy of output) or **Option B** (auxiliary contacts). Which models *do* have input? See table below.
|
||||||
|
|
||||||
|
| Model | Channels | Process image | Notes |
|
||||||
|
|-------|----------|---------------|-------|
|
||||||
|
| **EL2032** | 2 DO | 2 outputs + 2 diagnostic inputs | 24 V DC, 2 A. |
|
||||||
|
| **EL2034** | 4 DO | 4 outputs + 4 diagnostic inputs | 24 V DC, 2 A; short-circuit, line-break. |
|
||||||
|
| **EL2042** | 8 DO | 8 outputs + diagnostic inputs | 24 V DC, 0.5 A; confirm in datasheet. |
|
||||||
|
| EL2002, EL2004, EL2008 | 2/4/8 DO | Output only | No input. |
|
||||||
|
| EL2084, EL2088 | 4/8 DO | Output only | No input. |
|
||||||
|
| **EL2809** | 16 DO | **Output only** | No input (your module). |
|
||||||
|
|
||||||
|
On EL2032/EL2034 the input bits are **diagnostic** (e.g. short-circuit, line-break), not necessarily "output on" state. For true output-state read-back without wiring use Option B (auxiliary contacts) or Option C (copy of output).
|
||||||
|
|
||||||
|
There are **three practical ways** to get relay feedback:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Option A: Output read-back (only for DO modules that have Input/Status)
|
||||||
|
|
||||||
|
If the EL2809’s EtherCAT configuration in CODESYS exposes an **input** (TxPDO) or **status** word from the device, that is the read-back of the output state. Use the steps and code below. **Note:** The EL2809 datasheet specifies only “16 output bits” in the process image; not all DO modules provide a separate input/read-back. In CODESYS, verify whether an **Input** (or **Status**) process image exists for the EL2809; if it does not, use Option C instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**1. EtherCAT process image (reminder)**
|
||||||
|
|
||||||
|
- **Output process image**: PLC → device. You write here to drive the EL2809 channels (your light/boiler commands).
|
||||||
|
- **Input process image**: Device → PLC. If the EL2809 supports read-back, it will send data here (e.g. actual output state or status). This is the TxPDO from the slave.
|
||||||
|
|
||||||
|
Option A uses this **input** data as relay feedback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**2. Check whether the EL2809 has read-back in CODESYS**
|
||||||
|
|
||||||
|
1. Open your CODESYS project and the **Device Tree** (often under **Application** or **EtherCAT Master**).
|
||||||
|
2. Expand the EtherCAT master and locate the **EL2809** device.
|
||||||
|
3. Open the EL2809 and look at its **I/O Mapping** or **Process Image** (name depends on CODESYS version).
|
||||||
|
4. You should see at least:
|
||||||
|
- **Output**: one 16-bit word (or 16 BOOLs) – this is what you write to drive the relays.
|
||||||
|
5. Check if there is also:
|
||||||
|
- **Input**: one 16-bit word (or 16 BOOLs) – data *from* the EL2809.
|
||||||
|
If **Input** (or **Status** / **Actual value**) is present, the module supports read-back; use Option A. If only **Output** exists, use Option C.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**3. Where the variable appears**
|
||||||
|
|
||||||
|
After scanning the bus (or adding the device), CODESYS usually creates symbols for the process image, for example:
|
||||||
|
|
||||||
|
- Under a **GVL** (Global Variable List) linked to the EtherCAT device, or
|
||||||
|
- Under **I/O Mapping** as a linked variable (e.g. `EtherCAT_Master.EL2809.Input` or `EL2809_Input`).
|
||||||
|
|
||||||
|
The exact path depends on your CODESYS version and how the EtherCAT device was added. Typical patterns:
|
||||||
|
|
||||||
|
- `GVL.EtherCAT.EL2809_Input` (WORD or 16 BOOLs)
|
||||||
|
- `EtherCAT_Master.EL2809.Inputs` or `.Input`
|
||||||
|
- Or 16 separate bits: `EL2809_Input_0` … `EL2809_Input_15`
|
||||||
|
|
||||||
|
If no Input symbol exists, create a **linked variable**: add a new variable in a GVL, set its type to WORD (or ARRAY[0..15] OF BOOL), and link it to the EL2809’s **Input** process image byte/word in the I/O mapping dialog.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**4. Channel-to-light mapping**
|
||||||
|
|
||||||
|
You must map each EL2809 channel to your feedback structure. Example mapping (adjust to your wiring):
|
||||||
|
|
||||||
|
| EL2809 channel | Use | Feedback target |
|
||||||
|
|----------------|-----|------------------|
|
||||||
|
| 0 (bit 0) | Light | e.g. masterBedroom l_1 |
|
||||||
|
| 1 | Light | masterBedroom l_2 |
|
||||||
|
| … | … | … |
|
||||||
|
| 14 | Light | last room / l_6 |
|
||||||
|
| 15 | Boiler | boiler relay |
|
||||||
|
|
||||||
|
Define your own table: which physical channel drives which room’s which light (and which channel is the boiler). Then map the **Input** bits to `EtherCAT_RelayFeedback` accordingly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**5. Example code: fill `EtherCAT_RelayFeedback` from EL2809 Input**
|
||||||
|
|
||||||
|
Assume the EL2809’s read-back is available as a **WORD** in a GVL named `GVL_IO`, with the symbol `EL2809_Input` (or as 16 BOOLs `EL2809_Input_0` … `EL2809_Input_15`). Adjust names to match your project.
|
||||||
|
|
||||||
|
**If you have a WORD (e.g. `GVL_IO.EL2809_Input`):**
|
||||||
|
|
||||||
|
```iec
|
||||||
|
// Map EL2809 read-back (WORD) to relay feedback. Adjust bit order and room/channel
|
||||||
|
// mapping to match your wiring. Example: Ch0..Ch5 = masterBedroom l_1..l_6.
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_1_status := GVL_IO.EL2809_Input.%X0; // Ch0
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_2_status := GVL_IO.EL2809_Input.%X1;
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_3_status := GVL_IO.EL2809_Input.%X2;
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_4_status := GVL_IO.EL2809_Input.%X3;
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_5_status := GVL_IO.EL2809_Input.%X4;
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_6_status := GVL_IO.EL2809_Input.%X5;
|
||||||
|
EtherCAT_RelayFeedback.masterBathroom.l_1_status := GVL_IO.EL2809_Input.%X6;
|
||||||
|
// ... continue for all 15 lights (channels 0..14). Channel 15 = boiler relay;
|
||||||
|
// if you want boiler relay read-back, assign to your boiler status struct instead.
|
||||||
|
```
|
||||||
|
|
||||||
|
**If you have 16 BOOLs (e.g. `GVL_IO.EL2809_Ch0` … `GVL_IO.EL2809_Ch15`):**
|
||||||
|
|
||||||
|
```iec
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_1_status := GVL_IO.EL2809_Ch0;
|
||||||
|
EtherCAT_RelayFeedback.masterBedroom.l_2_status := GVL_IO.EL2809_Ch1;
|
||||||
|
// ... same mapping as above, using EL2809_Ch2 .. EL2809_Ch15
|
||||||
|
```
|
||||||
|
|
||||||
|
**Where to call this:** Execute this mapping in the **same task** that runs `PLC_App`, and **before** you call `fb_room` instances (so `EtherCAT_RelayFeedback` is up to date when passed as `relay_status`). Typically this is at the top of `PLC_App` or in a dedicated “I/O update” section.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**6. Ensure `EtherCAT_RelayFeedback` is defined**
|
||||||
|
|
||||||
|
You need a global (or program-level) variable that holds the feedback for all rooms and the boiler, e.g.:
|
||||||
|
|
||||||
|
```iec
|
||||||
|
VAR_GLOBAL
|
||||||
|
EtherCAT_RelayFeedback: STRUCT
|
||||||
|
masterBedroom: struct_room_outs;
|
||||||
|
masterBathroom: struct_room_outs;
|
||||||
|
// ... all rooms ...
|
||||||
|
// If boiler feedback is separate: boiler_relay_status: BOOL;
|
||||||
|
END_STRUCT;
|
||||||
|
END_VAR
|
||||||
|
```
|
||||||
|
|
||||||
|
Each `fb_room` is then called with `relay_status := EtherCAT_RelayFeedback.<room>`. The boiler’s `relay_output` in `struct_boiler_status` can be set from the same EL2809 input bit (channel 15) if you use it for status.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**7. Summary Option A**
|
||||||
|
|
||||||
|
| Step | Action |
|
||||||
|
|------|--------|
|
||||||
|
| 1 | In Device Tree → EL2809, check for **Input** (or Status) in process image. |
|
||||||
|
| 2 | If present, note the symbol (e.g. `GVL_IO.EL2809_Input` or 16 BOOLs). |
|
||||||
|
| 3 | Define your channel → room/light mapping table. |
|
||||||
|
| 4 | In `PLC_App` (before calling `fb_room`), assign each Input bit to the corresponding `EtherCAT_RelayFeedback.<room>.l_X_status` (and boiler if applicable). |
|
||||||
|
| 5 | Pass `EtherCAT_RelayFeedback.<room>` as `relay_status` into each `fb_room`. |
|
||||||
|
|
||||||
|
So: **relay feedback = value read from the EL2809’s Input process image**, when the device provides it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Option B: Auxiliary contacts on the relays (hardware feedback)
|
||||||
|
|
||||||
|
The relay has (or you add) an **auxiliary (feedback) contact** that closes when the relay is energized. That contact is wired to a **digital input** on the EtherCAT bus (e.g. spare channels on the EL1809 or an extra DI module).
|
||||||
|
|
||||||
|
- **Wiring**: Relay coil ← DO channel; relay auxiliary contact → DI channel.
|
||||||
|
- **In the program**: The DI channel *is* the relay feedback.
|
||||||
|
e.g. `EtherCAT_RelayFeedback.masterBedroom.l_1_status := GVL.EtherCAT_Master.DI_Module.Ch5;`
|
||||||
|
(where Ch5 is the DI connected to relay 1’s auxiliary contact.)
|
||||||
|
|
||||||
|
So: **relay feedback = state of the DI that is wired to the relay’s auxiliary contact.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Option C: No hardware feedback – use command as “feedback” (EL2809 without read-back)
|
||||||
|
|
||||||
|
If the EL2809 does **not** expose an input/read-back in the process image and you do **not** use auxiliary contacts (Option B), use the **output command as the “feedback”**:
|
||||||
|
|
||||||
|
- **In the program**: Build `EtherCAT_RelayFeedback` from the **same variables** you write to the EL2809 outputs. For example, after you assign `EtherCAT_Outputs.masterBedroom_l1 := masterBedroom.lights.l_1;`, also set
|
||||||
|
`EtherCAT_RelayFeedback.masterBedroom.l_1_status := masterBedroom.lights.l_1;`
|
||||||
|
(or copy from a single source of truth for each channel).
|
||||||
|
- Then `l_X_status` in `struct_room_outs` reflects “what we commanded,” not “what the relay actually did.” HA and the PLC stay in sync, but **relay or wiring faults are not detected**.
|
||||||
|
|
||||||
|
**Implementation:** Section 3.2 (PLC_App): after each `fb_room` call, set `EtherCAT_RelayFeedback.<room> := <room>.lights`; initialize `EtherCAT_RelayFeedback` to zero at startup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Summary (with EL2809)
|
||||||
|
|
||||||
|
| Option | What you need | True feedback? | This project |
|
||||||
|
|--------|----------------|----------------|--------------|
|
||||||
|
| **A** | DO input/read-back in process image | Yes | Not used (EL2809 has no input). |
|
||||||
|
| **B** | Relay auxiliary contacts + DI | Yes | Not used. |
|
||||||
|
| **C** | Copy of output variables | No | **Used.** See Section 3.2. |
|
||||||
|
|
||||||
|
**Option C (chosen):** `fb_room` gets `relay_status` from `EtherCAT_RelayFeedback`. Each cycle, after calling `fb_room`, we set `EtherCAT_RelayFeedback.<room> := <room>.lights`, so the next cycle uses this as relay feedback. Initialize `EtherCAT_RelayFeedback` to zero at startup.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Part 5: Network Variables
|
## Part 5: Network Variables
|
||||||
|
|||||||
Reference in New Issue
Block a user