Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

nodes7

plcpeople4kMIT0.3.18TypeScript support: definitely-typed

Routine to communicate with Siemens S7 PLCs

S7, Siemens, PLC, RFC1006, iso-on-tcp

readme

nodeS7

NodeS7 is a library that allows communication to S7-300/400/1200/1500 PLCs using the Siemens S7 Ethernet protocol RFC1006.

This software is not affiliated with Siemens in any way, nor am I. S7-300, S7-400, S7-1200 and S7-1500 are trademarks of Siemens AG.

WARNING

Fully test everything you do. In situations where writing to a random area of memory within the PLC could cost you money, back up your data and test this really well. If this could injure someone or worse, consider other software.

Installation

Using npm:

  • npm install nodes7

Using yarn:

  • yarn add nodes7

Optimization

  • It is optimized in three ways - It sorts a large number of items being requested from the PLC and decides what overall data areas to request, then it groups multiple small requests together in a single packet or number of packets up to the maximum length the PLC supports, then it sends multiple packets at once, for maximum speed. So a request for 100 different bits, all close (but not necessarily completely contiguous) will be grouped in one single request to the PLC, with no additional direction from the user.

  • NodeS7 manages reconnects for you. So if the connection is lost because the PLC is powered down or disconnected, you can continue to request data with no other action necessary. "Bad" values are returned, and eventually the connection will be automatically restored.

  • NodeS7 is written entirely in Javascript, so no compiler installation is necessary on Windows, and deployment on other platforms (ARM, etc) should be no problem.

PLC Support

  • S7-1200 and S7-1500 CPU access requires access using "Slot 1" and you must disable optimized block access (in TIA portal) for the blocks you are using. In addition, you must "Enable GET/PUT Access" in the 1200/1500 controller in TIA Portal. Doing so opens up the controller for other access by other applications as well, so be aware of the security implications of doing this.

  • This has been tested on direct connections to newer PROFINET CPUs and Helmholz NetLINK PRO COMPACT and IBH units. (Note with these gateways you often have to specify the MPI address as the slot number) It is reported to work with other CPU/CP combinations as well, although not all S7-200 datatypes are supported. S7 routing is not supported.

  • Logo 0BA8 PLCs are supported although you should set your local and remote TSAP to match your project, and your addresses have to be specified differently. DB1,INT0 should get VM0. DB1,INT1118 should get AM1.

VFD Support

  • SINAMICS S120 and G120 FW 4.7 and up work as well, as these drives support direct connection USING SLOT 0 (instead of other examples that use 1 or 2) and some modified parameter addressing. This technique can work with these drives with other software as well and is documented on the Siemens website. Basically, to address parameter number 24, output frequency for example, is defined in the documentation as a real number, so DB24,REAL0 would return the output frequency. If this parameter were an array, DB24,REAL1 would return the next in sequence even though a Siemens programmer would be tempted to use REAL4 which is not correct in this case. For this reason, normal S7 optimization must be disabled. After you declare conn = new nodes7; (or similar) then add conn.doNotOptimize = true; to ensure this isn't done, and don't try to request these items using array notation as this implies optimization, request REAL0 then REAL1 etc. doNotOptimize is now also supported as a connection parameter.

Credit to the S7 Wireshark dissector plugin for help understanding why things were not working. (http://sourceforge.net/projects/s7commwireshark/)

Examples

var nodes7 = require('nodes7'); // This is the package name, if the repository is cloned you may need to require 'nodeS7' with uppercase S
var conn = new nodes7;
var doneReading = false;
var doneWriting = false;

var variables = {
      TEST1: 'MR4',          // Memory real at MD4
      TEST2: 'M32.2',        // Bit at M32.2
      TEST3: 'M20.0',        // Bit at M20.0
      TEST4: 'DB1,REAL0.20', // Array of 20 values in DB1
      TEST5: 'DB1,REAL4',    // Single real value
      TEST6: 'DB1,REAL8',    // Another single real value
      TEST7: 'DB1,INT12.2',  // Two integer value array
      TEST8: 'DB1,LREAL4',   // Single 8-byte real value
      TEST9: 'DB1,X14.0',    // Single bit in a data block
      TEST10: 'DB1,X14.0.8'  // Array of 8 bits in a data block
};

conn.initiateConnection({ port: 102, host: '192.168.0.2', rack: 0, slot: 1, debug: false }, connected); // slot 2 for 300/400, slot 1 for 1200/1500, change debug to true to get more info
// conn.initiateConnection({port: 102, host: '192.168.0.2', localTSAP: 0x0100, remoteTSAP: 0x0200, timeout: 8000, doNotOptimize: true}, connected);
// local and remote TSAP can also be directly specified instead. The timeout option specifies the TCP timeout.

function connected(err) {
  if (typeof(err) !== "undefined") {
    // We have an error. Maybe the PLC is not reachable.
    console.log(err);
    process.exit();
  }
  conn.setTranslationCB(function(tag) { return variables[tag]; }); // This sets the "translation" to allow us to work with object names
  conn.addItems(['TEST1', 'TEST4']);
  conn.addItems('TEST6');
  // conn.removeItems(['TEST2', 'TEST3']); // We could do this.
  // conn.writeItems(['TEST5', 'TEST6'], [ 867.5309, 9 ], valuesWritten); // You can write an array of items as well.
  // conn.writeItems('TEST7', [666, 777], valuesWritten); // You can write a single array item too.
  conn.writeItems('TEST3', true, valuesWritten); // This writes a single boolean item (one bit) to true
  conn.readAllItems(valuesReady);
}

function valuesReady(anythingBad, values) {
  if (anythingBad) { console.log("SOMETHING WENT WRONG READING VALUES!!!!"); }
  console.log(values);
  doneReading = true;
  if (doneWriting) { process.exit(); }
}

function valuesWritten(anythingBad) {
  if (anythingBad) { console.log("SOMETHING WENT WRONG WRITING VALUES!!!!"); }
  console.log("Done writing.");
  doneWriting = true;
  if (doneReading) { process.exit(); }
}

API

nodes7.initiateConnection(options, callback)

Description

Connects to a PLC.

Arguments

Options

Property type default
rack number 0
slot number 2
port number 102
host string 192.168.8.106
timeout number 5000
localTSAP hex undefined
remoteTSAP hex undefined

callback(err)

err
err is either an error object, or undefined on successful connection.

nodes7.dropConnection(callback)

Description

Disconnects from a PLC. This simply terminates the TCP connection.

Arguments

callback()

The callback is called upon completion of the close.

nodes7.setTranslationCB(translator)

Description

Sets a callback for name - address translation.

This is optional - you can choose to use "addItem" etc with absolute addresses.

If you use it, translator should be a function that takes a string as an argument, and returns a string in the following format: <data block number.><memory area><data type><byte offset><.array length>

Examples:

  • MR30 - MD30 as REAL
  • DB10,LR32 - LREAL at byte offset 32 in DB10, for 1200/1500 only
  • DB10,INT6 - DB10.DBW6 as INT
  • DB10,I6 -same as above
  • DB10,INT6.2 - DB10.DBW6 and DB10.DBW8 in an array with length 2
  • DB10,X14.0 - DB10.DBX14.0 as BOOL
  • DB10,X14.0.8 - DB10.DBB14 as an array of 8 BOOL
  • PIW30 - PIW30 as INT
  • DB10,S20.30 - String at offset 20 with length of 30 (actual array length 32 due to format of String type, length byte will be read/written)
  • DB10,S20.30.3 - Array of 3 strings at offset 20, each with length of 30 (actual array length 32 due to format of String type, length byte will be read/written)
  • DB10,C22.30 - Character array at offset 22 with length of 30 (best to not use this with strings as length byte is ignored)
  • DB10,DT0 - Date and time
  • DB10,DTZ0 - Date and time in UTC
  • DB10,DTL0 - DTL in newer PLCs
  • DB10,DTLZ0 - DTL in newer PLCs in UTC

The DT type is the well-known DATE_AND_TIME type of S7-300/400 PLCs, a 8-byte-wide field with BCD-encoded parts

The DTZ type is the same as the DT, but it expects that the timestamp is in UTC in the PLC (usually NOT the case)

The DTL type is the one seen on newer S7-1200/1500 PLCs, is 12-byte long and encodes the timestamp differently than the older DATE_AND_TIME

The DTLZ type is also the same as the DTL, but expecting the timestamp in UTC in the PLC

In the example above, an object is declared and the translator references that object. It could just as reference a file or database. In any case, it allows cleaner Javascript code to be written that refers to a name instead of an absolute address.

nodes7.addItems(items)

Description

Adds items to the internal read polling list.

Arguments

items can be a string or an array of strings.

If items includes the value _COMMERR it will return current communication status.

nodes7.removeItems(items)

Description

Removes items to the internal read polling list.

Arguments

items can be a string or an array of strings.

If items is not defined then all items are removed.

nodes7.writeItems(items, values, callback)

Description

Writes items to the PLC using the corresponding values and calls callback when done.

You should monitor the return value - if it is non-zero, the write will not be processed as there is already one it progress, and the callback will not be called.

Arguments

items can be a string or an array of strings.

If items is a single string, values should then be a single item.

If items is an array of strings, values must also be an array of values.

callback(err)

err
a boolean indicating if ANY of the items have "bad quality".

nodes7.readAllItems(callback)

Description

Reads the internal polling list and calls callback when done.

Arguments

callback(err, values)

err
a boolean indicating if ANY of the items have "bad quality".
values
an object containing the values being read as keys and their value (from the PLC) as the value.

changelog

Version: 0.3.18

  • Add support for WDT to specify date/time which can be either UTC or local time depending on a connection parameter
  • Call back readDoneCallback even when asked to read no valid tags

Version: 0.3.17

  • Modify the globalTimeout variable when timeout is specified as a connection parameter

Version: 0.3.16

  • Return error on timeout based on timer, similar to what is seen if there is a real TCP timeout

Version: 0.3.15

  • Check for NaN byte length causing crash in processS7Packet (thanks to cstim)

Version: 0.3.14

  • Allows DWT as well as DW for type specifier for compatibility with some OPC server tag file export format
  • Allows doNotOptimize as a connection parameter (it defaults to false, should be set to true for G120 drives)

Version: 0.3.13

  • Fix for error ECONNRESET (thanks to adopozo)
  • Documentation improvements including G120 drive support documentation (thanks to aurelien49 for testing this)

Version: 0.3.12

  • Fix for WORD datatype partially missing

Version: 0.3.11

  • Addition of date and time datatypes (thanks to gfcittolin)
  • Minor documentation improvements

Version: 0.3.10

  • Fixes for sequence number collisions (thanks to gfcittolin)

Version: 0.3.9

  • Fixes for connection reset in cases where enough data was being read that more polls were required than could run in parallel

Version: 0.3.8

  • Further improvements on connection reset

Version: 0.3.7

  • outputLog spelling correction and connection ID added to some outputLog instances where it was missing
  • LREAL type mentioned in documentation
  • LINT type added but BigInt64 requires Node 12 so hold for now
  • Only reset on packet timeout when connected

Version: 0.3.6

  • Reset on packet timeout
  • LREAL type added

Version: 0.3.5

  • Fixed behavior if a write is requested while an earlier-requested one is in progress.
  • Other improvements to re-establishing connection

Version: 0.3.4

  • Hotfix to prevent crash due to delayed packet (thanks to gfcittolin)

Version: 0.3.3

  • Hotfix to prevent crash from LOGO PLC sending split packet (thanks to gfcittolin)

Version: 0.3.2

  • Fix bug related to bit array length (thanks to luisbardalez)
  • Better tracking of timers during dropConnection (thanks to gfcittolin)

Version: 0.3.1

  • Fix bug related to variable timeout

Version: 0.3.0

  • Add variable timeout (thanks to babinc)
  • Add reference to MIT license to package.json
  • Use of arrow functions requires dropping support for very old versions of node

Version: 0.2.5

  • Fix request packet bigger than PDU size

Version: 0.2.4

  • Fix logging when slicing response packet from PLC

Version: 0.2.3

  • Fix support for string arrays

Version: 0.2.2

  • Fix readDoneCallback typeof typo

Version: 0.2.1

  • Change from Buffer.from() to buffer.slice(), so we keep compatible with versions of NodeJS older than 6.x

Version: 0.2.0

  • Implement TSAP mode connection. Allows to directly specify local and remote TSAP values instead of only rack/slot. Useful for connecting with PLCs like Logo.

Version: 0.1.15

  • Ensure the socket is destroyed on connection cleanup

Version: 0.1.14

  • Fix bug to handle the case when more than one packet is waiting in the incoming buffer

Version: 0.1.13

  • Fix bug when writing a single character

Version: 0.1.12

  • Add more options for datatype syntax (thanks to sembaye)
  • Add support for RFC1006 fast acknowledge for old PLCs and WinAC RTX (thanks to sembaye)
  • Fix for onClientClose causing readAllItems to never return when connection closed by partner

Version: 0.1.11

  • Fix error when reading across multiple DBs

Version: 0.1.10

  • Fix errors writing single/multiple items of bit and byte length
  • Fix errors writing arrays of boolean with length greater than 8 and at least one true value

Version: 0.1.9

  • Fix missing self.globalWriteBlockList reinitialize
  • remove dependencies
  • Linting

Version: 0.1.8

  • Fix missing self in dropConnection
  • Add callback to dropConnection

Version: 0.1.7

  • Add optional options to NodeS7 constructor
  • Add silent/debug mode options

Version: 0.1.6

  • Fixes #4: Error on writing more then 32 byte of data
  • Fixes #5: Error on writing Array of Boolean

All other version are not recorded.