System designHow-to

Barcode string disappears after one PLC scan: latch it on the pulse

A barcode reader's result string lasts one PLC scan. Copy it into a latched buffer on the read-complete pulse and set the idle timeout for your baud rate.

LumaSpec Vision11 min read Share on LinkedIn

On this page 8 sections
  1. 01Why does the barcode string vanish after one scan?
  2. 02What do you need in place before you change the logic?
  3. 03How do you latch the string on the read-complete pulse?
  4. 04How long should the inter-character timeout be at your baud rate?
  5. 05How do you prove the latch holds before the line runs it?
  6. 06Which mistakes bring the disappearing string back?
  7. 07When should the reader hold the code instead of the PLC?
  8. 08Questions

A serial code reader hands the PLC a finished string and a one-scan flag, and then the string is gone. Check the buffer a scan later and you find cleared characters or a placeholder, while the part moves on unrecorded. The fix is a few lines of logic: copy the whole string into your own latched buffer on the rising edge of the read-complete pulse, raise a new-data flag, and let the consumer clear it. The sections below cover the tags, the structured text, the idle-timeout arithmetic for your baud rate, and the bench test that proves the latch holds — for fixed readers on the line and handheld readers at a bench alike.

Why does the barcode string vanish after one scan?

The read-complete output is a pulse, not a state: it is TRUE for the single scan in which the block finished receiving. Then the block clears the raw buffer or fills it with a placeholder, so any code that reads it a scan later finds nothing.

The behaviour is deliberate. A serial read routine piles characters into its own work buffer, and only when the frame is complete does it publish the string, pulse the done output and re-arm for the next code. The published string belongs to the block, not to you, so treat it as valid for exactly one scan.

The timing makes the loss easy to miss. At a 2 ms scan cycle the pulse exists for 2 ms, so an HMI polling every 100 ms overlaps it about 2 times in 100. The string is also a moving target mid-transfer: a 9-character frame (an 8-digit code plus CR) carries 9 × 10 = 90 bits with 8N1 framing, which is 90 / 9600 ≈ 9.4 ms at 9600 bit/s, or about 5 scans at 2 ms. A per-scan read catches fragments; a read one scan late catches the reset.

How the characters land in that receive buffer, nulls included, is its own subject — ASCII barcode data framing in the PLC receive buffer walks through it. The decision here is absolute: touch the raw output only inside the done scan, and let every other consumer read your latch.

What do you need in place before you change the logic?

You need the reader's frame format (terminator, prefix, maximum length), a string tag and three BOOL tags of your own, a read block or port call that exposes the finished string with its one-scan done flag, and a consumer able to take the string when told.

Read the frame format out of the reader's manual: terminator (CR, CR/LF or none), any prefix it prepends, and the longest string it can emit. A reader that ends frames with CR/LF feeding a block that stops at CR leaves a stray LF to open the next frame, which surfaces later as phantom characters. If a preamble must be stripped on capture, barcode scanner capture without a prefix covers that side, and storing ASCII barcode strings in PLC memory shows the byte and word layout the latch below writes into.

If the PLC side is a legacy serial port, pick a reader that speaks serial; the category's interface filters split them cleanly. The MV-ID6200PM-00C-NNG is one of the logistics readers on that interface.

Fixed code readersHikrobot MV-ID6200PM-00C-NNG 20 MP logistics code reader, high performanceMV-ID6200PM-00C-NNG
Interface
Serial
Resolution
16-25 MP
Sensor format
1"
Shutter
Global
$5,699.00 · quotedView and quote

Modern cells more often carry the string over Ethernet or USB — most models in the category connect over Fast Ethernet or USB — and these three cover both ports:

Fixed code readersHikrobot MV-ID3013PM-12M-WBN 12mm focal length 1.3 MP Compact Industrial Code ReaderMV-ID3013PM-12M-WBN
Interface
Fast Ethernet
Resolution
Up to 3 MP
$1,979.00View part
Fixed code readersHikrobot MV-ID813M-05S-WBN-SR 1.3 MP resolution, 4.9 mm focal length, White light, SR, GigE, Static cableMV-ID813M-05S-WBN-SR
Interface
GigE
Resolution
Up to 3 MP
$719.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

The MV-ID3013PM-12M-WBN reads with a 12 mm focal length at 1.3 MP over Fast Ethernet, the MV-ID813M-05S-WBN-SR pairs a 4.9 mm focal length with its own white light on GigE, and the LS-CRD-0047 works at a 60-120 mm reading distance over USB 2.0. Filter the whole category by interface in the code reader finder. For bench and rework stations, handheld code readers feed the PLC the same way over USB or Fast Ethernet.

How do you latch the string on the read-complete pulse?

Detect the rising edge of the done pulse and, in that same scan, copy the whole raw string into your own buffer and raise a new-data flag. The copy then survives every later scan until you clear it, which is the whole fix.

Set the tags up first:

  1. From the read block: ReadDone : BOOL, the one-scan done pulse, and CodeRaw : STRING(64), the raw string as received.
  2. Of your own: ReadDonePrev : BOOL := FALSE, CodeLatch : STRING(64) := '', CodeNew : BOOL := FALSE and CodeTaken : BOOL := FALSE. The names are illustrative; keep your project's conventions.
  3. Place the copy network immediately after the read block call, and nowhere else in the project.
  4. Have the consumer — robot, HMI, MES writer — set CodeTaken once it has taken the string, and strip the terminator where you use the value; trim a substring from a barcode scan string shows the string handling.

The copy itself is two short networks of structured text:

IF ReadDone AND NOT ReadDonePrev THEN
    CodeLatch := CodeRaw;      // whole string, terminator included
    CodeNew   := TRUE;         // one fresh-code event per read
END_IF;
ReadDonePrev := ReadDone;      // age the pulse by one scan

IF CodeTaken THEN
    CodeNew   := FALSE;        // consumer resets the handshake
    CodeTaken := FALSE;
END_IF;

CodeNew is the only state the rest of the machine may act on. It rises with the copy and falls only when the consumer confirms, which turns a 2 ms pulse into a state a 100 ms poller can catch. For capture that lives in the HMI instead of the PLC, USB barcode scanner on a PLC HMI sorts out which side owns the string.

The edge detector costs one BOOL and stops double counts: if the block ever holds its flag for two or three scans, the edge keeps a single read from being copied and counted several times. Leave the terminator inside CodeLatch, so a raw log always matches what the wire carried; presentation is the consumer's job.

How long should the inter-character timeout be at your baud rate?

Set the idle gap that ends a frame to at least 20 character times, which is 20 × 1.04 ms ≈ 21 ms at 9600 bit/s. Keep the default 100 ms there because it carries almost five times that minimum.

t_char = 10 bits / baud (8N1: 1 start + 8 data + 1 stop) t_idle ≥ 20 × t_char (end-of-frame idle gap) t_frame = (L + 1) × t_char (L = characters in the code)

A reader sends string characters back-to-back with gaps well under two character times, so an idle gap of 20 character times can only be the end of the frame. The rule scales with the port, and this table is the whole decision:

Baud rate (bit/s) Character time 20-character minimum Working setting
1200 8.33 ms 167 ms 200 ms
9600 1.04 ms 21 ms 50 ms
38400 0.26 ms 5.2 ms 10 ms
115200 0.087 ms 1.7 ms 5 ms

With a terminator ending every frame, the timeout is only a fault guard and never adds latency, which is why 100 ms at 9600 bit/s is fine as shipped. Without a terminator the timeout becomes the frame detector and lands straight in the scan-to-flag budget, so use the working column and keep it no longer than the application can wait — barcode scanner to PLC response time counts that budget line by line.

Decision: 200 ms at 1200 bit/s, 50 ms at 9600, 10 ms at 38400 and 5 ms at 115200; one setting per project, written down, and re-checked with the bench test below.

How do you prove the latch holds before the line runs it?

Force the pulse once by hand and watch the buffer for a full minute: with CodeRaw held at 06789121 for one scan of a 2 ms cycle, CodeLatch must still read 06789121 after 30,000 scans, and a new-code counter must step from 0 to exactly 1.

Run the bench sequence:

  1. Disconnect the reader and force ReadDone TRUE for exactly one scan with CodeRaw := '06789121'.
  2. Watch CodeLatch for 60 s. A minute is 60,000 ms, or 30,000 scans at 2 ms; the string must still read 06789121 at scan 30,000, with no placeholder anywhere.
  3. Add a counter, call it NewCount, inside the copy branch, and confirm it stepped from 0 to exactly 1.
  4. Trigger a real second read of a different code, 08912343 say: CodeLatch must update and NewCount must reach 2, which proves the edge fires per read and not per level.
  5. Run 100 parts past the reader. NewCount must equal 100 and must match the reader's own read counter; a gap means pulses are being missed or doubled.
  6. Repeat the forced pulse with the port still unplugged and confirm nothing latches — a failed read must leave the machine in a defined no-code state.

Ship the logic when 100 parts produce 100 counter steps and the minute-long hold passes; before that, it is not proven.

Which mistakes bring the disappearing string back?

Five mistakes account for nearly every relapse: copying outside the pulse scan, clearing the buffer in the same branch that sets it, using the done pulse as a downstream validity condition, skipping the edge detect, and shortening the idle timeout below the character times your baud rate needs.

  • Reading CodeRaw outside the pulse scan. A network later in the cycle, a second task, an HMI tag read — all of them see the re-armed buffer. The only legal read of the raw string is inside the edge branch.
  • Clearing the latch in the branch that sets it. Copy-then-clear in one pass hands the consumer an empty string; the clear belongs to CodeTaken or to the next trigger.
  • Wiring downstream actions to the pulse. The reject gate and the robot run their own cycles, and by the time they look, the pulse is gone; they read CodeNew, never the pulse.
  • Skipping the edge when the block holds its flag for several scans. One read, three copies, three counter steps, and the counts no longer match the parts.
  • Shortening the idle timeout below the table's minimum. The frame splits mid-string, and the symptom returns as garbage characters instead of an empty buffer.

Every one of these has the same root: someone trusted the pulse to last longer than one scan. On any PLC, it does not.

When should the reader hold the code instead of the PLC?

Hand the job to the reader when the PLC cannot keep up or the decision is not the PLC's to make: match verification, retry on a bad read, and buffering a burst of codes all run better inside a fixed reader, leaving the PLC a single confirmed result per part.

Match verification — does this code equal the order the MES expected — belongs in the reader, which can re-trigger and re-read within milliseconds while the PLC would need a fresh trigger cycle; barcode match verification: build a pass/fail check station builds exactly that case. Trigger hygiene is the other half: fixed barcode reader trigger modes sets sensor, serial or free-run triggering, and trigger a code reader from a robot program and confirm the scan closes the loop with a handshake — the reader-side twin of the CodeNew flag above.

Even then, keep the latch. When a read comes back wrong, the PLC-side copy of the raw string is the evidence that separates a mis-decode from characters lost on the wire.

Decision: latch in the PLC always; move verification, retry and burst buffering into the reader only when read rates or match logic outgrow the scan cycle. More string-to-PLC patterns sit on the systems topic page, and if a port still drops frames after all this, tell us what your port receives — the culprit is usually one timeout or terminator setting.

Questions

Does the same latch work when the reader sends codes over Ethernet instead of serial?

Yes. The pulse-and-copy pattern is identical; only the receive call changes. A TCP payload usually lands whole, so the done flag often sets in a single scan even more cleanly than on serial, but you still copy on its edge and still handshake the consumer. Size CodeLatch for the longest code the line can produce before you go live.

How large should the latched string buffer be?

Reserve the longest string the line can emit plus the terminator. Plain part numbers fit in 32 characters, but concatenated GS1 codes with several application identifiers run well past that, so STRING(64) is the safe default. A copy into a shorter tag truncates silently, so guard the copy with a length check and let an oversize code raise a fault instead of passing trimmed.

Does the latch logic work for a handheld reader too?

Yes, whenever the handheld feeds the PLC over its serial or network port: the done pulse behaves the same, and only the trigger source changes, a person instead of a photoelectric sensor. Handhelds that connect as a keyboard wedge bypass the PLC entirely and never raise a pulse, so for those the capture happens in the HMI, not in your logic.

Why did my string variable show an asterisk after the read?

The read block had already re-armed. Some read blocks fill their output with a placeholder character once the one-scan window has passed, and the asterisk you saw is that placeholder, which is exactly the disappearing-string symptom. After the fix you should never see it; if it returns, the copy has drifted out of the pulse scan or a second task is overwriting the buffer.

Is it safer to clear the latched string after every part?

Keep the string, gate the action. Clearing CodeLatch each cycle is the safest habit on machines that act on the code, because a stale code can never pass twice; for traceability you can keep the last code in a separate history tag. Either way, no downstream action may run without CodeNew set, because that flag, not the string, is what says the code is fresh.

What happens when two codes arrive back to back?

Each completed string raises its own done pulse, so the edge logic latches both in order. Trouble starts when pulses arrive closer together than the consumer's cycle: the second CodeNew overwrites the first handshake before anyone reads it. If that can happen on your line, replace the single buffer with a small FIFO queue sized to the worst burst and let the consumer drain it at its own pace.