Connect an FTP Smart Camera to Roboflow and a PLC A Python bridge application called the VHV5 Viewer lets FTP/SFTP-only smart cameras such as the Omron VHV5 feed images into a Roboflow RF-DETR Workflow for scratch and dent detection on 3D-printed shock towers, then writes a single pass/fail status word to an Omron NX102-9000 PLC over OPC UA in about 800 ms per part on a local GPU. The Viewer acts as an FTP/SFTP server on the camera side, a Roboflow client in the middle and an OPC UA client on the PLC side, because Roboflow does not receive images over FTP and Workflow blocks only run when called. The setup runs on a Ryzen 9 3950X PC with 64 GB RAM and a Quadro RTX 4000 (8 GB) using the Roboflow Inference Docker container, and also reports missing parts, unreadable QR tags and inference failures. You can add AI defect detection to smart cameras that only send images over FTP or SFTP, like the Omron VHV5. A small Python bridge receives each image, runs it through a Roboflow RF-DETR Workflow that finds scratches and dents on 3D-printed parts, and writes one status word to an Omron NX102 PLC over OPC UA, in about 800 ms per part on a local GPU. This guide shows how to build a 3D print defect detection system with an FTP camera, Roboflow, and a PLC. The system inspects finished 3D-printed shock towers for scratches and dents, then sends an inspection result to an Omron NX102 controller over OPC UA. The connection starts with a small Python application called the VHV5 Viewer. An Omron VHV5 barcode reader captures each part in a fixture and uploads its image over FTP or SFTP. The Viewer sends that image to a Roboflow Workflow running in the cloud or locally in a Docker container, then writes the result to the PLC. Operators see the verdict in the Viewer and on indicator lamps at the station. We’ll walk through the camera setup, defect detection model, Workflow logic, and PLC integration. Along the way, we’ll show how consistent lighting and positioning support inspection, how detections become a compact status word, and how the application reports missing parts, unreadable QR tags, and inference failures. What Defects Can This System Detect? This project uses computer vision to detect scratches and dents on finished 3D-printed shock towers. A camera captures each part in a fixed inspection fixture, and a trained Roboflow model locates visible defects in the image. The application converts those detections into a pass/fail result that an Omron PLC uses to control indicator lamps. The inspection checks for two defect classes: Scratch: Visible surface marks identified by the model. Dent: Localized surface damage identified by the model. The Workflow also checks whether a part is present and whether its QR tag can be read. These checks prevent an empty fixture or an unreadable serial number from being treated as a successful inspection. Hardware and Software for 3D Print Defect Detection The project has three layers, and any one can be replaced without touching the other two. Hardware: Everything sits on one Ethernet switch and one subnet. With inference running locally, no image leaves the site. Component Model Role Barcode reader Omron VHV5 VHV5-F000L050M-SRX-D Captures each part and reads the QR tag Machine controller Omron NX102-9000 Decodes the result and drives the lamps I/O units ID3344 inputs , NX-OD4256 outputs E-stop and trigger button in, three lamps out Power supply S8VK-X09024A 24 VDC Ethernet switch W4S1-05D 5 ports, one cable per device Inference PC Ryzen 9 3950X, 64 GB RAM, Quadro RTX 4000 8 GB Runs the Viewer app and the Roboflow Inference container Software: Omron's VHV5 WebLink, a browser tool, configures the camera. The VHV5 Viewer, a custom Python app extended for this project, is the bridge between the camera, Roboflow and the PLC, and it logs every part to CSV. AI: A Roboflow RF-DETR opens in a new tab Small object detection model inside a Roboflow Workflow. The same Workflow runs on the Roboflow Serverless Cloud API or in a Roboflow Inference Docker container on the GPU PC, and the Viewer can send each image to either. Connect an FTP Camera to Roboflow Roboflow's camera integrations are built around streams such as RTSP, USB and GigE Vision, and Roboflow doesn't receive images over FTP. The VHV5, like many smart cameras and barcode readers, can upload files into FTP or SFTP servers. A custom Python block in a Roboflow Workflow can't stand in for that server: blocks run only when the Workflow is called, but something needs to be listening to the camera at all times to get the images. So the Viewer app plays three roles: an FTP and SFTP server on the camera side, a Roboflow client in the middle, and an OPC UA client on the PLC side. On the Camera Two settings in WebLink do the job. The FTP settings Device → TCP/IP Settings point at the PC's IP address with a username, and the cycle archive output Setup → Output is set to FTP Server. After every cycle, the camera uploads two files: the PNG image and a JSON report with its own result for that cycle. On the PC FTP and SFTP in one app. The Viewer runs an FTP server on port 21 and an optional SFTP server on port 22, built on the pyftpdlib and paramiko libraries. SFTP encrypts the transfer and can authenticate with the camera's SSH key: WebLink exports the camera's public key, and the Viewer imports it. Inspect on arrival, pair before deciding. The image goes to the Workflow the moment it lands. Over FTP the image arrives before the report; over SFTP the report comes first. The Viewer accepts either order and builds the verdict once both halves are in. One HTTP call per part. The PNG goes to the Workflow as base64 in a single POST to /infer/workflows/{workspace}/{workflow id}, either on the Roboflow Serverless Cloud API https://serverless.roboflow.com or on the local container http://localhost:9001 . Python's standard library is enough. Never block the upload. Inference runs on a worker thread, never on the FTP thread, so a slow call can't hold up the next part. OPC UA from the same app. The Viewer is also the OPC UA client to the PLC, built on the asyncua library. It runs on its own background thread, reconnects if the PLC drops, and puts a timeout on every write, so an offline PLC can't stall the camera side. The tags it writes are covered in the OPC UA section below. The body of the Workflow request is small: JSON { "api key": "...", "inputs": { "image": { "type": "base64", "value": "