Code readingHow-to

USB barcode scanner to CODESYS: update a tag via OPC-UA

Capture USB barcode scanner input outside the runtime in Node-RED, write it to a CODESYS string tag over OPC-UA, with capture paths, latency and tag sizing.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01Why can't CODESYS read my USB scanner directly?
  2. 02What do you need for this integration?
  3. 03Which capture path should you pick: HID keystrokes, USB serial or Ethernet?
  4. 04How does Node-RED write the scan into a CODESYS tag?
  5. 05How fast does a scan reach the tag?
  6. 06How big should the string tag be?
  7. 07How do you stop duplicate scans and empty writes?
  8. 08How do you verify the scan lands in the program?
  9. 09When should you skip OPC UA and wire the reader straight in?
  10. 10Questions

Your barcode scanner works fine when a text box has focus: scan, and the digits appear as if typed. The trouble starts when the destination is a machine. A CODESYS program reads tags, not keystrokes, and an operator should not have to click a field before every scan. This guide gives you the integration that removes the click. You capture the scan in Node-RED, outside the PLC runtime, and write it into a CODESYS string tag through the runtime's OPC-UA server. It is an afternoon on a bench: one capture-path decision, one Node-RED flow, one tag — plus the latency and sizing numbers that tell you whether the result is fast and roomy enough for your line.

Why can't CODESYS read my USB scanner directly?

It can't, because the runtime has no USB host driver of its own: the operating system owns the port, and a scanner running in keyboard (HID) mode types each scan as keystrokes into whatever field has focus — a text box in your HMI, never a PLC tag.

That typing behaviour explains the test you have already run: tap a text box, scan, and the payload lands as if typed on a keyboard. The runtime never sees that stream, because its variables are fed by its own tasks and communication interfaces, not by the OS keyboard queue. So the scan has to arrive as data. The pattern that removes the click is a capture process beside the runtime: it reads the scan at operating-system level and writes it to a tag over OPC UA.

If the controller side runs Linux, the capture half of this chain is exactly the one in USB barcode scanner on a Linux controller: serial port in Node-RED; this guide adds the hand-off into CODESYS. The three interface choices, with their trade-offs, are compared in barcode scanner input methods to a PLC: serial, USB and Ethernet.

What do you need for this integration?

Four things: the barcode scanner itself, a machine that owns its USB port or switch port and runs Node-RED, a CODESYS application with its OPC-UA server enabled, and one STRING tag marked for symbolic export — the setting that publishes a variable to the server.

On the reader side the interface decides the work. A wired USB handheld such as the LS-HCR-0001 suits a packing bench. A compact fixed reader such as the LS-CRD-0047 reads from 60-120 mm over USB 2.0, which covers a chute or fixture. An Ethernet handheld such as the LS-HCR-0002 talks over Ethernet and skips the USB layer of this build entirely; the last section covers that route.

The reader side, three ways:

Handheld code readersHandheld industrial barcode reader, 1.0 MP, USB 2.0 (LS-HCR-0001)LS-HCR-0001
Resolution
Up to 3 MP
Shutter
Global
$539.00View part
Fixed code readersCompact fixed industrial barcode reader, 1.2 MP, USB 2.0, 60-120 mm reading distance (LS-CRD-0047)LS-CRD-0047
Resolution
Up to 3 MP
Shutter
Global
$559.00View part
Handheld code readersHandheld industrial barcode reader, 1.0 MP (LS-HCR-0002)LS-HCR-0002
Resolution
Up to 3 MP
Shutter
Global
$539.00View part

On the software side you need:

  • Node-RED on the machine that owns the reader's port, with an OPC-UA client node installed from the palette manager.
  • The CODESYS application running, with its OPC-UA server active.
  • The reader's setup codes, if you plan to move it from keyboard mode to USB serial mode.
  • One test label for each symbology you scan in production.

Which capture path should you pick: HID keystrokes, USB serial or Ethernet?

Pick by interface, in this order of preference: an Ethernet reader wins when the hardware offers an RJ45 port; USB serial mode wins on a wired reader you can reconfigure with its setup code; global HID capture is the fallback for a reader fixed in keyboard mode.

Ethernet needs no USB stack, runs 100 m on a standard copper link, and can timestamp the scan at the reader. USB serial hands you the whole scan as one terminated line, with no focus involved — the cleanest capture on a bench. HID works with any keyboard-mode reader, but it needs a small global-keystroke helper and the string arrives character by character, which matters for timing. Serial-mode readers also accept host commands, so if scans go quiet after you send one, barcode scanner serial trigger: fix silent host commands is the diagnosis path. If the runtime side wants a COM-port view of a USB serial reader, map a USB serial port to a COM port inside your PLC runtime covers the mapping. Reader serial settings are commonly 9,600 baud, 8 data bits, no parity, one stop bit.

How does Node-RED write the scan into a CODESYS tag?

Node-RED's OPC-UA client node connects to the runtime's OPC-UA server at opc.tcp://<controller-ip>:4840, browses to the tag's NodeID, and writes the decoded string on every scan; the CODESYS program reads the same variable on its next task cycle.

Set the chain up in this order:

  1. On the machine that owns the reader's port, start Node-RED and install an OPC-UA client node from the palette manager.
  2. Put the reader on that machine: switch it to USB serial mode with its configuration barcode, or plug an Ethernet reader into the switch.
  3. Add the capture node: a serial node bound to the reader's port, splitting each scan on the line terminator, or a TCP listener for an Ethernet reader.
  4. Clean the payload before the write: strip the CR/LF suffix, trim whitespace, and carry the code in msg.payload, the message property Node-RED passes downstream.
  5. Configure the client node with the endpoint opc.tcp://<controller-ip>:4840, the security policy the server expects, and the server's user credentials.
  6. Browse the server's address space from its DeviceSet object down to the application's tag, and copy the NodeID and namespace index into the node's item settings.
  7. Wire each scan message to a write on that NodeID — one write per scan.
  8. In the CODESYS program, copy the incoming tag into a working STRING on value change and raise a new-scan flag for the logic that consumes it.

If you have never browsed a CODESYS information model, start from the DeviceSet object in the server's address space and walk down to the application's tag. Namespace indexes are server-specific, so always copy the NodeID from your own browse result rather than from a shared example flow.

How fast does a scan reach the tag?

The transport needs 15-140 ms for a 14-character scan. On a 9,600-baud serial link the line takes 14 × 10 ÷ 9,600 ≈ 15 ms. A low-speed USB HID reader types it over 14 polls at 8-10 ms each; the runtime adds one task cycle after that.

t_serial = characters × 10 bits ÷ baud rate = 14 × 10 ÷ 9600 bit/s ≈ 14.6 ms t_hid = characters × poll interval = 14 × 1 ms ≈ 14 ms (full-speed USB) = 14 × 8-10 ms ≈ 112-140 ms (low-speed USB)

The serial arithmetic uses 10 bits per byte: one start, eight data, one stop in 8N1 framing. Full-speed USB HID endpoints deliver one key report per 1 ms poll; low-speed readers commonly poll at 8-10 ms, which is where the 140 ms worst case comes from. The reader's own decode adds a few milliseconds for a clean 1D label, and the program sees the value on its next task cycle — a 4 ms task adds at most 4 ms. At one scan per second you send one write per second, so link load is not a design factor at bench rates.

The transport number matters for a second reason: a consumer that reads keystrokes early sees half a string. Line-oriented capture — serial, or the helper's finished-scan event — hands over the complete payload at once, and the PLC-side buffer handling is the one described in storing ASCII barcode strings in PLC memory: bytes, words and clears.

How big should the string tag be?

Leave it as a standard CODESYS STRING, which the runtime allocates at 80 characters: a 14-character EAN-13 scan with its Enter suffix fills 14 of them, so the default holds 80 ÷ 14 ≈ 5.7 such scans with room to spare; go longer only for dense 2D payloads.

The 80-character allocation is CODESYS's standard STRING length, so a bench of EAN-13, Code 128 and Code 39 labels fits many times over. Dense 2D payloads are the exception: the QR specification's maximum is 7,089 numeric digits, which no 255-character STRING holds. For fixed-format labels, cap the payload length in the reader configuration; for free-form 2D codes, declare a longer STRING for that one station and split the write. Code 128 line labels run to tens of characters and stay well inside the default.

How do you stop duplicate scans and empty writes?

Filter in Node-RED before the write: drop payloads shorter than the code's fixed length — 13 digits for EAN-13 — strip the suffix before comparing, and suppress a repeat of the same string inside a debounce window so one label scans once.

A double trigger or a half-presented label shows up as two writes for one physical event, or as a short string. Length-check first: an EAN-13 is exactly 13 digits, so anything else is discarded before it reaches the server. Then compare against the previous payload and drop repeats inside a 500 ms window — a starting value you widen when operators deliberately re-scan the same label. The framing characters that start and end a scan belong to the reader, and prefix and suffix framing for barcode scanner input sets them out. The previous-value comparison is the same pattern as barcode scanner first scan ignored: fix the null previousValue.

How do you verify the scan lands in the program?

Watch the tag itself: scan a known code, confirm the STRING in the runtime's online view matches character for character, check the new-scan flag pulses exactly once per scan, and repeat with the network cable pulled to see the failure mode.

With the application online, run three checks: the STRING matches the label character for character; the new-scan flag gives one clean pulse, with no oscillation while the value stays constant; and the tag updates within one task cycle of the write. Then pull the cable between the two machines and scan again: the client node should report the failed write through its error output instead of queueing it silently, and the program should keep running on its last good value. Reconnect, scan once more, and confirm the next write lands — that is the whole failure catalogue of this layout.

When should you skip OPC UA and wire the reader straight in?

Skip the helper when the reader speaks a protocol the runtime understands natively: an Ethernet/IP reader maps its scan word straight into the PLC's implicit I/O assembly, and a plain TCP reader lands in the runtime's socket interface — no Node-RED box in the path.

Go native when one of three things is true: scans must be stamped inside the PLC, no machine is available to run the helper, or the latency budget is single-digit milliseconds. An Ethernet/IP barcode reader to PLC: map the implicit I/O assembly writes its scan word into the PLC's input image every scan, and a reader without a published protocol still lands in the runtime's sockets, as barcode reader to PLC over raw TCP sockets without EtherNet/IP shows. Keep OPC UA when the reader is USB-only, when several consumers need the same scan, or when you want the hand-off independent of the reader's protocol family; the link-level trade-offs sit in barcode reader PLC communication: plan a reliable link.

If you are specifying the host as well, a vision controller such as MV-VC2140-128G40-1T provides 3-4 LAN ports, 2 USB 3 ports and 8 GB of memory with a 128 GB SSD and a 1 TB HDD, so both pieces of software share one industrial box.

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$1,679.00View partBuild a system with it

For the reader, start at handheld code readers and fixed code readers, filter by interface in the code-reader finder, and add a vision controller if the host belongs in the same order — or send us the layout through the quote form. More integration pieces sit on the code-reading topic page.

Questions

Can the scanner stay in keyboard (HID) mode for this setup?

Yes. A small helper reads the keystrokes globally, with no field in focus, and forwards each finished scan to Node-RED, which writes the tag exactly as before. The cost is one extra process to maintain, and the payload arrives character by character, so serial or Ethernet capture stays simpler whenever you can reconfigure the reader.

What does the OPC-UA NodeID for my tag look like?

It is a string built as ns, the namespace index, then s, the browse name, joined by semicolons. Connect once, browse from the DeviceSet object down to your application's tag, and copy the NodeID and namespace index into the client node's item settings. Namespace indexes differ between runtimes, so copy them from your own server rather than reusing a flow's values.

Does a wireless handheld reader work through the same flow?

Yes, as long as its base station or bridge offers a USB or Ethernet port: treat that port as the reader's interface and capture from it as in the steps. The radio only replaces the cable between scanner and base; everything from the capture process onward is unchanged.

Can one scan update both the PLC tag and an MES record?

Yes. Fan out in Node-RED after the payload cleanup: write the OPC-UA tag for the PLC, then post the same string to your MES over HTTP or queue it to MQTT. One capture feeds both consumers, and the PLC never waits on the MES call.

How do I timestamp each scan at the source?

Form the timestamp in the capture process, where the scan arrives, and write it to a second tag alongside the string. The PLC then records when the scan happened even if its own task was busy, and you can prove sequence order when two stations scan within the same cycle.