diff --git a/docs/codesys/plc-algorithm-design.md b/docs/codesys/plc-algorithm-design.md index 7f39b63..6f51689 100644 --- a/docs/codesys/plc-algorithm-design.md +++ b/docs/codesys/plc-algorithm-design.md @@ -50,9 +50,9 @@ This document defines the PLC algorithms for the home automation system: │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ EtherCAT I/O │ │ │ │ ┌──────────────┐ ┌──────────────────────────────┐ │ │ -│ │ │ EL1809 │ │ Output Module │ │ │ -│ │ │ 16x DI 24V │ │ 16x DO (Relays) │ │ │ -│ │ │ (Switches) │ │ (Lights + Boiler) │ │ │ +│ │ │ EL1809 │ │ EL2809 │ │ │ +│ │ │ 16x DI 24V │ │ 16x DO 24V (Relays) │ │ │ +│ │ │ (Switches) │ │ Lights + Boiler │ │ │ │ │ └──────────────┘ └──────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────┘ @@ -633,26 +633,42 @@ 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. := .lights` (see Section 3.2). + ### 3.2 Main Program Logic ```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 // ===================================================== -// Master Bedroom (fb_room: switches + relay_status in, lights out) +// Master Bedroom 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; 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 +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 @@ -686,12 +702,16 @@ NVL_Out.boiler_status.error_code := boiler.error_code; ## Part 4: I/O Mapping -### 4.1 EtherCAT Digital Outputs +### 4.1 EtherCAT Digital Outputs: EL2809 -| Address | Function | Description | -|---------|----------|-------------| -| 16#1A00 - 16#1A0E | Lighting | Room light relays (existing) | -| 16#1A0F | Boiler | Water boiler relay (new) | +**Module**: Beckhoff **EL2809** – 16-channel digital output, 24 V DC, 0.5 A per channel (overload/short-circuit protected). + +| Channel | Address (typical) | Function | Description | +|---------|--------------------|----------|-------------| +| 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) @@ -700,6 +720,191 @@ NVL_Out.boiler_status.error_code := boiler.error_code; | 16#1900 - 16#190F | Switches | Room physical switches (existing) | | 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.`. 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..l_X_status` (and boiler if applicable). | +| 5 | Pass `EtherCAT_RelayFeedback.` 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. := .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. := .lights`, so the next cycle uses this as relay feedback. Initialize `EtherCAT_RelayFeedback` to zero at startup. + --- ## Part 5: Network Variables