[RQ8.12] The ResponseSPDU shall be built by the SafetyProvider by copying RequestSPDU.MonitoringNumber and RequestSPDU.SafetyConsumerID into the ResponseSPDU. After this, SPDU_ID, Flags, the SafetyData and the NonSafetyData shall be updated. Finally, ResponseSPDU.CRC shall be calculated and appended.


Figure 21 – Overview of task for SafetyProvider

For the ResponseSPDU.Flags, see Clause For the calculation of the SPDU_ID, see Clause For the calculation of CRC, see Clause

[RQ8.13] SPDU_ID_1, SPDU_ID_2 and SPDU_ID_3 shall be calculated according to

Figure 22 and Table 35.


Figure 22 – Calculation of the SPDU_ID

Table 35 – Presentation of the SPDU_ID

SPDU_ID_1 := SafetyBaseID (bytes 0…3) XOR SafetyProviderLevel_ID (bytes 0…3)

SPDU_ID_2 := SafetyBaseID (bytes 4…7) XOR SafetyStructureSignature (bytes 0…3)

SPDU_ID_3 := SafetyBaseID (bytes 8…11) XOR SafetyBaseID (bytes 12…15) XOR SafetyProviderID (bytes 0…3)

NOTE: In case of a mismatch between expected SPDU_ID and actual SPDU_ID, the following rules can be used for diagnostic purposes:

  • If all of SPDU_ID1, SPDU_ID2, and SPDU_ID3 differ, there probably is a mismatching SafetyBaseID.
  • If SPDU_ID3 differs, but SPDU_ID1 and SPDU_ID2 do not, there is a mismatching SafetyProviderID.
  • If SPDU_ID2 differs, but SPDU_ID1 and SPDU_ID3 do not, the structure or identifier of the safety data do not match.
  • If SPDU_ID1 differs, but SPDU_ID2 and SPDU_ID3 do not, the SafetyProviderLevel does not match.

By these rules, there is a very low probability (<10-9) that a mismatching SafetyBaseID will be misinterpreted. From a practical view, this probability can be ignored.

The SafetyProviderLevel is the SIL the SafetyProvider implementation (hardware & software) is capable of.

Table 36 – Coding for the SafetyProviderLevel_ID


Value of SafetyProviderLevel_ID



[RQ8.14] Exactly one of the values provided in Table 36 shall be used as constant code value for SafetyProviderLevel_ID. They were chosen in such a way that the hamming distance becomes maximal (hamming distance of 21).

[RQ8.15] Measures shall be taken to avoid that a SafetyProvider is erroneously using a code-value belonging to a SIL that is higher than the SIL it is capable of. For instance, a SafetyProvider capable of SIL1-3 should not be able to accidently use the value 0xAB47F33B used for SIL4. One way to achieve this is to avoid that this constant appears in the source code of the SafetyProvider at all.

The SafetyProviderLevel is independent to the SIL capability of the provided SafetyData, see Clause 3.2.

SafetyStructureSignature is used to check the number, data types, and order of application data transmitted in SafetyData. If the SafetyConsumer is expecting anything different than what the SafetyProvider actually provides, SafetyStructureSignature will differ, allowing the SafetyConsumer to enable fail-safe substitute values.

In addition, the identifier of the structure type (SafetyStructureIdentifier) is also taken into account when calculating SafetyStructureSignature. This ensures that the SafetyProvider and the SafetyConsumer are using the same identifier for the structure type, effectively avoiding any confusion.

For instance, if a SafetyProvider defines a structure with identifier “vec3D_m” comprising three floats containing a three-dimensional vector in the metric system, this structure could not be used by a SafetyConsumer expecting a structure of type “vec3D_in” where the vector components are given in inch, or even at a SafetyConsumer expecting a structure of type “orientation”, containing three floats to define an orientation using Euler angles.

[RQ8.16] SafetyStructureSignature shall be calculated as CRC32-signature (polynomial: 0x F4ACFB13, see Annex B.1) over SafetyStructureIdentifier (encoding: UTF-8), SafetyStructureSignatureVersion and the sequence of the DataType IDs. After each datatype ID, a 16-bit zero-value (0x0000) shall be inserted. All integers shall be encoded using little endian byte ordering. Data shall be processed in reverse order, see Annex B.1. The value “0” shall not be used as signature. Instead, the value “1” shall be used in this case.

The terminating zero of SafetyStructureIdentifier shall not be considered when calculating the CRC.

[RQ8.17] SafetyStructureIdentifier may be visible in the OPC UA information model for diagnostic purposes but shall not be evaluated by the SafetyConsumer during runtime.

[RQ8.18] For all releases up to Release 2.0 of the specification, the value for SafetyStructureSignatureVersion shall be 0x0001.


SafetyStructureIdentifier,e.g. “Motörhead” = 0x4d 0x6f 0x74 0xc3 0xb6 0x72 0x68 0x65 0x61 0x64

SafetyStructureSignatureVersion := 0x0001

1. DataType Int16: (Id = 0x0004), // see Clause 6.4

2. DataType Boolean: (Id = 0x0001),

3. DataType Float32: (Id =0x000A)

SafetyStructureSignature =

= CRC32_Backward(0x4d, 0x6f, 0x74, 0xc3, 0xb6, 0x72, 0x68, 0x65, 0x61, 0x64,


0x04,0x00, 0x00,0x00,

0x01,0x00, 0x00,0x00,

0x0A, 0x00, 0x00, 0x00) =

= CRC32_Forward(

0x00, 0x00, 0x00, 0x0A,0x00, 0x00, 0x00, 0x01,

0x00, 0x00, 0x00, 0x04,


= 0xe2e86173

NOTE: The insertion of 0x0000 values after the DataType ID, allows for introducing arrays in later version of OPC UA Safety.

NOTE: SafetyStructureSignatureVersion is the version of the procedure used to calculate the signature, as defined in [RQ8.16]. If future releases of this specification define an alternative procedure, they will indicate this by using a different version number.

OPC 10000-3, clause 5.8.2 defines different categories of DataTypes. Regarding the DataType ID which is to be used within the StructureSignature, the following holds:

  • For Built-in DataTypes, the ID from Table 1 of OPC 10000-6 is used as DataType ID.
  • For Simple DataTypes, the ID of the Built-in DataType from which they are derived is used.
  • As of now, Structured DataTypes (including OptionSets) shall not be used within Safety Data. Arrays are not supported. Instead, multiple variables of the same type are used.
  • Enumeration DataTypes are encoded on the wire as Int32 and therefore shall use the ID of the Int32 Built-in DataType.

The SafetyProvider calculates the CRC signature (ResponseSPDU.CRC) and sends it to the SafetyConsumer as part of SPDU. This enables the SafetyConsumer to check the correctness of the SPDU including the SafetyData, Flags, MNR, SafetyConsumerID and SPDU_ID by recalculating the CRC signature (CRC_calc).

[RQ8.19] The generator polynomial 0x F4ACFB13 shall be used for the 32-Bit CRC signature.

[RQ8.20] If SafetyData is longer than one byte (e.g., if it is of data type UInt16, Int16 or Float32), it shall be decoded and encoded using little endian order in which the least significant byte appears first in the incremental memory address stream.

[RQ8.21] The calculation sequence shall begin with the highest memory address (n) of the STrailer counting back to the lowest memory address (0) and then include also the SafetyData beginning with the highest memory address.

Figure 23 and shows the calculation sequence of a CRC_SPDU on a little-endian machine, using an example SafetyData with the following fields:

Int32 var1



Int16 var4


The STrailer and SafetyData have a total length of 34 bytes. The calculation of ResponseSPDU.CRC (SafetyProvider) or CRC_calc (SafetyConsumer) is done in reverse order, from bottom to top. In the example shown in Figure 23, CRC calculation starts at byte index 33 (most significant byte of the MNR) and ends at byte index 0.

NOTE: the reverse order ensures that the effectiveness of the CRC mechanism remains independent of any CRCs used within the underlying OPC UA channel, even if it would coincidentally use the same CRC polynomial.


Figure 23 – Calculation of the CRC (on little-endian machines, CRC32_Backward)

An alternative way to calculate the CRC (particularly useful on big-endian machines) is shown in Figure 24. Here, the individual elements of the ResponseSPDU are already arranged in memory in reversed order, and CRC calculation is executed from byte 0 to byte 33.


Figure 24 – Calculation of the CRC (on big-endian machines, CRC32_Forward)

[RQ8.22] On the SafetyConsumer, CRC_calc shall be calculated using data received in the ResponseSPDU, and not from expected values.