npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

omron-eip-tagdatalink

v0.1.6

Published

Omron NX/NJ EtherNet/IP tag data link adapter (target) for Node.js, with a Sysmac EDS generator.

Downloads

32

Readme

Omron-eip-TagDataLink

This library lets a PC read and write data to and from an Omron NJ/NX controller over EtherNet/IP implicit messaging, using the same mechanism Omron controllers use to share data with each other — what Omron calls a tag data link. It's meant for moving larger blocks of data, and it does it cyclically: once a connection is up, the data keeps refreshing on its own at a fixed interval instead of you asking for it each time.

It has been tested on real hardware. I haven't been able to try every possible case — very large tag sets in particular — so if you run into something, please let me know.

The Node-Red version can be found here: https://www.npmjs.com/package/node-red-contrib-omron-eip-tagdatalink

If you instead need to read or write individual tags on demand (explicit messaging), here are the drivers for that:

  • Node.js: https://www.npmjs.com/package/omron-eip
  • Node-RED: https://www.npmjs.com/package/node-red-contrib-omron-eip

This package also includes a tool that generates the EDS file you need to set up the connection between the PC and the controller (more on what that file is below).

One naming note: an Omron NJ/NX is technically a "Machine Automation Controller," or MAC, not a PLC — but I'll just call it a PLC here to keep things simple.


How it works

A little background first, because the rest makes a lot more sense once the roles are clear.

EtherNet/IP is an industrial network protocol that runs over ordinary Ethernet cabling. It has two styles of talking:

  • Explicit messaging: you send a request and get a reply, one transaction at a time — like asking "what's the value of this tag right now?" The two drivers linked above do this.
  • Implicit messaging: once it's set up, the two devices just keep swapping a fixed set of data automatically, over and over, at a set interval. You don't ask each time — it's continuous. "Cyclic" is just the industrial word for "repeating on a timer."

Omron's tag data link is the implicit messaging. You mark some variables as shared on each side, decide which ones go in which direction, and the controllers exchange them continuously — no read/write logic in your program.

Every tag data link connection has two ends:

  • The originator (Omron also calls it the scanner) is the device that opens the connection. It's the one holding the settings that say "connect to device X, swap these chunks of data, every N milliseconds." Normally that's a controller.
  • The target (also called the adapter) is the device that accepts the connection and exchanges data with the originator. A remote I/O block is a target; another controller can be a target too.

This library makes your PC act as the target. Your NJ/NX controller is the originator — it reaches out and opens the connection to the PC, and the two start swapping data. As far as the controller is concerned, the PC is just another EtherNet/IP device on the network, no different from talking to a second controller or an I/O block. (This same engine is also what a Node-RED node would sit on top of: the node would present the PC to the controller as a device and hand the incoming and outgoing data to your flow.)

Which direction is which

Everything below hinges on direction, and the labels are written from the controller's point of view, so they feel backwards at first. Worth getting straight now:

  • Output (in Sysmac) means the controller produces the data and the PC reads it. In EtherNet/IP terms this is the O→T direction ("originator to target").
  • Input (in Sysmac) means the controller consumes the data and the PC writes it. That's the T→O direction.

So an "Output" is something you read on the PC, and an "Input" is something you write from the PC. The names describe the controller's side, not yours.

A read connection still needs a tiny bit of data going back (you'll see this twice)

Here's a quirk that trips people up: Sysmac will not open a connection that only flows one way. So a read connection — controller Output coming to the PC — also has to carry a little data heading back the other way. The fix is to make a small Input variable (a single INT, 2 bytes, is plenty) that the PC sends back as a throwaway "return" value. It doesn't have to mean anything; it just has to be there so Sysmac is willing to open the connection.

So in practice a read connection is built from two pieces: the real Output data you actually care about, and a tiny Input return. Keep that in mind — it comes up again when we build the read connection.

What the EDS file is for

Before the controller will talk to the PC, it needs a description of what the PC looks like on the network: its identity, and the blocks of data it offers and how big each one is. That description is an EDS file (Electronic Data Sheet) — just a text file in a standard format. You drop it into Sysmac Studio, and from then on the PC shows up as a device you can pick, already sized correctly for your connections. The generator in this package writes that file for you from a copy/paste of your Sysmac setup, so you never have to hand-edit it.

(Those blocks of data the EDS describes are called assemblies, and each one has a number. You'll see those assembly numbers in the generator's output and in Sysmac — they're just IDs for each chunk of data.)


Walkthrough: variables, then EDS, then connections, then test

Do these in order. There are a lot of small steps, and the usual reason a connection won't open later is that one of them got skipped — a variable that wasn't downloaded, a tag set that wasn't registered, an EDS that wasn't reloaded. Go through them in sequence and you should be fine.

1. Create the published Input and Output variables

In Sysmac, create the global variables you want to exchange. For each one, set its Network Publish attribute — that's the setting that exposes a variable to the network so it can be part of a tag data link. Set it by direction:

  • a variable the controller sends to the PC → Output (the PC reads it)
  • a variable the PC sends to the controller → Input (the PC writes it)

For this example we'll exchange:

  • Out_ReadOnly (Output) — 20 bytes the controller produces for the PC to read
  • In_WriteOnly (Input) — 20 bytes the PC produces for the controller
  • In_ReadReturn (Input) — a 2-byte dummy return so the read connection is allowed (that quirk from above)
  • In_Custom1 / Out_Custom1 — a structure (sMyDataType1) in each direction
  • In_Custom2 / Out_Custom2 — a second structure (sMyDataType2) in each direction

Notice In_ReadReturn in that list: because Out_ReadOnly is read-only data, we also made that small Input for the PC to send back, so Sysmac will open the read connection.

Step 1 — creating the Input and Output published variables in Sysmac

2. Run the EDS generator and paste your variables

From a terminal:

npx omron-eip-tagdatalink-gen-eds

The first thing it asks for is a copy/paste of your variable grid from Sysmac. Select your published variables in Sysmac's variable table, copy them, paste into the terminal, and press Enter twice. The blank line is how the generator knows the paste is finished.

Step 2 — running the generator; it asks you to paste the variables

Your paste looks like this (it's tab-separated, exactly as it comes out of Sysmac):

Out_ReadOnly	ARRAY[0..9] OF INT			False	False	Output	READ data: controller -> PC (EDS Assem100, target 100, 20 B)
In_WriteOnly	ARRAY[0..9] OF INT			False	False	Input	WRITE data: PC -> controller (EDS Assem101, target 101, 20 B)
In_ReadReturn	INT			False	False	Input	Dummy return for the read connection (EDS Assem102, target 102, 2 B)
In_Custom1	sMyDataType1			False	False	Input	
Out_Custom1	sMyDataType1			False	False	Output	
In_Custom2	sMyDataType2			False	False	Input	
Out_Custom2	sMyDataType2			False	False	Output	

3. Paste any structures it asks for

If the generator sees a variable whose type is a structure (a UDT, a custom data type made of several fields) — here sMyDataType1 and sMyDataType2 — it stops and asks you to paste that structure's definition. If a structure contains another structure inside it, it asks for that one next, and keeps going until it has every nested type.

Step 3 — the generator asks for the first structure, then the next

4. Copy the structure definitions from Sysmac

Open the data type view in Sysmac, copy each structure, and paste it the same way (Enter twice after each).

Step 4 — the structures in Sysmac Studio

The paste looks like this:

sMyDataType1	STRUCT				NJ
Test_Array_BOOL	ARRAY[0..9] OF BOOL				
Test_ULINT	ULINT				
Test_Custom	sMyDataType2				
Test_Real	REAL				
Test_String	STRING[256]				


sMyDataType2	STRUCT				NJ
Bool1	BOOL				
Bool2	BOOL				
Bool3	BOOL				
Bool4	BOOL				
Bool_5_to_15	ARRAY[4..15] OF BOOL

5. Go online and download the variables to the controller

The variables have to actually exist in the running controller, not just in your offline project on the PC. Go online with the controller and use Synchronize to transfer (download) the project — variables and all — into the controller.

Step 5 — go online and press Synchronize to download to the controller

If you skip this, the connection fails later with a "Tag Name Resolution Error," which just means the controller went looking for the variable and couldn't find it — because it was never downloaded.

6. Open the EtherNet/IP Connection Settings

In Sysmac, go to Tools, then EtherNet/IP Connection Settings.

Step 6 — Tools then EtherNet/IP Connection Settings

7. Register your tag sets, then paste them into the generator

Next you register the tag sets. A tag set is just a named bundle of one or more variables that travel together as a single connection. This is where you decide how your data is grouped — each tag set becomes one side of a connection.

Step 7 — registering the tag sets

In this example each tag set holds a single variable, and we'll end up with 4 connections (2 bidirectional, 1 write, 1 read). You don't have to do it that way, though. You can put several variables into one tag set and move them all in a single connection, and that's often the better choice — one connection carrying a grouped tag set is a lot less overhead than a handful of tiny ones. Group your variables by what naturally travels together.

Back in the generator, it asks for your Input tag sets. Copy them from Sysmac and paste:

Tagset	In_WriteOnly	Byte	20		Auto	Not included
Tag	In_WriteOnly	Byte	20	0		
Tagset	In_ReadReturn	Byte	2		Auto	Not included
Tag	In_ReadReturn	Byte	2	0		
Tagset	In_Custom1	Byte	288		Auto	Not included
Tag	In_Custom1	Byte	288	0		
Tagset	In_Custom2	Byte	10		Auto	Not included
Tag	In_Custom2	Byte	10	0	

then your Output tag sets:

Tagset	Out_ReadOnly	Byte	20		Auto	Not included	
Tag	Out_ReadOnly	Byte	20	0			Cleared
Tagset	Out_Custom1	Byte	288		Auto	Not included	
Tag	Out_Custom1	Byte	288	0			Cleared
Tagset	Out_Custom2	Byte	10		Auto	Not included	
Tag	Out_Custom2	Byte	10	0			Cleared

Step 8 — Input and Output tag sets pasted into the generator

8. Build your connections

Now you pair tag sets into connections. This part is entirely your design, and if you make a mistake you can go back a step or start over at any point. You already decided your connections when you planned your tag sets, so have that plan in front of you.

In this example there are 4 connections. The first is bidirectional — you pick the Output tag set (what the PC reads) and the Input tag set (what the PC writes). Here that's the Custom1 pair.

Step 9 — adding a connection: bidirectional, choosing the Custom1 input and output tag sets

Next, the write-only connection (PC to controller):

Step 10 — creating the write-only connection

Then the read-only connection (controller to PC). Remember the quirk from earlier: a read connection also needs a small return going back, so this one uses the real Output tag set together with the little In_ReadReturn Input tag set as its dummy write. Without that return, Sysmac won't open it.

Step 11 — the read-only connection, with the dummy-return Input

Finally the second bidirectional connection (Custom2). Since there's only one Input and one Output tag set left, the generator picks them automatically.

For all of these the screenshots leave the connection name at its default, but you can name it whatever you want — that name is what you'll see later in Sysmac Studio. When you've added the last connection, type f to finish.

Step 12 — the last bidirectional connection (Custom2), auto-selected

9. Name the EDS file

The generator asks for a file name. This name is what shows up in Sysmac when you pick the target device, so make it something you'll recognize. Here it's node-test-2.

Step 13 — naming the EDS file

It then tells you where it saved the file. Your path will be different from the screenshot — it depends on what folder you ran the command in.

Step 14 — where the EDS file was saved

10. Install the EDS into Sysmac

Copy the .eds file into Sysmac's EDS folder:

C:\Program Files\OMRON\Sysmac Studio\IODeviceProfiles\EipConnection\Eds

Step 15 — the Sysmac Eds folder

Important: Sysmac only reads that folder when it starts up. So save your project, fully close Sysmac Studio, and reopen the project before the new EDS will show up.

11. Add the PC as a target device

Back in EtherNet/IP Connection Settings, add a target device. Set the IP address of the PC running this library (in this example 192.168.251.50), pick the node-test-2 EDS, and choose the latest version.

Step 16 — adding the target device with the PC's IP and the EDS

12. Recreate the connections in Sysmac using the generator's summary

The generator prints a summary of every connection — the names, directions, sizes, and assembly/connection numbers.

Step 17 — the connection summary printed by the generator

Use that summary to add the connections to the target device in Sysmac. They line up one-to-one with what the generator listed.

Step 18 — the 4 connections on the target device, matching the summary

Two choices to make on this screen:

  • Connection type: choose Point-to-Point, not Multicast. Multicast sends one copy of the data to the whole network at once, which only helps if a bunch of devices all need the exact same data; otherwise it just adds noise, and plenty of switches handle it poorly. For a single PC trading its own data, point-to-point (one sender, one receiver) is simpler and more reliable.
  • RPI (Requested Packet Interval) is how often the data is refreshed on that connection, in milliseconds. Smaller means faster updates but more network traffic. In this example they're all set to 50.

13. Transfer the EtherNet/IP settings to the controller

This is a separate transfer from the variable download back in step 5 — different button, different data. Go online and transfer the EtherNet/IP Connection Settings to the controller. The controller will only open connections that are part of its running configuration, so this step is what actually arms them.

Step 19 — transferring the EtherNet/IP settings to the controller

14. Test the connection

The generator offers to run a live connection test. Do it — it's the quickest way to confirm everything works before you build anything on top of it.

Step 20 — the generator offering the connection test, with the pre-flight warning

Before you test, make sure:

  • everything is transferred to the controller — both the variables (step 5) and the EtherNet/IP settings (step 13)
  • the PC and the controller are on the same subnet (same local network range) and the PC can reach the controller. A quick way to check is to ping the controller's IP from a command prompt on the PC; if you get replies, they can see each other.
  • the PC's firewall isn't blocking the connection. EtherNet/IP uses two network ports, TCP 44818 and UDP 2222, and both have to be allowed inbound on the PC.

The test asks for the controller's IP address and which PC network adapter to use. You can leave the adapter blank to listen on all of them, or type in the IPv4 address of the PC's network card that's wired to the controller.

Step 21 — entering the controller IP and the PC network adapter

It runs for a bit and then tells you which connections came up and which didn't:

  • Some work, some fail: the ones that failed almost always have a variable or a setting that didn't make it to the controller. Re-transfer both the variables and the EtherNet/IP settings, then retry (the test gives you a retry option).
  • All fail: that's usually a network problem, not a configuration one. Confirm you can ping the controller from the PC, and try turning the firewall off for a moment to rule it out.
  • Can't ping the controller at all: then the problem is below EtherNet/IP entirely. Check the cabling, and check that both devices are on the same subnet — that means matching subnet masks and IP addresses in the same range (for example both 192.168.251.x).

Install

npm install omron-eip-tagdatalink

If you're installing from a local folder instead of from npm, give npm an actual path that starts with ./ or a full path, for example npm install "./omron-eip-tagdatalink". If you write it as a bare name/name, npm assumes it's a GitHub repository and tries to run git, which is why something like npm install npm/omron-eip-tagdatalink fails.

Use it in your code

Once the controller is talking to the PC, this is how you actually get and send the data from Node.js. You set up the same connections you made in Sysmac, then you listen for incoming data and call a function to send outgoing data.

Remember the direction rule: read is data coming in from the controller (its Output, the O→T direction), and write is data you send out to the controller (its Input, the T→O direction). The byte sizes you describe here have to match the tag sets and the EDS.

const { OmronTdlDevice, S, ARR, M } = require('omron-eip-tagdatalink');

const tenInts = () => ({ members: [M('data', ARR(S('INT'), 0, 9))] }); // 20 bytes
const oneInt  = () => ({ members: [M('ret',  S('INT'))] });            //  2 bytes

const device = new OmronTdlDevice({
  host: '0.0.0.0',                            // listen on every network card, or pass one PC IP
  identity: { productName: 'node-test-2' },   // optional; match the name in your EDS
  connections: [
    { name: 'In_WriteOnly', write: tenInts() },                 // PC writes out (T->O)
    { name: 'Out_ReadOnly', read: tenInts(), write: oneInt() }, // PC reads in (O->T) + the dummy return
    { name: 'IO',           read: tenInts(), write: tenInts() } // bidirectional
  ],
});

device.on('listening', () => console.log('listening on TCP 44818 / UDP 2222'));
device.on('open',   (name, c) => console.log(`open ${name}: read ${c.otPayloadBytes}B / write ${c.toPayloadBytes}B`));
device.on('values', (name, values) => console.log(`from PLC -> ${name}`, values)); // data the controller sent
device.on('close',  (name, why) => console.log(`closed ${name} (${why})`));
device.on('error',  (e) => console.error('error:', e.message));

device.start();

// send data out to the controller; call this whenever your values change
setInterval(() => {
  device.write('In_WriteOnly', { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] });
}, 100);

process.on('SIGINT', () => { device.stop(); process.exit(0); });

If a connection carries a structure, describe its layout with the builders instead of a flat array, for example STRUCT('NJ', [M('Test_ULINT', S('ULINT')), M('Test_Real', S('REAL')), /* ... */], 'sMyDataType1'), and the fields have to add up to the byte size Sysmac shows for that type. You only need to spell out the real structure if you want the data handed back to you as named fields; if you just want the connection to open, any layout that comes to the right number of bytes works.

A few things worth knowing

  • Only one program can use the EtherNet/IP ports (TCP 44818 / UDP 2222) on a given IP address at a time, so you can run one of these adapters per IP.
  • 64-bit numbers (the LINT, ULINT, and LWORD types) come back as JavaScript BigInt values. JSON.stringify can't handle a BigInt on its own and will throw, so if you need to turn the data into JSON, pass a small replacer: JSON.stringify(v, (_k, x) => typeof x === 'bigint' ? x.toString() : x).
  • The connection names are just labels for you. Behind the scenes the library matches an incoming connection to one of your configs by its data size, so try to keep each connection's (read, write) byte sizes distinct so there's no ambiguity.

API summary

new OmronTdlDevice(options)

  • host — the IP of the network card to listen on, or '0.0.0.0' for all of them (the default).
  • identity — optional, like { productName, productCode, vendorId, deviceType }; the defaults already match the generated EDS.
  • connections — a list of { name, read?, write? }. read and write are tag sets, written as { members: [ M(name, type), ... ] }. read is what the PC consumes (O→T) and write is what the PC produces (T→O).
  • logLevel — one of 'error', 'warn', 'info', 'debug'.

Methods: start(), stop(), and write(connectionName, valuesObject), which sets the data the PC sends out on that connection and returns true if the connection is open.

Events: listening, open(name, conn), values(name, values, conn), stale(name, conn), unbound(conn), close(name, reason, conn), and error(err). The connection object passed to those handlers carries otPayloadBytes, toPayloadBytes, peerHost, otRpiUs, toRpiUs, and rxCount.

Type builders for tag set members: S(scalar) for a single value ('BOOL', 'INT', 'DINT', 'REAL', 'LINT', 'ULINT', 'LREAL', and so on), ARR(elemType, from, to) for an array, STR(length) for a string, STRUCT(offsetType, members, name) for a structure, and M(memberName, type) for one field inside a tag set or structure.

Regenerating the EDS later

Any time you change your variables or tag sets, run the generator again and reinstall the EDS (and remember to close and reopen Sysmac so it picks up the new file):

npx omron-eip-tagdatalink-gen-eds

Troubleshooting

A connection never opens and Sysmac shows a "Tag Name Resolution Error." That message means the controller went to look up a variable in one of its tag sets and couldn't find it. Almost always the variables or the EtherNet/IP settings weren't actually downloaded to the controller, or a variable isn't set to Network Publish in the right direction. Re-transfer both, set the controller to RUN, and retry. This is a controller-side issue — it isn't the PC, the EDS, or the data type. (Structures that contain strings, arrays of BOOL, and nested structures are all confirmed to work over a tag data link.)

All connections fail. That's almost always the network rather than the configuration. Make sure you can ping the controller from the PC, check that the firewall is allowing inbound TCP 44818 and UDP 2222, and confirm both devices are on the same subnet.

An old EDS keeps loading. Delete it from the Sysmac EDS folder, copy the new one in, and restart Sysmac.

You get EADDRNOTAVAIL when the program starts. The host IP you gave isn't one of the PC's own addresses. Use the PC's real network card IP, or '0.0.0.0' to listen on all of them.

License

GPL-2.0