Your PLC code belongs in Git — a tour of Nautilus

PLC code stored natively in Git, with the tests and deployments software teams take for granted.

Traditional PLC version control is a folder: Main.ACD, Main_backup.ACD, Main_Jimothy_Fixed.ACD — a new suffix every time somebody remembers to click Save As. Comparing two of them means the vendor's compare tool, and git diff on a binary project file returns one gray line that tells you nothing.

I've built control systems for nearly 20 years, starting with traditional proprietary tools and migrating toward bespoke industrial software. Nautilus is what I built so I could develop SCADA applications the same way I develop software applications.

What it is

Nautilus is open source (Apache-2.0) and has three parts:

  • a soft PLC runtime written in Go — a deterministic scan loop that executes IEC 61131-3 logic,
  • a VS Code extension with graphical editors for the IEC 61131-3 languages,
  • an HMI component library for building operator screens in SvelteKit.

It runs on general-purpose hardware and is programmed with open-source tools. No vendor lock-in, no tool licenses. All code is stored natively in git, which buys the things software teams take for granted: tests gated by continuous integration, and deterministic deployments via continuous delivery. As the software toolchain improves, so does your ability to program PLCs.

Install and scaffold

With Go installed, installation is one command:

go install github.com/joyautomation/nautilus/cmd/nautilus@latest

The scaffolder asks what you want in the project: a full-featured demo, a minimal starting point, or projects designed to be extended through the Go SDK, and which language your main program should use: structured text, ladder, function block diagram, or sequential function chart.

nautilus new demo-tank --template minimal

The result is a runnable project: the nautilus.yaml configuration file, test files, and your main program as a plain text file.

It's running

cd demo-tank
nautilus run

The built-in web dashboard shows runtime metrics: scan time, task execution statistics, jitter, scan count, and current tag values. On my machine, the scan runs with less than half a millisecond of jitter and an execution time under 0.1 ms, and the dashboard breaks the last scan into its phases: input read, logic execute, output write.

Tag values can be written from the tag table, and there's a simple REST API for getting data in and out:

curl -X POST localhost:8080/api/tags \
  -H 'Content-Type: application/json' \
  -d '{"name":"Setpoint","value":65}'

The setpoint updates immediately; the actuator follows on the next scan. That page isn't an HMI — it's a window — but the same HTTP API that feeds it would feed one.

The editor

Control logic is edited in VS Code with the Nautilus extension. The IEC 61131-3 programs are stored in a text format we designed for exactly this: .st, .ld, .fbd, .sfc and VS Code recommends the extension the moment it sees one of those files.

With a controller running, live values appear inline next to each variable like being online with a traditional controller, except there's no Go Online step. A language server highlights errors as you type, the same as any mainstream language: typo a tag name and the squiggle appears immediately, with the diagnostic on hover and in the Problems panel.

Online edit, with a safety net

Change the logic while the controller runs and the extension tells you the workspace differs from the controller, in the status bar. The Download Program to Controller command warm-swaps the program retained state carries across, and the scan counter runs straight through the download. If an edit would fault the runtime, the download is refused and the running program is untouched. There's a rollback command for the last running version, too.

The payoff

The logic change you just made is a text change, so:

git diff

just works and so does everything downstream of it: history, review, branching, pull requests. nautilus pull --check compares your offline code against what the controller is actually running; nautilus pull brings a field edit back into the repository instead of losing it. And every language has a diff view in the editor, so you get the graphical comparison of traditional tools offered directly in your IDE.

How far it goes

The heated-tank-nogo example is the same idea at full size: a heated surge tank with four tasks at four scan rates — control at 10 Hz, reports at 1 Hz, an annunciator at 5 Hz, and a simulated plant. The control loop is a function block diagram, the interlocks are ladder, the plant physics are structured text. Each graphical editor edits the underlying text file, which is what makes graphical and text diffs possible. (The SFC batch example in that repo is borrowed from another project to show the editor — the tank itself doesn't run a sequence.)

Ship it

nautilus build

produces a single self-contained binary (21 MB in this case) that runs your logic on any general-purpose computer. And if you run many nearly-identical sites, you don't fork the project: the program is shared, each site differs by its own manifest, and nautilus build -m gives you a unique binary per site.

There's also an HMI toolkit: SvelteKit components fed by the runtime's live tag stream, with the layout tooling you'd expect from a SCADA package.

What it isn't

Nautilus is not a safety controller, but for the majority of industrial use cases it offers good reliability and determinism.

Docs and the quickstart: https://nautilus.joyautomation.com · Source: https://github.com/joyautomation/nautilus

Joy Automation is a system integrator, not a vendor. If you’d like help implementing a system on Nautilus, our services (https://joyautomation.com) run from assessments to full software and hardware projects.