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
Maintainers
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 readIn_WriteOnly(Input) — 20 bytes the PC produces for the controllerIn_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 directionIn_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.

2. Run the EDS generator and paste your variables
From a terminal:
npx omron-eip-tagdatalink-gen-edsThe 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.

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.

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).

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 BOOL5. 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.

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.

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.

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
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.

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

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.

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.

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.

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.

10. Install the EDS into Sysmac
Copy the .eds file into Sysmac's EDS folder:
C:\Program Files\OMRON\Sysmac Studio\IODeviceProfiles\EipConnection\Eds
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.

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.

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

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.

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.

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.

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-tagdatalinkIf 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, andLWORDtypes) come back as JavaScript BigInt values.JSON.stringifycan'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? }.readandwriteare tag sets, written as{ members: [ M(name, type), ... ] }.readis what the PC consumes (O→T) andwriteis 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-edsTroubleshooting
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
