Triggering a smart camera from a PLC, a robot or an edge box does not need the vendor software in the loop. Many Ethernet smart cameras and code readers, on top of their own protocol, take plain ASCII command lines over a raw TCP socket: you send a trigger line, the camera answers on the same socket with the result, and any TCP client can drive it — a PLC, a robot controller, Node-RED or a few lines of Python. This guide gives the full round trip: connection, command format, terminator handling, timeout and keep-alive values with the arithmetic behind them, and a loopback test that proves your parser before the camera is even powered.
What you need
- A camera with an Ethernet interface and a command mode over raw TCP. The manual lists the mode (often as a no-protocol mode) and prints the port number next to it.
- A host that can open a TCP client connection: a PLC with socket instructions, a robot controller's socket messaging, Node-RED's TCP-Request node, or a few lines of Python.
- Three facts from the camera's manual: its IP address, its command port and its command table — command words, parameter order, reply fields and the terminator byte.
Our smart cameras and fixed code readers connect over GigE or Fast Ethernet. Choose on optics and cycle time, then take the command mode, port and command words from the chosen model's manual. All three smart cameras here list a GigE interface:
- Interface
- GigE
- Resolution
- Up to 3 MP
- Interface
- GigE
- Resolution
- 6-10 MP
- Interface
- GigE
- Resolution
- 5 MP
Code-only checks run fastest on a dedicated reader, and both readers below connect over Fast Ethernet:
- Interface
- Fast Ethernet
- Resolution
- Up to 3 MP
- Interface
- Fast Ethernet
- Resolution
- 5 MP
The camera finder filters both families by interface, resolution and lens mount.
The round trip in seven steps
- Put the camera in its raw TCP command mode and save the setting, so it survives power cycles. Note the port it prints.
- Open one connection and keep it open. The camera listens as the TCP server; your host connects as the client — the same roles as when you wire a PLC as the client of a reader's TCP server. A persistent socket skips the connect handshake on every part (1-3 ms each) and avoids one-session-per-port limits when old sockets linger.
- Send the command as bytes, terminator included. A trigger is one line of ASCII characters ending in the terminator byte, usually CR, 0x0D. Both lines below are placeholders for the real words in your manual's command table:
host sends: TRIG 1<CR>
camera replies: OK 1 GM9M0452<CR>
- Read until the terminator, not a fixed length. A NO READ line is shorter than a 40-character code. Append every received chunk to a buffer and treat the line as complete only when the terminator byte arrives.
- Parse the fields in the manual's order. Most reply lines carry a status word first, then the read data, then position or score fields. If one shot reads both a code and printed text, the field layout multiplies — reading multiple text strings in one image shows how to number them.
- Act on the status field first. Route NG and NO READ replies as data, never as silence. If the reply carries a score, set the pass band on it rather than on decode success alone — the same judgement as judging whether a decode is trustworthy.
- Repeat on the same socket. Close and reopen only after a timeout or a refused write, not between parts.
Send it from any client
Node-RED
Build the payload as a string that ends with the terminator. An event or Inject node supplies the part or job number, a Function node assigns it to msg.payload as "TRIG 1\r", and the TCP-Request node sends those bytes and returns the camera's reply as a Buffer. Give the node the camera's address, port and a reply timeout from the budget below. A buffer-parse step then splits the reply on \r into fields you can route on. Four nodes — event, function, TCP-Request, buffer parse — are the whole integration.
Python
One function covers the cycle. The host, port and command word are placeholders; take yours from the manual:
import socket
def trigger(host: str, port: int, cmd: bytes = b"TRIG\r") -> str:
# One command-response cycle. Placeholders: host 192.168.1.50,
# port 5000, cmd b'TRIG\r' -- use your camera's values.
with socket.create_connection((host, port), timeout=2.0) as s:
s.settimeout(0.5) # response timeout from the budget below
s.sendall(cmd) # command line + CR terminator
reply = b""
while not reply.endswith(b"\r"): # read to the terminator
chunk = s.recv(1024) # 1024-byte read buffer
if not chunk:
raise ConnectionError("peer closed before a terminator")
reply += chunk
return reply.rstrip(b"\r\n").decode("ascii")
PLC and robot
The PLC sends a byte array of ASCII codes ending in 0x0D and stores the reply characters the same way — storing ASCII barcode strings in PLC memory covers the receive side byte by byte. For the send side as a function block, this walkthrough of sending ASCII commands from a PLC shows the pattern end to end. A robot controller's socket instructions send the identical bytes; parsing several results out of one socket string shows the reply side on a robot.
Timeouts, retries and keep-alive that hold up
Worked example for a code-reading trigger: 2 ms exposure, 45 ms worst-case decode, 1 ms on a clean switched LAN. 2 + 45 + 1 = 48 ms. Ten times that is 480 ms, so set the response timeout at 500 ms; the margin absorbs a stray slow decode without stalling the cell. Set the connect timeout at 2 s, which rides out a camera rebooting at the far end.
Once the cell runs, log send and receive timestamps. The worst reply time over 100 cycles replaces this estimate with a measurement.
An idle socket can die for an hour without either side noticing: typical stacks send the first keep-alive probe only after 7,200 s (two hours) of silence. Poll a status or version command every 10 s while the line is idle. Two failed polls plus two 500 ms timeouts means the link is down: 2 × 10 s + 2 × 0.5 s = 21 s from failure to reconnect. The wider plan — addressing, switch ports, behaviour on a power cut — follows the same rules as planning a reliable reader-to-PLC link.
Retry once after a 500 ms timeout, then raise a fault upward. Gate that retry on part state:
If triggers misfire while the socket is busy, that is usually a one-shot capture race rather than a link fault — why one-shot capture misses frames walks that failure down.
Does per-part TCP keep up? Take one part every 800 ms, a 48 ms worst-case reply and 5 ms of PLC task time: 48 + 5 = 53 ms, or 6.6% of the 800 ms pitch. Reply traffic is about 80 bytes per part: 80 B × 1.25 parts/s = 100 B/s, noise on any switched network. Below roughly a 100 ms pitch, per-part command-response starts to eat the cycle budget; move to the camera's hardware trigger input and a push output mode.
Command-response or push: match the direction to the cell
Command-response — the pattern this guide covers — is the right shape when the host decides when to inspect: a pick station, a verification gate, a re-check after an alarm. The mirror shape is push: the camera connects out to a listener on the host and publishes every result unprompted, which is what sending OCR strings over TCP describes from the camera's side. When the line triggers itself from its own sensor, push removes the host round trip entirely. If your controller prefers to read results as data rather than parse a socket, smart camera results over OPC UA replaces the parser. To match roles on either side, use TCP client or listener: match your reader to your host.
Common mistakes
- LF instead of CR. The camera ignores a line with the wrong terminator byte and stays silent. Log what you send in hex and check the last byte first.
- A new connection per part. Connect handshakes add 1-3 ms per cycle, and a lingering old socket can trip the one-session-per-port limit on the next connect.
- Fixed-length reads. A 40-character code, a short serial and a NO READ all differ in length; only the terminator marks the end.
- Images expected on the command socket. Result lines are text. Pictures go to the camera's separate image upload or output service, in parallel.
- Tuning over TCP. Command words select and trigger saved jobs; thresholds, ROIs and decode settings are edited in the setup tool and saved into the camera.
Prove it on the bench, then in the cell
Run the client against a loopback listener first — no camera needed, same script as the function above:
import socket, threading
def listener(): # stands in for the camera
srv = socket.create_server(("127.0.0.1", 5000))
conn, _ = srv.accept()
assert conn.recv(64) == b"TRIG\r" # command arrived with its CR
conn.sendall(b"OK\r") # the reply the parse loop expects
threading.Thread(target=listener).start()
assert trigger("127.0.0.1", 5000) == "OK" # loop closed on the terminator
Then bench the real camera: fire 100 triggers at a known-good part with send and receive timestamps in the log. The pass mark is 100 replies inside the 500 ms timeout and zero reconnects — enough cycles to exercise the split-packet path. Only then wire the same bytes into the running machine.
Where to go next
Pick the camera on optics, resolution and interface in the camera finder, or step through the whole station with the configurator below. If a reply still will not parse, send us the raw bytes from your log and we will read them with you — contact. More integration guides sit on the code-reading topic page.
The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.
Questions
Does the vendor setup software have to run for the TCP link to work?
No. The command mode is served by the camera's own firmware: once the mode, IP address and inspection jobs are saved into the camera, the setup tool can be closed and even unplugged. The software matters only for creating and tuning jobs. Keep a copy of the job file with the panel documentation so a replacement camera can be loaded and back on the socket quickly.
What comes back over the command socket — just a string?
Result lines carry text: a status word, the decoded data, and position or score fields. Images do not travel on this socket. If you need the picture for an archive or a re-review station, run the camera's separate image upload or output service in parallel, and leave the command socket lean so the reply always lands inside the timeout budget.
Can two hosts share one camera over TCP?
Usually not on the command port: these cameras typically accept a single client per command service, and a second connect is refused or bumps the first session. Run one owner of the trigger socket and give everyone else the results indirectly — fieldbus, OPC UA, or a separate publish port if the camera's manual lists one. A small broker on the PLC works when two clients both genuinely need to command the camera.
Why does a reply sometimes arrive in two packets?
TCP is a byte stream, not a message service. The camera writes one line, but the stack may segment it anywhere, and the previous line can still be sitting in the buffer when you read. Nothing is wrong: read continuously, append to a buffer, and split on the terminator. Parsers that treat every receive call as one complete message work on the bench and fail in production.
Can the same socket change inspection settings?
Treat the command set as run-time control: trigger, select among saved jobs, read status and results. Editing thresholds, ROI geometry or decode parameters belongs in the setup tool, saved into the camera. That split keeps the PLC interface stable across job revisions — the socket commands never change when a job is retuned, only which job number the host selects.




