Work / picw-overview

picw-overview: Raspberry Pi 4B checkweigher controller, no Mitsubishi PLC

Replacing a Mitsubishi PLC with a Raspberry Pi 4B as master of a dynamic checkweigher (Thames Side T16 / XT1-SO)

Open Source · 2026 · Active

Source on GitHub ↗ · Need one like it? →

How does it work?

Replacing a Mitsubishi PLC with a Raspberry Pi 4B as the master of a dynamic checkweigher.

Load cell: Thames Side T16 (viscous damped, 5–35 kg). Transmitter: Thames Side XT1-SO (24-bit, 2400 readings/s, RS485 Modbus RTU). Throughput: 40 items/min.

Source is in a private repository. This page is an overview.

Why replace the PLC

The PLC is scan-cycle bound, can't hold product recipe tables or item history, and has no capacity for signal processing on the weight trace. None of that is fixed by a faster PLC.

Safety

The E-stop and guard interlocks are hardwired through a safety relay that removes power from the conveyor motor and pneumatic actuators directly. The Pi reads E-stop state for display, logging, and interlocking its own outputs. It is never the only element between an operator and a moving part. No part of this software is a safety function.

Regulatory position

Internal quality control only. The machine is unstamped and holds no OIML R51 approval as an automatic weighing instrument — the XT1000's OIML R76 certificate covers non-automatic weighing and approves the transmitter as a module, not the machine.

So there is no legal metrology constraint. If the machine is ever brought into a legally controlled application, the design must be revisited first.

Architecture

One service plus a kiosk browser. The control loop runs on a dedicated thread and is never driven from an HTTP handler — a slow request must not be able to stall a reject.

Machine            one tick drives everything
 ├── RTLoop        edges, item queue, desync detection, reject, jitter
 ├── ScaleService  XT1000 over Modbus RTU, reconnect with backoff
 ├── Scanner+Binder  per-item binding, product changeover
 └── Store         products, items, events, audit  (SQLite WAL)
        ↑ ticks on its own thread
FastAPI  REST + WebSocket  →  chromium --kiosk

Every hardware boundary is a protocol with two implementations:

Dual verdict

The XT1000 classifies in hardware from 2400 readings/s and drives its setpoint outputs. The Pi independently classifies from the streamed weight with its own filtering. Both are logged per item; a config key picks which drives the rejector, defaulting to the XT1000.

The point is evidence: prove the Pi's classification is actually better on this machine with this product before trusting it with reject decisions.

Roles, not pins

Runtime code binds to role names — item_entry, reject_actuator, weight_ready — never to GPIO numbers. The setup wizard maps physical channels onto roles, and the machine cannot leave setup mode until every required role is bound exactly once.

Queue desynchronisation

The failure mode the design is built around. If the entry photo-eye counts an item the exit eye misses, the FIFO shifts by one and every subsequent item inherits the previous item's verdict — silently rejecting good product and passing bad. Each queued item carries an expected arrival window derived from belt speed and distance; an exit edge matching no window is treated as a desync, not an item. Default response is to stop the belt.

Status

Complete and running.

327 tests · 90% coverage · ruff clean · mypy strict clean

Real-time core, Modbus driver for the transmitter, SQLite storage with product recipes and an append-only audit log, barcode binding and product changeover, REST + WebSocket API, touchscreen HMI and setup wizard, one-line installer, systemd units, kiosk browser, Samba export, and WiFi priority management.

Every hardware boundary has a simulated implementation behind the same protocol, so the whole machine — packages, weights, barcodes, rejects, faults — runs on a Raspberry Pi with nothing attached.

sudo bash deploy/install.sh      # simulated mode
sudo bash deploy/install.sh --hardware

Interface

Five tabs sized for a 10" panel: Run (live weight, throughput, verdict conflicts, jitter, simulator with fault injection), Products (recipe editor with barcode binding), I/O (live input LEDs, guarded output forcing), Setup (role bindings, belt geometry, serial scan with generated udev rules), Diagnostics (jitter histogram, events, audit).

Vanilla JS and CSS — no build step, no npm, no CDN, because it runs in a kiosk browser on a read-only root.

Setup wizard

The commissioning tool, reachable from the HMI and re-runnable step by step as a diagnostic. Serial auto-detect sweep across baud/parity/address; live sensor state indicators you can trip by hand; guarded output forcing; reject-delay learn mode; product recipes bound to barcodes; and a supervised trial run reporting verdict disagreement, reject confirm rate, throughput, and the jitter histogram that decides whether the Python real-time approach holds.

Design notes

reject path and wears out.

failure for Pi appliances.

Stack

Python 3.11 · pytest · ruff · mypy (strict) · libgpiod · FastAPI · SQLite · systemd

What do people ask about it?

Why replace a Mitsubishi PLC with a Raspberry Pi on a checkweigher?

The PLC is scan-cycle bound, cannot hold product recipe tables or item history, and has no capacity for signal processing on the weight trace — and a faster PLC fixes none of that. A Raspberry Pi 4B runs the control loop on its own dedicated thread, never from an HTTP handler, keeps products, items, events and an audit trail in SQLite, and still serves the operator's kiosk screen.

Which load cell and transmitter does it read?

A Thames Side T16 viscous-damped load cell (5–35 kg) and a Thames Side XT1-SO transmitter: 24-bit, 2,400 readings a second, over RS485 Modbus RTU with reconnect and backoff. Throughput is 40 items a minute.

Is the Raspberry Pi a safety device?

No. The E-stop and guard interlocks are hardwired through a safety relay that removes power from the conveyor motor and the pneumatic actuators directly. The Pi reads the E-stop state for display, logging and interlocking its own outputs; it is never the only element between an operator and a moving part, and no part of the software is a safety function.

Is it legal for trade — OIML approved?

It is internal quality control only. The machine is unstamped and holds no OIML R51 approval as an automatic weighing instrument; the XT1000's OIML R76 certificate approves the transmitter as a module for non-automatic weighing, not the machine. A legally controlled application would mean revisiting the design first.

Can it run without the hardware?

Yes. Every hardware boundary is a protocol with two implementations — GPIO through libgpiod or a simulated I/O, the Modbus scale or a simulated one, the serial scanner or a simulated one — so the whole machine runs on a laptop for development and tests.

What else did I build?

View all →

GXBridge-MCP

Control Mitsubishi GX Works3 and your PLC with AI — an MCP server for any agent GX Works3 · Industrial Automation

eplanbridge

Drive EPLAN Pro Panel 2022.0.3 from outside the application — and build a 6-axis robot-arm control schematic with it. C# · EPLAN

tovex-cli-showcase

tovex-cli: command-line control of Siemens TIA Portal and live S7 PLCs, built for AI agents. Public overview; source is private.

/ Work / {{ project.name }} {{ project.category }}

{{ project.name }}{{ project.headline }}

{{ project.tagline }}

{{ m }}

What is it?

{{ project.description }}

npm i {{ project.npm }}

What does it do?

{{ f.n }}

{{ f.text }}

What is it made of?

{{ t }}

How does it work?

{{ b.s }}

{{ b.s }}

{{ b.s }}

{{ b.s }}
{{ b.s }}
  • {{ it }}

Read the full README on GitHub →

What do people ask about it?

{{ f.q }}

{{ f.a }}

What did I write about it?

What else did I build?

View all →

/ Work

Where did
it go?

That project doesn't exist. Head back to the full list.