diff --git a/docs/drivers/S7.md b/docs/drivers/S7.md
index c10a27bf..492f2faa 100644
--- a/docs/drivers/S7.md
+++ b/docs/drivers/S7.md
@@ -68,28 +68,93 @@ Addresses use Siemens TIA-Portal / STEP 7 Classic syntax, parsed by
| Area | Example | Meaning |
|------|---------|---------|
-| Data block | `DB1.DBX0.0` / `DB1.DBW0` / `DB1.DBD4` | DB number + size suffix `X`(bit) / `B`(byte) / `W`(word) / `D`(dword), optional `.bit` for `DBX` |
+| Data block | `DB1.DBX0.0` / `DB1.DBW0` / `DB1.DBD4` / `DB1.DBB8` | DB number + size suffix `X`(bit) / `B`(byte) / `W`(word) / `D`(dword), optional `.bit` for `DBX` |
| Merker (M) | `MB0` / `MW0` / `MD4` / `M0.0` | Marker byte; size prefix `B`/`W`/`D`, or bare offset `.bit` for bit access |
| Input (I) | `IB0` / `IW0` / `I0.0` | Process-image input |
| Output (Q) | `QB0` / `QW0` / `Q0.0` | Process-image output |
+| Timer | `T0` / `T15` | Timer area (read-only — see Wide types section) |
+| Counter | `C0` / `C10` | Counter area (read-only — see Wide types section) |
Parsing is strict and runs once at `InitializeAsync` so a config typo fails fast
at load instead of surfacing as `BadInternalError` on every read. Bit offsets
must be 0-7, byte offsets non-negative, DB numbers >= 1.
-> **Timer (`T{n}`) and Counter (`C{n}`)** addresses parse cleanly but the read
-> path has no decode case for them yet — the driver rejects them at init with an
-> explicit error rather than letting them surface a misleading type-mismatch.
-
## Data types
`S7DataType` declares the **semantic** type; `S7.Net` returns an unsigned boxed
value (bool / byte / ushort / uint) that the driver reinterprets without an
-extra PLC round-trip. Wired through today: `Bool`, `Byte`, `Int16`, `UInt16`,
-`Int32`, `UInt32`, `Float32`. `Int64`, `UInt64`, `Float64`, `String`, and
-`DateTime` are declared in the enum but **rejected at init** — half-implemented
-types must not create OPC UA nodes that then return `BadNotSupported` on every
-access.
+extra PLC round-trip.
+
+| DataType | S7 Type | Width | Read | Write |
+|----------|---------|-------|------|-------|
+| `Bool` | BOOL | 1 bit | yes | yes |
+| `Byte` | BYTE | 1 byte | yes | yes |
+| `Int16` | INT | 2 bytes | yes | yes |
+| `UInt16` | WORD | 2 bytes | yes | yes |
+| `Int32` | DINT | 4 bytes | yes | yes |
+| `UInt32` | DWORD | 4 bytes | yes | yes |
+| `Float32` | REAL | 4 bytes | yes | yes |
+| `Int64` | LINT | 8 bytes | yes | yes |
+| `UInt64` | ULINT/LWORD | 8 bytes | yes | yes |
+| `Float64` | LREAL | 8 bytes | yes | yes |
+| `String` | STRING | `StringLength + 2` bytes | yes | yes |
+| `DateTime` | DATE_AND_TIME | 8 bytes | yes | yes |
+| Timer (`T{n}`) | TIME | — | yes | **read-only** |
+| Counter (`C{n}`) | COUNTER | — | yes | **read-only** |
+
+Wide types (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`) and Timer/Counter require
+byte-anchored addressing — see the section below.
+
+## Wide types & Timer/Counter
+
+### Byte-anchored addressing for wide / structured types
+
+Wide types (`Int64`, `UInt64`, `Float64`/LReal, `String`, `DateTime`) are
+**byte-anchored**: the address must use the `B` suffix pointing at the **first
+byte** of the value; the driver reads the correct number of contiguous bytes
+based on the DataType.
+
+| DataType | Address form | Bytes read | Example |
+|----------|-------------|------------|---------|
+| `Int64` | `DB{n}.DBB{offset}` / `MB{offset}` / `IB{offset}` / `QB{offset}` | 8 | `DB1.DBB8` → LINT at bytes 8–15 |
+| `UInt64` | same | 8 | `DB2.DBB16` → ULINT at bytes 16–23 |
+| `Float64` | same | 8 | `DB1.DBB8` → LREAL at bytes 8–15 |
+| `DateTime` | same | 8 | `DB3.DBB0` → DATE_AND_TIME at bytes 0–7 |
+| `String` | same | `StringLength + 2` | `DB4.DBB0`, StringLength=40 → 42 bytes |
+
+Using a `W` or `D` suffix with a wide DataType is a config error caught at
+`InitializeAsync`. For array tags (`isArray: true`), wide-type element arrays
+are deferred — see Deferrals below.
+
+### Timer read
+
+**Address**: `T{n}` (e.g. `T0`, `T15`). **DataType must be `Float64`**. The OPC UA node
+is a scalar `Float64` whose value is the timer's elapsed time in **seconds** (as a double).
+Timers are **read-only** this phase; a write attempt returns `BadNotWritable`.
+
+### Counter read
+
+**Address**: `C{n}` (e.g. `C0`, `C10`). **DataType must be `Int32`**. The OPC UA node is
+a scalar `Int32` whose value is the current **count** (raw word from the PLC).
+Counters are **read-only** this phase; a write attempt returns `BadNotWritable`.
+
+> **Known limitation — Counter BCD encoding on S7-300/400**: `S7.Net`'s
+> `Counter.FromByteArray` returns the raw big-endian word without BCD decode.
+> On **classic S7-300/400** the C-area word is BCD-encoded (0–999), so the
+> surfaced value can differ from the actual count on that hardware. S7-1200/1500
+> use IEC/DB counters (plain integers) where the raw word is correct. BCD
+> reinterpretation for legacy C-area is a live-hardware-gated follow-up.
+
+### Deferrals (not yet implemented)
+
+The following are explicitly deferred and will produce a config error or
+`BadNotSupported` if attempted:
+
+- **Wide-type arrays** — array tags (`isArray: true`) with element types `Int64`,
+ `UInt64`, `Float64`, `String`, or `DateTime`.
+- **`S7WString`** (2-byte character strings; distinct from classic `STRING`).
+- **`DTL` / `DateTimeLong`** (12-byte Siemens date-and-time-long).
+- **Timer / Counter writes** — surfaced as `BadNotWritable` until write support lands.
## Capability surface
@@ -152,8 +217,9 @@ loops over the response buffer decoding each element individually using the same
reinterpret/box logic as scalar reads. This keeps wire round-trips at 1 per array tag
regardless of N.
-**Supported element types** — any `S7DataType` that has a scalar decode path today
-(`Bool`, `Byte`, `Int16`, `UInt16`, `Int32`, `UInt32`, `Float32`). The `ReadBytesAsync`
+**Supported element types** — the narrow scalar types (`Bool`, `Byte`, `Int16`, `UInt16`,
+`Int32`, `UInt32`, `Float32`). Wide types (`Int64`, `UInt64`, `Float64`, `String`,
+`DateTime`) and Timer/Counter are deferred for array contexts. The `ReadBytesAsync`
network half is a thin `S7.Net` call; the decode half is fully unit-tested.
**Unit test coverage** — `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/` covers
@@ -162,9 +228,9 @@ normal dev (the S7 sim is on the shared Docker host but currently offline).
**Live-verify** — integration-fixture-gated (not Mac-verifiable without the S7 sim up).
-**Deferrals** — array *writes*, multi-dimensional arrays, per-element historization. The
-`Timer` / `Counter` address types that are rejected at init for scalars are also excluded
-for arrays.
+**Deferrals** — array *writes*, multi-dimensional arrays, per-element historization,
+wide-type array elements (`Int64`, `UInt64`, `Float64`, `String`, `DateTime`), and
+Timer/Counter array elements.
See [Uns.md §Array tags](../Uns.md#array-tags-1-d) for the cross-driver coverage matrix
and the UI authoring flow.
diff --git a/docs/v2/driver-specs.md b/docs/v2/driver-specs.md
index b7cb1325..4365114e 100644
--- a/docs/v2/driver-specs.md
+++ b/docs/v2/driver-specs.md
@@ -395,13 +395,73 @@ Pure managed .NET, MIT license. No native dependencies.
| Area | Address Syntax | Area Code | Examples |
|------|---------------|-----------|----------|
-| Data Block | `DB{n}.DB{X\|B\|W\|D}{offset}[.bit]` | 0x84 | `DB1.DBX0.0`, `DB1.DBW0`, `DB1.DBD4` |
-| Merkers | `M{B\|W\|D}{offset}` or `M{offset}.{bit}` | 0x83 | `M0.0`, `MW0`, `MD4` |
-| Inputs | `I{B\|W\|D}{offset}` or `I{offset}.{bit}` | 0x81 | `I0.0`, `IW0`, `ID0` |
-| Outputs | `Q{B\|W\|D}{offset}` or `Q{offset}.{bit}` | 0x82 | `Q0.0`, `QW0`, `QD0` |
+| Data Block | `DB{n}.DB{X\|B\|W\|D}{offset}[.bit]` | 0x84 | `DB1.DBX0.0`, `DB1.DBW0`, `DB1.DBD4`, `DB1.DBB8` |
+| Merkers | `M{B\|W\|D}{offset}` or `M{offset}.{bit}` | 0x83 | `M0.0`, `MW0`, `MD4`, `MB4` |
+| Inputs | `I{B\|W\|D}{offset}` or `I{offset}.{bit}` | 0x81 | `I0.0`, `IW0`, `ID0`, `IB0` |
+| Outputs | `Q{B\|W\|D}{offset}` or `Q{offset}.{bit}` | 0x82 | `Q0.0`, `QW0`, `QD0`, `QB0` |
| Timers | `T{n}` | 0x1D | `T0`, `T15` |
| Counters | `C{n}` | 0x1C | `C0`, `C10` |
+### Supported Data Types
+
+| DataType | S7 Type | Width | Read | Write | Notes |
+|----------|---------|-------|------|-------|-------|
+| `Bool` | BOOL | 1 bit | yes | yes | |
+| `Byte` | BYTE | 1 byte | yes | yes | |
+| `Int16` | INT | 2 bytes | yes | yes | |
+| `UInt16` | WORD | 2 bytes | yes | yes | |
+| `Int32` | DINT | 4 bytes | yes | yes | |
+| `UInt32` | DWORD | 4 bytes | yes | yes | |
+| `Float32` | REAL | 4 bytes | yes | yes | |
+| `Int64` | LINT | 8 bytes | yes | yes | Byte-anchored — see below |
+| `UInt64` | ULINT/LWORD | 8 bytes | yes | yes | Byte-anchored — see below |
+| `Float64` | LREAL | 8 bytes | yes | yes | Byte-anchored — see below |
+| `String` | STRING | `StringLength + 2` bytes | yes | yes | Byte-anchored; default `StringLength`=254 |
+| `DateTime` | DATE_AND_TIME | 8 bytes | yes | yes | Byte-anchored — see below |
+| Timer | TIME | — | yes | **no** | `T{n}` address only; DataType must be `Float64`; value = seconds |
+| Counter | COUNTER | — | yes | **no** | `C{n}` address only; DataType must be `Int32`; value = raw word |
+
+### Wide / Structured Types — Byte-Anchored Addressing
+
+Wide types (`Int64`, `UInt64`, `Float64`/LReal, `String`, `DateTime`) are **byte-anchored**: the
+address must use the `B` suffix to specify the start byte; the driver reads the
+correct number of bytes based on the DataType.
+
+| DataType | Required address form | Example |
+|----------|-----------------------|---------|
+| `Int64` | `DB{n}.DBB{offset}` / `MB{offset}` / `IB{offset}` / `QB{offset}` | `DB1.DBB8` → LINT at bytes 8–15 |
+| `UInt64` | same | `DB2.DBB16` → ULINT at bytes 16–23 |
+| `Float64` | same | `DB1.DBB8` → LREAL at bytes 8–15 |
+| `DateTime` | same | `DB3.DBB0` → DATE_AND_TIME at bytes 0–7 |
+| `String` | same | `DB4.DBB0` DataType=String, StringLength=40 → reads 42 bytes |
+
+Using a `W` (word) or `D` (dword) suffix with a wide DataType produces an address parse
+error at `InitializeAsync`.
+
+### Timer / Counter Read
+
+**Timer (`T{n}`)** — reads the timer's current value as elapsed **seconds** (double). The OPC UA
+node DataType is `Float64`. Timers are **read-only** this phase; writes return `BadNotWritable`.
+
+**Counter (`C{n}`)** — reads the counter current value as a raw count (`int`). The OPC UA node
+DataType is `Int32`. Counters are **read-only** this phase; writes return `BadNotWritable`.
+
+> **Counter known-limitation**: `S7.Net`'s `Counter.FromByteArray` returns the raw big-endian
+> word without BCD decode. On **classic S7-300/400** the C-area word is BCD-encoded (0–999),
+> so the surfaced value can differ from the actual count on that hardware. S7-1200/1500 use
+> IEC/DB counters (plain integers) where the raw word is correct. BCD reinterpretation for
+> legacy C-area is a live-hardware-gated follow-up.
+
+### Deferrals (not yet implemented)
+
+The following are explicitly deferred and will return `BadNotSupported` or a config error if attempted:
+
+- **Wide-type arrays** — array tags (`isArray: true`) with element types `Int64`, `UInt64`,
+ `Float64`, `String`, or `DateTime`.
+- **`S7WString`** (2-byte characters; distinct from `STRING`).
+- **`DTL` / `DateTimeLong`** (12-byte Siemens date-and-time-long).
+- **Timer / Counter writes** — surfaced as `BadNotWritable` until full write support lands.
+
### PDU Size & Optimization
| PLC | PDU Size | Max Data per Read |
diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs
index 2cda2f1e..57a26c72 100644
--- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs
+++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/ReadCommand.cs
@@ -20,13 +20,13 @@ public sealed class ReadCommand : S7CommandBase
IsRequired = true)]
public string Address { get; init; } = default!;
- // Driver.S7.Cli-002: help text trimmed to the types the driver actually implements.
- // Int64 / UInt64 / Float64 / String / DateTime are defined in S7DataType but the driver
- // raises NotSupportedException (→ BadNotSupported) on reads of those types.
+ // Driver.S7.Cli-002: help text reflects the types the driver currently implements.
/// Gets the data type to interpret the address as.
[CommandOption("type", 't', Description =
- "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 (default Int16). " +
- "Int64, UInt64, Float64, String, and DateTime are not yet implemented and will return BadNotSupported.")]
+ "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 / Int64 / UInt64 / Float64 / String / DateTime (default Int16). " +
+ "Wide types (Int64, UInt64, Float64/LReal, String, DateTime) are byte-anchored: address must use the B suffix " +
+ "(e.g. DB1.DBB8, MB4) pointing at the first byte; width comes from the DataType. " +
+ "Timer (T{n}) reads elapsed seconds as Float64 (read-only). Counter (C{n}) reads the count as Int32 (read-only).")]
public S7DataType DataType { get; init; } = S7DataType.Int16;
/// Gets the maximum string length for string-type reads.
diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/SubscribeCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/SubscribeCommand.cs
index 24b7e9eb..dbbf68a3 100644
--- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/SubscribeCommand.cs
+++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/SubscribeCommand.cs
@@ -17,10 +17,12 @@ public sealed class SubscribeCommand : S7CommandBase
public string Address { get; init; } = default!;
/// Gets the data type of the address.
- // Driver.S7.Cli-002: help text trimmed to the types the driver actually implements.
+ // Driver.S7.Cli-002: help text reflects the types the driver currently implements.
[CommandOption("type", 't', Description =
- "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 (default Int16). " +
- "Int64, UInt64, Float64, String, and DateTime are not yet implemented and will return BadNotSupported.")]
+ "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 / Int64 / UInt64 / Float64 / String / DateTime (default Int16). " +
+ "Wide types (Int64, UInt64, Float64/LReal, String, DateTime) are byte-anchored: address must use the B suffix " +
+ "(e.g. DB1.DBB8, MB4) pointing at the first byte; width comes from the DataType. " +
+ "Timer (T{n}) reads elapsed seconds as Float64 (read-only). Counter (C{n}) reads the count as Int32 (read-only).")]
public S7DataType DataType { get; init; } = S7DataType.Int16;
/// Gets the polling interval in milliseconds.
diff --git a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/WriteCommand.cs b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/WriteCommand.cs
index 2ccb1d10..90287597 100644
--- a/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/WriteCommand.cs
+++ b/src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli/Commands/WriteCommand.cs
@@ -20,13 +20,12 @@ public sealed class WriteCommand : S7CommandBase
public string Address { get; init; } = default!;
/// Gets or sets the data type of the value to write.
- // Driver.S7.Cli-002: help text trimmed to the types the driver actually implements.
- // Int64 / UInt64 / Float64 / String / DateTime are defined in S7DataType but the driver
- // raises NotSupportedException (→ BadNotSupported) on any read/write of those types;
- // advertising them misleads operators who then see BadNotSupported with no explanation.
+ // Driver.S7.Cli-002: help text reflects the types the driver currently implements.
[CommandOption("type", 't', Description =
- "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 (default Int16). " +
- "Int64, UInt64, Float64, String, and DateTime are not yet implemented and will return BadNotSupported.")]
+ "Bool / Byte / Int16 / UInt16 / Int32 / UInt32 / Float32 / Int64 / UInt64 / Float64 / String / DateTime (default Int16). " +
+ "Wide types (Int64, UInt64, Float64/LReal, String, DateTime) are byte-anchored: address must use the B suffix " +
+ "(e.g. DB1.DBB8, MB4) pointing at the first byte; width comes from the DataType. " +
+ "Timer (T{n}) and Counter (C{n}) are read-only — writes to them return BadNotWritable.")]
public S7DataType DataType { get; init; } = S7DataType.Int16;
/// Gets or sets the value to write.