Problem Overview
A Siemens LOGO! 8.FS4 base module runs an application that contains a Yearly Timer function block. The user exposes the timer's parameters to a Modbus TCP master by mapping them into the controller's VM (Variable Memory) area. The first mapped parameter is ON YEAR, placed at VM address 0. The second mapped parameter is ON TIME, placed at VM address 2. Reads of ON YEAR succeed and return the correct year offset, but writes using Modbus function codes FC6 (Write Single Register) or FC16 (Write Multiple Registers) complete without an exception response yet the Yearly Timer's year value never updates. The companion ON TIME parameter, mapped to address 2, reads and writes correctly under the same master, which makes the failure look like a read-only access-rights issue. It is not: it is a byte-alignment trap where the year value is stored in the high byte of a 16-bit Modbus word and the master is only landing data in the low byte of that word.
The behavior is reproducible on multiple LOGO! 8 controllers in the same configuration. The fix is to either write a full 16-bit word to the Modbus register with the year value placed in the high byte, or to remap the ON YEAR parameter away from VM address 0 so that the byte that the Yearly Timer function block actually reads is the byte the master is targeting. Both fixes are field-verified and are documented below with PDU examples and Wireshark capture guidance.
Affected Hardware and Firmware
| Series | Firmware | Modbus role | Default port | Behavior with ON YEAR at VM 0 |
|---|---|---|---|---|
| LOGO! 8 (6ED1052-1... family) | FS4 | Modbus TCP server (slave) | 502/TCP | ON YEAR write fails silently when master targets the low byte of VW0 |
| LOGO! 8 with Ethernet (no display variants included) | FS4 | Modbus TCP server (slave) | 502/TCP | Same alignment behavior observed on equivalent VM mapping |
| LOGO! 8.3 family (later revisions) | FS4+ | Modbus TCP server (slave) | 502/TCP | Same alignment behavior expected when first parameter is a single byte |
LOGO! 8 base modules include an integrated Ethernet interface and a Modbus TCP server. The server listens on the standard 502/TCP port and exposes the VM area as Modbus Holding Registers starting at address 0. The user-defined VM mapping inside LOGO!Soft Comfort configures which function block parameters are addressable, their data types, and their byte offsets. The Yearly Timer function block on FS4 firmware uses a 16-bit word host for the ON YEAR byte, with the year value stored in the high byte of that word.
Modbus VM Address Model in LOGO! 8
The VM area in LOGO! 8 is byte-addressable. Each parameter in the VM mapping table consumes an integral number of bytes determined by its declared data type:
- Byte (1 byte) — Boolean flags, single-byte counters, the ON YEAR parameter (a two-digit year offset from 2000).
- Word (2 bytes) — 16-bit analog values, integer counters, threshold values.
- Double word (4 bytes) — 32-bit values, time-of-day in T# format (DWORD, milliseconds since 0:00:00.000).
Modbus itself is a 16-bit register protocol. The LOGO! Modbus server packs the byte-addressable VM into 16-bit Modbus Holding Registers beginning at register 0, which corresponds to the first two bytes of VM (V0/V1 = VW0 in LOGO!Soft Comfort terminology). The packing convention is little-endian: the low byte of a 16-bit Modbus register maps to the lower VM address, and the high byte maps to the next higher VM address.
| LOGO! VM byte | Modbus register | Word view | Bit position | Typical occupant |
|---|---|---|---|---|
| VB0 | Register 0, low byte | VW0 | Bits 0-7 | First mapped parameter |
| VB1 | Register 0, high byte | Bits 8-15 | ON YEAR (year offset) when ON YEAR is the first mapped parameter | |
| VB2 | Register 1, low byte | VW2 | Bits 0-7 | Second mapped parameter |
| VB3 | Register 1, high byte | Bits 8-15 |
The little-endian convention means that for any 16-bit word, the low byte of the Modbus register value is the byte at the lower VM address. If the Modbus master writes the 16-bit value 0x0026 to register 0, the slave stores 0x00 at VB0 and 0x26 at VB1.
Root Cause: Byte vs Word Alignment
The ON YEAR parameter of the Yearly Timer is encoded as a single byte holding the two-digit year offset from 2000. The valid range is 0..99, representing years 2000..2099. When ON YEAR is the first parameter in the VM mapping, it is stored at VB0. However, the LOGO! runtime reads the year from the high byte of the 16-bit word that contains VB0, i.e., from VB1. This is an internal alignment choice in the LOGO! 8.FS4 firmware and is consistent across the two controllers the user tested.
The Modbus protocol does not have a way to address a single byte. Every Modbus write targets a 16-bit register, even when the application parameter is a single byte. The slave receives a 16-bit value and is responsible for placing its two bytes into the VM area in the correct order. The LOGO! Modbus server places the first PDU data byte at the low byte of the register and the second PDU data byte at the high byte of the register. For an FC6 write of register 0 with the 16-bit value 0x0026, the wire bytes are 0x00 0x26, and the slave stores 0x00 at VB0 and 0x26 at VB1. That write updates the year correctly.
The failure mode the user observed occurs when the Modbus master writes only a single byte to the register, or when the master's byte order is reversed. Some byte-oriented masters that pre-date the Modbus register abstraction will write a single byte to a specified VM offset; when that offset is VB0, the year at VB1 is never touched. Other masters will write the same byte to both bytes of the 16-bit word, which yields 0x26 at both VB0 and VB1 but is a non-standard encoding of the value. The original poster's first attempt, writing to VB0 only, did not update the year. The poster's later attempt, writing the 16-bit word 0x0026 to VW0, did update the year.
Mathematically, the encoded 16-bit word for the year is:
Word = (year_offset << 8) | 0x00
where year_offset = target_year - 2000, 0 <= year_offset <= 99
For year 2038, year_offset = 38 = 0x26, and the word is (0x26 << 8) | 0x00 = 0x0000 | 0x2600 = 0x2600 in big-endian wire order, which becomes the little-endian PDU bytes 0x00 0x26 for a Modbus register value of 0x0026. This is the canonical encoding for ON YEAR on LOGO! 8.FS4.
Why the Master Receives No Exception Response
Modbus defines six standard exception codes: 01 (Illegal Function), 02 (Illegal Data Address), 03 (Illegal Data Value), 04 (Slave Device Failure), 05 (Acknowledge), and 06 (Slave Device Busy). None of these describe the case where a write is well-formed at the protocol level but is semantically misaligned with the application data model. From the slave's perspective, a write to register 0 with a valid 16-bit value is fully legal: the address exists, the value is in range, and the slave has accepted the write. The fact that the application-level Yearly Timer FB does not reflect the change is invisible at the Modbus layer.
This is why the original poster observed that both FC6 and FC16 return success but the year does not change. The Modbus transaction completed successfully; the semantic error lies one level up in the application data model. The poster correctly concluded that the issue is not with Modbus but with the parameter alignment.
Diagnostic Procedure
- Capture the Modbus TCP exchange between master and slave with Wireshark on port 502.
- Apply display filter
tcp.port == 502to isolate the LOGO! traffic from other Ethernet traffic. - For each write request, right-click and choose Follow > TCP Stream to see the full MBAP-headered conversation.
- Decode the Modbus PDU: confirm function code, starting address, quantity (for FC16), byte count, and data payload.
- Cross-reference the Modbus register address against the LOGO!Soft Comfort VM mapping table to determine which VM bytes each register covers.
- Identify whether the write is targeting a single byte (one of the two bytes of a 16-bit word) or a full 16-bit word.
- Write a known test pattern, such as
0x1234, to the suspect register and read it back. If the read returns0x3412, the master is byte-swapping; if it returns0x1234, the master is byte-correct. - Toggle a second parameter known to share the same word: a write to the companion byte of the same word should also fail to take effect on the Yearly Timer FB, confirming the alignment is the issue.
tcp port 502 in Wireshark to capture only the LOGO! Modbus traffic. In the per-packet detail pane, expand the Modbus layer to view the function code and data. The MBAP header is the 7-byte prefix before the function code: transaction ID, protocol ID (always 0x0000 for Modbus), length, and unit ID.Solution A: Word-Aligned Write to VW0
The clean fix is to write a 16-bit word to Modbus register 0 with the year value placed in the high byte and the low byte set to 0. With the year offset Y, the 16-bit word to transmit is (Y << 8) | 0x00. This ensures the byte that the Yearly Timer FB reads at VB1 is the year value.
FC6 (Write Single Register) request example
| Field | Value (hex) | Decoded meaning |
|---|---|---|
| Function code | 0x06 | Write Single Register |
| Register address Hi | 0x00 | Register 0 high byte of address |
| Register address Lo | 0x00 | Register 0 low byte of address |
| Register value Hi | 0x00 | High byte of the 16-bit value (sent first in PDU) |
| Register value Lo | 0x26 | Low byte of the 16-bit value (year offset 38) |
For the wire-level value, the Modbus PDU encodes the 16-bit register value as two bytes: high byte first, low byte second. The slave stores the high byte of the wire value at the low byte of the VM word (VB0) and the low byte of the wire value at the high byte of the VM word (VB1). For year 2038, the wire bytes 0x00 0x26 yield VB0 = 0x00 and VB1 = 0x26, which is what the Yearly Timer FB reads.
0x26 0x00, the slave will store VB0 = 0x26 and VB1 = 0x00, and the year will not update. Always verify byte order with a known test pattern before commissioning.FC16 (Write Multiple Registers) request example
To set both ON YEAR and ON TIME in a single atomic transaction, the master can write two consecutive registers starting at 0. The data field carries four bytes: the year word followed by the low word of the time DWORD.
FC16 PDU (write 2 registers starting at 0)
Function code : 0x10
Starting address : 0x0000 (Hi=0x00, Lo=0x00)
Quantity : 0x0002 (Hi=0x00, Lo=0x02)
Byte count : 0x04
Data : 0x00 0x26 0x00 0x00
^year word ^time word low half (DWORD, illustrative)
Always pad the year word to 16 bits with a 0x00 low byte and confirm the byte order expected by the master. The LOGO! Modbus server expects the standard Modbus big-endian wire order for 16-bit register values (high byte first, low byte second), with the low byte of the Modbus register value mapped to the lower VM address.
Solution B: Remap ON YEAR to a Word-Aligned Address
If the master only supports single-register writes and cannot guarantee a 16-bit word write, the supported workaround is to move ON YEAR to a VM address where it occupies a byte that the master reliably targets. In the LOGO!Soft Comfort VM mapping table, place a 16-bit dummy parameter at address 0 and move ON YEAR to address 2. With ON YEAR at address 2, the value will land in the high byte of VW2 (VB3) when the master writes a 16-bit word to register 1. The Yearly Timer FB on LOGO! 8.FS4 reads the year from the byte position that corresponds to the second VM entry, so the year is now written correctly by FC6 and FC16 single-register writes targeted at the appropriate register.
The original poster confirmed that placing ON YEAR at address 2 (i.e., the second VM entry) is a reliable workaround even when Solution A cannot be guaranteed by the master. This approach is also useful when the application already maps a 16-bit word at VM address 0 and the developer cannot change that mapping.
| Strategy | ON YEAR VM address | Modbus register | Write command | Robustness |
|---|---|---|---|---|
| Original (broken) | 0 (VB0) | 0 | FC6 to register 0 with high byte = year | Requires master to write full word |
| Remap (workaround) | 2 (VB2) | 1 | FC6 to register 1 with high byte = year | Tolerates master that only writes full word to a known register |
| Remap with leading dummy | 1 (VB1) | 0 | FC6 to register 0 with low byte = year | Requires prior verification of the internal read byte |
Verification
- After the write, issue an FC3 (Read Holding Registers) for register 0 (or the remapped register) and confirm the high byte now reflects the requested year offset.
- In LOGO!Soft Comfort, open Online View and inspect the Yearly Timer FB's ON YEAR parameter to confirm the value matches the write payload.
- Trigger the Yearly Timer by stepping the LOGO! internal clock to a date within the active window. Confirm the timer output goes high at the configured time.
- Capture the exchange in Wireshark and verify the PDU bytes match the desired 16-bit value, with no byte swapping.
- Repeat the test with a second Modbus master (e.g., a different SCADA or HMI) to confirm cross-vendor compatibility.
- Power-cycle the LOGO! base module and re-read the parameter to confirm whether the value is retained across reboot. LOGO! 8 VM parameters are not retained by default; enable the retentive flag on the Yearly Timer FB if persistence is required.
Modbus TCP Wire Format and MBAP Header
Modbus TCP wraps every PDU in a 7-byte MBAP (Modbus Application Protocol) header. The MBAP header is required for TCP transport and is not present in Modbus RTU. The fields are:
| Field | Size (bytes) | Endianness | Description |
|---|---|---|---|
| Transaction ID | 2 | Big-endian | Echoed by slave; used to match request and response |
| Protocol ID | 2 | Big-endian | Always 0x0000 for Modbus |
| Length | 2 | Big-endian | Bytes following this field, including unit ID and PDU |
| Unit ID | 1 | n/a | Slave address; LOGO! accepts 0xFF or 0x01 by default |
For a 6-byte FC6 PDU (function code + address + value), the length field is 0x0006. For an FC16 PDU with 4 data bytes, the length field is 7 + quantity*2 for the data plus overhead; specifically, length = 1 (unit ID) + 1 (function code) + 2 (starting address) + 2 (quantity) + 1 (byte count) + 4 (data) = 11 = 0x000B.
The 16-bit register values inside the PDU are also big-endian on the wire. The LOGO! Modbus server, like all compliant Modbus TCP slaves, converts the wire bytes to its internal little-endian VM representation. Master developers must therefore encode the year word as (year_offset << 8) | 0x00 in big-endian, which becomes 0x00 0x26 on the wire for year 2038.
Modbus Time Data Encoding Reference
Time-of-day values in Modbus-connected devices use several encodings. The LOGO! Yearly Timer ON TIME parameter is a T# value (TIME, IEC 61131-3) occupying 32 bits, expressed in milliseconds. Across vendors, three common encodings exist:
| Encoding | Register layout | Example: 1 hour | Vendor example |
|---|---|---|---|
| BCD HHMMSS | 3 registers, 6 nibbles | 0x01 0x00 0x00 | Schneider Electric, some legacy inverters |
| Seconds since midnight (32-bit) | 2 registers, DWORD | 0x00000E10 | Generic SCADA, some RTUs |
| Milliseconds (IEC TIME, 32-bit) | 2 registers, DWORD, little-endian | 0x0036EE80 | Siemens LOGO! T# format |
Eaton's How to Use Time Data Type in Modbus3.dll KB article documents the Modbus3Extra protocol extension used by Eaton PLCs, which adds string and time data types to standard Modbus RTU. The same byte-ordering and offset discipline applies: a 32-bit time value must be written as two consecutive 16-bit registers with the correct byte order, or the slave will accept the write but the application will read garbage from the high half of the wrong word.
For LOGO! 8, the T# value is a 32-bit signed integer in milliseconds, with the low word at the lower Modbus address. The wire encoding for a 1-day time value (86,400,000 ms = 0x05265B00) is the 16-bit word 0x0000 (low word) followed by 0x0526 (high word) when read in big-endian register order. The 32-bit value crosses two Modbus registers, so any FC6 write of a single register will corrupt the time value by writing only one of the two 16-bit halves. Use FC16 with quantity = 2 to update both halves atomically.
Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
| Write to ON YEAR at address 0 silently fails, no exception | Year value in VB1 of VW0; master writes VB0 only | Send 16-bit word to register 0 with year in high byte (Solution A) |
| Write succeeds, read returns correct value, but FB does not trigger | Word written to wrong VM entry due to remap shift | Re-verify VM mapping offsets in LOGO!Soft Comfort |
| FC16 with two registers writes only the first | Byte-count field or quantity field mismatch | Set byte count = 4, quantity = 2, data = 4 bytes |
| Read returns year value in low byte instead of high | Big-endian vs little-endian master | Swap data bytes in PDU before sending |
| Both ON YEAR and ON TIME write but timer never fires | Yearly Timer not enabled in FB or output not wired | Verify EN input, output connection, and time window in Online View |
| Intermittent writes succeed after power cycle | LOGO! VM not retained across reboot | Use retentive parameter flags in VM mapping or rebuild write on every scan |
| Wireshark shows correct PDU but FB value unchanged | Master is using a different VM mapping than the LOGO! project | Compare exported VM mapping text from LOGO!Soft Comfort with the master's register table |
| FC6 returns 0x02 (Illegal Data Address) | Register outside the LOGO! VM area | Confirm the highest mapped address; LOGO! typically supports up to 64 bytes |
Best Practices for Time-Type Modbus Writes
- Always write a full 16-bit word, never a single byte, when the parameter shares a word with another parameter.
- Use FC16 (Write Multiple Registers) for atomic updates of multi-register time values; never split a multi-word parameter across separate FC6 transactions.
- Document the Modbus register layout in a single table stored in the controller's project documentation, including the byte that each application parameter occupies within its host word.
- Verify the byte order on the master side with a known pattern: write
0x1234to a test word and read it back. If the read returns0x3412, swap the byte order in the master. - Where supported, enable Modbus transaction logging on the master to capture every PDU for post-mortem analysis.
- Reserve the first VM entries for 16-bit words; place single-byte parameters at even VM offsets so they always occupy the high byte of a known word and the year/T# semantics are predictable.
- Test the read-back path after every write to confirm the parameter round-tripped through the VM area correctly.
- When the master supports it, enable read-after-write to detect silent alignment failures immediately rather than at the next scheduled FB trigger.
Common Pitfalls and Edge Cases
A subtle edge case arises when the master library accepts a byte array as the data payload of an FC6 write and does not auto-pad to a full 16-bit register. Some libraries will accept a 1-byte data field and pad it to a 16-bit register by zero-extending on the right (so the master byte becomes the low byte of the register and the high byte is zero). Other libraries will pad on the left (so the master byte becomes the high byte). For ON YEAR on LOGO! 8.FS4, the master's padding direction must place the year in the high byte of the 16-bit register value. Test with a known year value and read back to confirm.
A third edge case is the use of a multi-master Modbus TCP connection. If two masters are writing to the same VM area concurrently, the second write will silently overwrite the first. For Yearly Timer parameters that change infrequently (typically once per year), this is not a concern, but for parameters updated on every scan, the application must serialize writes or accept last-writer-wins semantics.
Security and Access Considerations
LOGO! 8 base modules expose the Modbus TCP server on the integrated Ethernet port with no authentication by default. Any host on the same network segment can read and write the VM area. For industrial deployments, place the LOGO! on a segregated VLAN, restrict the Modbus server to a known set of master IP addresses via the LOGO! access control list (where supported), and consider enabling the LOGO! Web Server password for management access. The Modbus TCP server itself does not support authentication in the base protocol; security is provided by network segmentation and, where available, the LOGO! 8 firewall feature on the base module.
Related Cross-Vendor Notes
The byte-alignment pitfall described in this article is not unique to Siemens LOGO!. Other Modbus TCP servers that expose byte-addressable data through 16-bit registers face the same alignment issue. Unitronics UniStream and Vision controllers, for example, expose a similar MB (Modbus) and MI (Modbus Integer) area where the application must respect the byte order of the underlying data type. The same diagnostic procedure — write a known pattern, read back, compare with the expected byte position — applies across vendors.
For master developers, the most reliable cross-vendor approach is to treat every Modbus register write as a 16-bit operation and to document the byte that each application parameter occupies within its host word. The Yearly Timer alignment on LOGO! 8.FS4 is a useful case study: a single-byte parameter at the start of the VM area requires the master to write the high byte of a 16-bit word, which is the opposite of the typical big-endian convention many developers assume.
Is the LOGO! 8.FS4 Yearly Timer ON YEAR parameter truly writable via Modbus?
Yes. The parameter is declared read/write in the LOGO!Soft Comfort Online Help and accepts both FC6 and FC16 writes when the 16-bit word carrying the year is written correctly with the year in the high byte. The failure mode is byte alignment, not the parameter's access rights.
Why does writing to VB0 fail without an exception response?
The slave sees a valid write to a valid register with a valid 16-bit value and returns success. The misalignment between the master byte target and the Yearly Timer FB's source byte is an application-level error that the Modbus protocol does not detect.
Does LOGO! 8 use a +1 offset in its Modbus addressing?
Some firmware revisions apply a +1 offset to the Modbus address space. Always verify the offset empirically by reading a known word and comparing it with the LOGO!Soft Comfort VM mapping before relying on a static address in the master.
Can ON YEAR and ON TIME be written in a single FC16 request?
Yes, if they are placed in consecutive Modbus registers. Use FC16 with quantity = 2 (for year word plus time low word) and a single data field that combines the year word and the time DWORD low half in the correct byte order. For the full 32-bit T# value, quantity = 2 is sufficient because the time value crosses exactly two Modbus registers.
How is the year value encoded for transmission?
Encode the two-digit year offset from 2000 as a single byte in the high byte of VW0, with the low byte set to 0. For year 2038, transmit the 16-bit word 0x0026 to Modbus register 0; the wire bytes are 0x00 0x26, and the slave stores 0x00 in VB0 and 0x26 in VB1, which is what the Yearly Timer reads.