# Siemens S7

> Connect Siemens S7 PLCs by address over S7comm or by variable name over S7CommPlus - the Access field, rack and slot, memory areas, typed byte/bit addressing, variable names, the HMI settings and the native data types.

The **Siemens S7** driver talks to Siemens S7 PLCs in one of two ways, chosen
on each device by its **Access**: **By address** over S7comm, the classic Step 7
protocol, with absolute addressing (memory area, data-block number, byte and
bit) that needs no symbol export from the engineering project; or **By name**
over S7CommPlus, the protocol TIA Portal and Siemens HMIs use, where each tag
names its variable as TIA Portal writes it and the CPU says its type. Add a
device under the Siemens S7 driver in the [Connector](connector) and fill in
the fields below; the panel shows only the fields the device's access uses.

## Access {#access}

| Aspect | By address | By name |
| --- | --- | --- |
| Protocol | S7comm on ISO-on-TCP, port 102 | S7CommPlus over TLS 1.3 on ISO-on-TCP, port 102 |
| CPUs | S7-300, S7-400, S7-1200, S7-1500 | S7-1500 with firmware V2.9 or later, S7-1200 with firmware V4.5 or later, configured in TIA Portal V17 or later |
| Device fields | Host, Rack / Slot | Host, Username and Password, both optional |
| What a tag names | Area, DB number, byte and bit, and its data type | The variable's name, for example `GanterOpt.Real_`; the CPU says its type |
| On the CPU | PUT/GET permitted and data blocks without optimized access ([Prepare the CPU](#prepare-the-cpu)) | Nothing to change: optimized data blocks are read and PUT/GET is not needed |
| Discover | None | The CPU's variables ([Discovery](#discovery)) |

A device created now starts **By name**. A device saved before the choice
existed keeps reading **By address**, as it always did. Switching the access
keeps both the tags' typed addresses and their variable names, so switching back
finds them where they were. A CPU that does not answer the access by name says
so on the device's page ([A CPU without the access by name](#a-cpu-without-the-access-by-name)).

## Connection {#connection}

| Field | Meaning | Default |
| --- | --- | --- |
| **Host** | IP address or host name of the CPU (or of the CP module that fronts it). | empty |
| **Access** | **By address** or **By name** ([Access](#access)). | By name |
| **Rack / Slot** | By address only. The CPU's rack and slot numbers, as configured in the Siemens hardware layout. Two number boxes on one row: rack first, slot second. | 0 / 0 |
| **Username / Password** | By name only, both optional: the password of the CPU's protection, where its access level asks for one, and a user name only for a CPU that keeps users. Kept protected on this computer. | empty |
| **Poll interval (ms)** | Device-wide read period for tags that do not override it. | 1,000 |

The TCP port is fixed at 102 (ISO-on-TCP), so there is no port field. The
device header shows the assembled endpoint, for example `s7://192.168.0.2:0:1`
(host, rack, slot) by address and `s7://192.168.0.2?access=name` by name.

By address, rack and slot must match the hardware: S7-1200 and S7-1500 CPUs answer on
rack 0, slot 0 or 1; a typical S7-300 CPU sits at rack 0, slot 2; S7-400 racks
vary with the layout. A wrong slot fails the connection rather than reading
wrong data.

Each tag may set its own **Poll interval (ms)**; a tag value of 0 falls back to
the device interval.

## Read by name {#read-by-name}

A tag of a device read by name names its variable in the Source card's
**Variable name** box, as TIA Portal writes it: the data block and the variable,
a dot before a member, an array element in brackets. TIA Portal's double quotes
may be typed (`"GanterOpt".Real_`) and are dropped.

| Variable in TIA Portal | Variable name |
| --- | --- |
| `Real_` in the data block `GanterOpt` | `GanterOpt.Real_` |
| `Valor` of the UDT instance `Udt_` | `GanterOpt.Udt_.Valor` |
| Element 0 of the array `Vetor` | `GanterOpt.Vetor[0]` |
| The DTL `DataHora` | `GanterOpt.DataHora`: one tag, a date and time |

There is no area, data-block number or byte to type: the CPU says where the
variable lies and its type, and the driver reads it as the tag's data type:

| CPU type | Data type | Note |
| --- | --- | --- |
| Bool | Boolean | |
| Byte, USInt; SInt | Byte; SByte | Unsigned; signed |
| Word, UInt; Int | UInt16; Int16 | Unsigned; signed |
| DWord, UDInt; DInt | UInt32; Int32 | Unsigned; signed |
| LWord, ULInt; LInt | UInt64; Int64 | All 64 bits, exact |
| Real; LReal | Float; Double | |
| Char, String | String | Windows-1252, as by address |
| WChar, WString | String | UTF-16, any script |
| Time, S5Time | Int32 | Milliseconds |
| LTime | Int64 | Nanoseconds |
| Time_Of_Day | UInt32 | Milliseconds since midnight, below 24 h |
| Date, Date_And_Time, DTL, LDT | DateTime | Date only; date and time |

Discover gives each tag the data type and the unit (ms, ns) of its variable. A
tag typed by hand takes the data type you pick, and the CPU's value is converted
to it.

A String or a WString is written whole, with the length it is declared with in
the block, which the driver reads from the variable before its first write: a
longer text is refused with both lengths and nothing is sent, and a String
refuses a character outside Windows-1252. A DTL is read and written whole, as
one date and time; its eight fields are never offered alone, since a field
written alone takes any value (month 90 included). A value outside its type's
range (a Time_Of_Day of 24 h or more, 40000 for an Int) is refused with the
range, and nothing is sent. A name the CPU does not hold is the tag's error,
saying so; a field of a DTL named alone is told to name the DTL.

### The HMI settings {#the-hmi-settings}

TIA Portal keeps three settings for each variable of a data block, in the
block's columns. The station honors them as Siemens' own HMI and OPC UA server
do, whatever the CPU itself lets through:

| Setting in TIA Portal | When it is cleared |
| --- | --- |
| **Accessible from HMI/OPC UA/Web API** | The variable is neither read nor written: the tag's error names the setting, and Discover leaves it out. |
| **Visible in HMI engineering** | Discover leaves the variable out; a tag that names it by hand reads it normally. |
| **Writable from HMI/OPC UA/Web API** | The variable is read only: a write is refused naming the setting, the tag's **Access** offers Read only and says why, and Discover offers it Read only. |

### A CPU without the access by name {#a-cpu-without-the-access-by-name}

A CPU that takes the connection and does not answer the access by name (an
S7-1500 before firmware V2.9, an S7-1200 before V4.5, a project configured in
a TIA Portal before V17, or a CPU that speaks only S7comm) leaves the device
offline. The device's page says it in a notice in place of the error: what the
CPU needs, where TIA Portal shows the firmware (the firmware the CPU runs under
**Online & diagnostics**, the version the project names in the CPU's properties
under **General > Catalog information**), and the three ways out:

1. Update the CPU's firmware, set the same version for the CPU in the TIA
   Portal project and download the hardware configuration. Updating the
   firmware alone is not enough.
2. Set **Access** to **By address**, with PUT/GET permitted and data blocks
   without optimized access ([Prepare the CPU](#prepare-the-cpu)).
3. Read the CPU through its own OPC UA server (an S7-1500, or an S7-1200 from
   firmware V4.4) with the [OPC UA](driver-opc-ua) driver.

Each tag of the device carries the same notice on its own page, and the notice
goes away by itself once the CPU answers by name. The journal and the agent read
the same steps in the device's error.

## Prepare the CPU {#prepare-the-cpu}

By address, an S7-1200 or S7-1500 serves S7comm's absolute addressing only with
two settings of its own, both made in TIA Portal and downloaded to the CPU. An
S7-300 or S7-400 needs neither, and a device read by name needs neither either.

| Setting | Where in TIA Portal | Without it |
| --- | --- | --- |
| **Permit access with PUT/GET communication from remote partner**, ticked | Open the CPU's device configuration, select the CPU, then **Properties > General > Protection & Security > Connection mechanisms**. Download the hardware configuration (**Download to device > Hardware configuration**): a download of the program alone does not carry it. | The CPU accepts the connection and refuses every read and write. The device stays connected and its tags read as bad quality. |
| **Optimized block access**, cleared, on every data block the station reads | In the project tree, under **Program blocks**, right-click the data block, then **Properties > General > Attributes**. Compile the block and download it. The block's **Offset** column then shows where each variable starts, which is what a tag's **Start byte** takes. | The CPU serves none of the block's bytes by address. Its tags read as bad quality, and the device's other tags keep reading. |

Changing **Optimized block access** reinitializes the data block at the
download: its variables go back to their start values. Flag memory (M), the
inputs (I) and the outputs (Q) are always addressable and need no change. A data
block that has to stay optimized can be read another way: the PLC program
copies the values the station reads into a data block without optimized access,
or into flag memory, and the tags point there; or the station reads the PLC
through its own OPC UA server (S7-1500; S7-1200 from firmware 4.4) with the
[OPC UA](driver-opc-ua) driver.

When the CPU refuses because of either setting, the device's page says which
one, with these steps, in a notice that goes away by itself once the CPU
accepts. For a data block it names the block and the tags it silences. The
journal and the agent read the same steps. A data block the CPU does not have,
and an address past the end of a data block, are said as what they are on the
tag that names them.

## Addressing {#addressing}

This section and the two after it (Data types, Writes) are the access by
address's; the access by name is in [Read by name](#read-by-name).

A tag of a device read by address is addressed by typed fields, not by a
free-text string. The panel shows them in the tag's Source card:

| Field | Meaning | Accepted values | Default |
| --- | --- | --- | --- |
| **Area** | The memory area to read. | `DataBlock`, `Merker`, `ProcessInput`, `ProcessOutput`, `Timer`, `Counter` | `DataBlock` |
| **DB number** | Which data block, shown only when Area is `DataBlock`. | 0 and up | 1 |
| **Start byte** | Byte offset inside the area where the value starts. In the `Timer` and `Counter` areas the box is labelled **Timer number** or **Counter number** and asks for the number of the timer or counter instead. | 0 and up | 0 |
| **Bit (0–7)** | Bit position inside the start byte. Shown only for Boolean tags. | 0–7 | 0 |
| **Max length (blank = 254)** | Declared maximum of an S7 STRING or WSTRING, in characters. Shown only for String tags outside the `Timer` and `Counter` areas. | 1–254 for a STRING, 1–16382 for a WSTRING, blank | blank |
| **Text type in the PLC** | The type the text is declared as in the block, beside its length: `STRING` (one byte per character, Windows-1252) or `WSTRING` (two bytes per character, UTF-16, any script). Shown where Max length is. | `STRING`, `WSTRING` | `STRING` |

The areas map to the usual Siemens notation: `Merker` is flag memory (M),
`ProcessInput` the process-image inputs (I/E), `ProcessOutput` the
process-image outputs (Q/A), and `Timer`/`Counter` the timer and counter
areas of S7-300 and S7-400 CPUs. Timers and counters are addressed by number, not by byte: `Timer` with
the number 5 reads T5, and `Counter` with the number 3 reads C3. Translating
classic absolute addresses:

| Classic address | Data type | Fields to enter |
| --- | --- | --- |
| `DB5.DBD12` holding a REAL | Float | Area `DataBlock`, DB number 5, Start byte 12 |
| `DB1.DBX3.4` | Boolean | Area `DataBlock`, DB number 1, Start byte 3, Bit 4 |
| `DB2.DBW20` holding an INT | Int16 | Area `DataBlock`, DB number 2, Start byte 20 |
| `MW10` | Int16 | Area `Merker`, Start byte 10 |
| `M10.3` | Boolean | Area `Merker`, Start byte 10, Bit 3 |
| `IB2` | Byte | Area `ProcessInput`, Start byte 2 |
| `Q4.1` | Boolean | Area `ProcessOutput`, Start byte 4, Bit 1 |
| `T5` | Int16 | Area `Timer`, Timer number 5 |
| `C3` | Int16 | Area `Counter`, Counter number 3 |

A Boolean tag reads and writes exactly one bit on the wire; it never rewrites
the surrounding byte. Every other type is byte-addressed at the start byte with
the exact width of its data type. The Source card's read-only **Source address
(wire)** line shows the assembled address as you edit.

## Data types {#data-types}

By address, the tag's **Data type** decides the S7 native type on the wire.
All thirteen S7-native widths are available:

| Data type | S7 type | Size on the wire |
| --- | --- | --- |
| Boolean | BOOL | 1 bit |
| SByte | SINT | 1 byte |
| Byte | USINT | 1 byte |
| Int16 | INT | 2 bytes |
| UInt16 | UINT | 2 bytes |
| Int32 | DINT | 4 bytes |
| UInt32 | UDINT | 4 bytes |
| Int64 | LINT | 8 bytes |
| UInt64 | ULINT | 8 bytes |
| Float | REAL | 4 bytes |
| Double | LREAL | 8 bytes |
| String | STRING or WSTRING | max length + 2 bytes (STRING), max length × 2 + 4 bytes (WSTRING) |
| DateTime | DTL | 12 bytes |

Multi-byte values use the S7-native big-endian byte order automatically; there
is no byte-order option because none is needed.

A String tag reads the standard S7 STRING layout: one byte of declared
maximum, one byte of current length, then the characters, one byte each in
Windows-1252, the code page TIA Portal and Siemens HMIs show a STRING in. What
the station writes is what TIA Portal and the HMI show ("Peça 12" stays
"Peça 12"), and the station reads it back the same. **Max length** counts
characters and must match the STRING declaration in the block (blank means the
S7 maximum of 254); the driver transfers max length + 2 bytes per read. A DateTime tag maps
to the DTL structure of the S7-1200/1500 family (year through nanoseconds);
the older 8-byte DATE_AND_TIME is not offered.

A String tag whose **Text type in the PLC** is `WSTRING` reads the S7 WSTRING layout instead: a word of declared maximum, a word of current length, then the characters, two bytes each in UTF-16, so it holds any script ("中" is written and read back as "中"). **Max length** still counts characters, from 1 to 16382, and must match the WString declaration in the block (blank means 254, the length TIA Portal gives a WString declared without one); the driver transfers max length × 2 + 4 bytes per read. S7comm cannot read the type off the CPU, so it has to match the variable: a tag left on `STRING` against a WString variable reads and writes the wrong layout. Every tag saved before the field existed is a `STRING`.

## Writes {#writes}

Tags whose **Access** is Read and write or Write only can be written. By
address, writes address the same area, byte and bit as reads: a Boolean write
sets its single bit, everything else writes the type's exact bytes. A new tag
starts as Read only.

A String write that takes more bytes than **Max length** is refused and
nothing is sent, never cut down to size: the journal and the agent read, for
example, "The DB1 byte 44 write was not sent: the text takes 21 bytes, and the
STRING holds 20." A text exactly as long as **Max length** is written whole.

A String write holding a character outside Windows-1252 (a Chinese character,
for instance) is refused the same way, never written with a question mark in
its place: the journal and the agent read, for example, "The DB1 byte 44 write
was not sent: the STRING holds Windows-1252 characters only, and '中' (U+4E2D)
is not one of them; it fits once the tag's text type in the PLC and the PLC variable are both WSTRING." The tag panel says the same
reason in the station's language.

To write such a text, declare the variable as WString in the block and set the tag's **Text type in the PLC** to `WSTRING`. A WSTRING counts two bytes per character, so a text longer than its **Max length** is refused the same way, with nothing sent: "The DB1 byte 94 write was not sent: the text takes 42 bytes, and the WSTRING holds 40 (20 characters of two bytes)."

## Discovery {#discovery}

A CPU read by name lists its variables; S7comm lists none, and no broadcast
finds a CPU. On the driver's page, **CPU to scan** in the **Scan scope** card
names the CPU: a Siemens device of the driver that reads its CPU by name,
scanned at that device's host and signed in with its password (the way a CPU
protected by a password lists its variables), or **An address typed here**,
with the CPU's address in **CPU address**.

**Discover** offers the CPU as one device with its variables as tags: those the
CPU marks accessible and visible from an HMI, every one ticked, typed as the
CPU says, and Read only where the CPU marks it not writable from an HMI. The
list is a tree by the path of each name: the data block, then each struct or
UDT instance and each array, then the variables, each named by the shortest end
of its name no other variable shares (`Real_`, `Valor`, `Vetor[0]`). A branch's
tick ticks or unticks everything under it, and the branch says how many of its
variables are ticked; a DTL is one variable. **Add device** creates a device
read by name with the ticked tags.

A CPU that a Siemens device of this station already reads by name is that
device, however the scan named it: the scan signs in with that device's
password, and **Add tags** adds the variables it has no tag for yet. On the
page of a device read by name, the Tags card's **Discover tags** does the same
for that device alone, with the same tree (see
[Discovery](connector-discovery#discovering-a-devices-tags)). A device read by
address offers no Discover tags: add its tags by hand.

## Good to know {#good-to-know}

- The Timer and Counter areas exist only on S7-300 and S7-400 CPUs. An
  S7-1200 has no such areas, and an S7-1500 refuses them over S7comm with
  "Address out of range" even with PUT/GET permitted; there, copy the timer or
  counter value into a data block or into flag memory in the PLC program and
  read it from there. Where the areas exist, they are addressed by number, not
  by byte offset: the box asks for the timer or counter itself (T0, T1, T2 and
  C0, C1, C2). Each value is one 16-bit word, so pair it with Int16; a wider or
  narrower data type is refused with the tag named in the journal, and the
  device's other tags keep reading.
- An unreachable PLC costs nothing at startup: the device keeps retrying in
  the background and its tags read as bad quality until the connection lands.
- A cable pulled after the device is online is noticed on the next read: the
  device goes offline, its tags turn bad quality, and it reconnects on its own
  once the network is back. An address the CPU refuses does not do that, so one
  mistyped tag never takes the rest of the device down with it. The tag whose read found the link gone says that the connection to the device was lost, in the station's language, and the Events page keeps Sharp7's words.
