DEV Community

Cover image for AI Writes the PLC Code. Proving It Works Is the Hard Part — 7 Free Hands-On Lessons
tia-forge
tia-forge

Posted on Originally published at hubwiz.com

AI Writes the PLC Code. Proving It Works Is the Hard Part — 7 Free Hands-On Lessons

The missing piece in AI-assisted PLC programming isn't code generation — it's proof. PLC AI 实战 is a free, seven-lesson, text-and-screenshots course that breaks that problem into seven checkable endpoints: a compile with 0 errors and 0 warnings, a read-back that matches the spec, a scenario report that says PASS/FAIL, and a regression run that shows behavior didn't change. No videos, no course upsell, and nothing to download.

I went through all seven lessons and pulled out the parts that matter if you already write PLC code for a living — plus the three failures the course deliberately shows instead of hiding.

1. The bottleneck moved from "write it" to "prove it"

Almost every engineer's first reaction to AI-generated SCL is the same: looks about right, but I don't dare use it.

"Looks about right" comes from the model's strengths — interfaces, variable names, IF/THEN structure. "I don't dare" comes from the plant floor: a wrong PLC program doesn't cost you a compile error, it costs you a machine doing the wrong thing.

The course is valuable because it shows three real failures rather than a demo where the AI gets it right the first time:

  1. Compiles clean, spec not implemented. The requirement said "after the sensor triggers, output for 2 seconds." The implementation used TON. The sensor is only high for one scan cycle, so the timer's ET never reaches 2000 ms and the output never turns on once.
  2. Compiles clean, nothing moves. While refactoring a three-motor delayed-start sequence, TP was written as TON. Compile: 0 errors, 0 warnings. Press the button: none of the three motors start.
  3. Compiles clean, the field never reads it. When changing an IO address, the TIA compiler does not check whether a physical channel exists. Point a tag at I30.0, which this project doesn't have, and you still get 0 errors and 0 warnings.

All three have the same shape: the compiler cannot see them. So the only defensible criterion is: every step needs evidence someone else can re-check — not the agent saying "done."

2. Four layers of verification, and what each one does not prove

This is the table worth remembering. Each lesson tells you which layer you're on:

Layer Who judges Proves Does not prove
Structural validation (scl_validate) The cloud simulator's parser Source parses, structure is legal TIA compile rules, behavior
TIA compile The TIA Portal compiler The block compiles in your project (0 errors, 0 warnings) Behavior matches the spec; the address has a physical channel
Cloud simulation A scan-level simulator Under a given input sequence, output assertions hold (PASS/FAIL) Hardware, bus, HMI, safety circuits; also not the TIA compiler
Virtual PLC / real hardware S7-PLCSIM Advanced / the machine Real scan behavior after download, diagnostics, faults Safety functions, interlocks, e-stops still need human review

A practical detail: scl_validate returning ok: true only means the source parses. The timer call from lesson 1 that omits PT will likely pass structural validation, while TIA reports Parameter 'PT' has to be used.

3. How the seven lessons are ordered

Stage Lesson Tool Time Endpoint (what you walk away with)
Beginner Write SCL: from requirement to 0 errors, 0 warnings TiaLink 30 min A clean compile plus a requirement → implementation → verdict table
Beginner Modify the first example in the library TiaLink 20 min One added tag, one series contact, clean compile, structural read-back
Intermediate Read an undocumented legacy TIA project TiaLink (read-only) 25 min A project specification document + a human review list; zero changes
Intermediate Change one IO address — what breaks? TiaLink (read-only + dryRun) 25 min Reference list, risk list, change plan, preflight result
Intermediate Simulate before commissioning: download to a virtual PLC TiaLink + PLCSIM Advanced 35 min A scenario table with measured values
Intermediate No TIA install: turn SCL into PASS/FAIL in the cloud Cloud simulation MCP 35 min An ALL PASS report plus the scan count
Hands-on Refactor without changing behavior: 3 copies → 1 FB TiaLink + PLCSIM 40 min Baseline vs regression, point by point

About 3.5 hours total. The order isn't by topic — it's the order work actually happens: write → modify → read → assess impact → simulate → verify → regress. Every lesson ends with a verification checklist; if you can't tick it, you're not done.

4. The seven things that actually trip people up

4.1 First compile: 2 errors, 2 warnings

The only input is one requirement: an FB_Sorter with Start / SensorA / SensorB / Reset in, DiverterA / DiverterB / Running out, interlocked, 2-second outputs, reset clears everything. Everything after that — read the project, add a CPU, write the block, import, compile, fix, read back, save — is the agent's job.

Measured on TiaLink 1.1.0.0 + TIA Portal V19 + S7-1200 (CPU 1214C, V4.6). The first version reads like it satisfies the spec, and compiles to 2 errors and 2 warnings:

  • [Error] Parameter 'PT' has to be used. — a timer call must supply PT every time, including the reset branch. The correct shape is one call per scan with reset folded into IN.
  • [Warning] The parameter '#Running' might not be initialized. — the output is read before it is definitely assigned. Carry the state in a Static variable with an initial value and assign the output unconditionally at the end.

Two engineering disciplines hide here as well: every import leaves behind an external source file in the project (they pile up over iterations), and import ≠ save — a successful import is not a saved project.

4.2 Modifying an example: don't import it into a live project

The second lesson practices the more common job: take existing logic and change it. The simplest library example (a single-button toggle, 4 networks in LAD) gets one new input: Enable at %I0.1, forcing the output off when 0 and preserving the toggle when 1.

Two edits total: one new tag, and one series contact after the power rail in Network 2. After a clean compile, structural read-back confirms Motor = Enable AND (Tag_1 OR Motor) AND NOT Tag_2.

Two genuinely annoying traps:

  • UId must be a decimal integer — hex is rejected outright;
  • TIA renumbers UId after import, so you cannot assert on UId; assert on structure instead.

And one precondition: examples ship with Main (OB1) and Default tag table, so importing overwrites same-named objects. Get a clean baseline compile first, so that when something breaks later you know whose fault it is.

4.3 Reading a legacy project: separate confirmed / inferred / unconfirmed

The third lesson is read-only against an MIT-licensed sample project: 5 blocks (OB1 + FB2/FB3 + DB12/DB13), 51 tags, 3 HMI screens with 40 variables. The output is a project specification, with zero changes to the project.

Three classic legacy shapes show up immediately:

  • The CPU is an F-type (1513F-1 PN, 6ES7 513-1FL02-0AB0, firmware V2.9) but there are no F-blocks. That doesn't mean the hardware is wrong, but it has to be listed as a safety item for a human to confirm.
  • An FB with an empty interface is not an empty block: Auto_mode / Manu_mode have no parameters at all; the logic reads and writes global variables (ZS-*, CV-*, HMI_*, SIM_*). Which means: track change impact through variables, not through interfaces.
  • One giant network: Auto_mode has a single network with 36 operands; 2 of Main's 4 networks are empty. Treating that as "one rung" is guaranteed to be wrong — split it into functional segments (sensor chain, actuator chain, alarm/simulation, MATLAB handshake).

One more reconciliation: 6 of the 51 tags are not referenced by any PLC block, and HMI_PID deserves special mention — the HMI has a PID screen, but there is no PID block and the tag is unused. "PID may not be implemented" is a human-confirm item. A document that doesn't distinguish confirmed / inferred / unconfirmed is fiction.

4.4 Change-impact analysis: a clean compile does not mean the field can read it

The fourth lesson answers "can I touch it?" One candidate change: ZS-101 (Infeed Sensor) from %I29.0 to %I29.4. Read-only throughout, with a dryRun preflight at the end.

The result is cleaner than expected — and more dangerous:

  • References: 6 in Auto_mode + 2 in Manu_mode, all reads, and zero absolute addresses inside blocks. So no block needs editing; only the tag table changes. That's what "impact assessed" means.
  • The HMI is symbolic (AddressAccessMode=Symbolic), so the address change follows automatically — but address strings hard-coded in screens or comments are not visible to the compiler and need a human.
  • Hardware mapping: %I29.0 belongs to the ET200SP DI 8x24VDC ST_2 (Input29); I29.0–3 are ZS-101..104, so %I29.4 is a free channel on the same module.
  • The dangerous part: TIA does not validate that the address has a physical channel. Point it at a non-existent address and you still get a clean compile, while the field never sees the signal. Changing an address also means changing the wiring ↔ address mapping — moving a terminal is something only a person or a drawing can confirm.
  • dryRun returns {action: "overwrite", kind: "tagTable", tagCount: 27} — you thought you changed one line; the whole 27-tag table gets rewritten. Without this preflight, you find out after other tags silently revert.

4.5 Virtual PLC: behavior finally becomes visible

The fifth lesson runs the program: the library example "exhaust fan control" (one network, TOF of 1 minute) is downloaded into an S7-PLCSIM Advanced 6.0 instance, driven by a scenario table, with measured values recorded.

The first whole-project compile is 0 errors, 1 warning: Inputs or outputs are used that do not exist in the configured hardware — the 1516 has no onboard I/O, so %I0.0 / %Q0.0 don't exist. Insert a DI and a DQ module (starting at 0) and it's 0/0.

The order is fixed: register instance → power on → wait for Stop → download → RUN. A freshly registered instance is empty, which is why you wait for Stop before downloading. /sim/status returning 403 means exactly two things: TiaLink was started with --no-sim, or PLCSIM Advanced isn't installed.

Four scenarios surface something the compiler can't see and the diagram reads wrong:

Press the stop button and the fan doesn't stop immediately. The TOF input is Turn_On_PB AND NOT Turn_Off_PB — for a TOF, releasing the start button and pressing stop are the same falling edge, so the fan keeps running for another minute either way. Reading the ladder, "stop means stop" is the natural (and wrong) conclusion.

Which changes the deliverable: not "I looked at it, it's fine," but scenario table + measured values + one conclusion.

4.6 Cloud simulation: define the word "trigger"

The sixth lesson takes a lighter path: no TIA, no PLCSIM, no Windows. The same SCL goes to a cloud simulation MCP endpoint, executed scan by scan, turned into PASS/FAIL by a scenario table.

Setup is three steps: sign in, create a sim_live_ API key, add https://tia.hubwiz.com/sim/mcp to any Streamable HTTP MCP client. The endpoint exposes six tools (lad_* / scl_*); this lesson uses scl_validate, scl_simulate, scl_run_scenario.

Billing and fields worth knowing up front:

  • initialize, tools/list, resources/read are free; each accepted tools/call costs one credit. New accounts get 100; this lesson uses roughly 8–12.
  • tickMs defaults to 10 ms, so 100 ticks = 1 second.
  • scl_simulate steps in scans; scl_run_scenario steps in ticks. That's the easiest mistake to make.

The first acceptance run produces the course's signature failure (indented block below — the source page prints it with full scan counts):

PASS  start latches, reset clears (3 step(s), 5 scan(s))
FAIL  sensor A pulse gives a 2 s diverter pulse: step 3 scan 100: DiverterA expected true, got false
FAIL  both sensors: A wins, only one output: step 2 scan 1: DiverterA expected true, got false
RESULT: FAIL (2 failure(s), 109 scan(s))
Enter fullscreen mode Exit fullscreen mode

The same root cause as lesson 1: a TON requires IN to stay true for the full PT, and the sensor is high for exactly one scan. "Trigger, then output for 2 seconds" was implemented as "hold it for 2 seconds, then output" — two different requirements.

Switching to TP (fixed pulse width) passes the first three scenarios, and then a new boundary case fails: a TP pulse is not cut short by IN := FALSE. DiverterA := PulseA.Q AND NOT Reset merely masks the output while the pulse keeps running, so the diverter comes back on when reset is released. The final design is latch + TON to end the pulse: make "currently diverting" an explicit ActiveA/ActiveB state, let Reset clear the state, and interlock on the other side's Active rather than its output.

The ALL PASS run: 8 scenarios + 16 sweep combinations + a 5,000-scan fuzz = 5,735 scan cycles. The report replays identically on local scl-sim 0.1.0 and the hosted endpoint (2026-10-19): same SCL plus the same scenario file yields the same PASS/FAIL and scan counts.

4.7 Refactor with regression: TP typed as TON, clean compile, zero motors

The last lesson covers "change the structure, not the behavior." The target is a three-motor delayed-start sequence: one button opens a 20-second window, three motors start at +5 / +10 / +15 s, and everything stops after the window. The original writes TON + coil three times in one network, differing only in PT and output — classic copy-paste.

The refactor collapses it into one parameterized FB (Enable / Pt in, Q out) instantiated three times, with tag table, symbols and addresses untouched.

The author also crashed once, which is the point: IEC_Timer_0_DB was called as TON instead of TP. Compile: 0 errors, 0 warnings. Press the button: no motor moves. Switching back to TP and replaying the same scenario table brings behavior back point for point.

Which is why the two disciplines matter more than the code:

  • Store a behavior baseline first. Same scenario table, run before and after, compare cell by cell. Refactoring without a baseline means using the plant as a test environment.
  • Trust only output bits and the scenario table. If a structured member (DB.Ton.ET) can't be read, change the evidence — don't fill the gap with "looks right."

5. Who does what: the agent acts, you judge

The course deliberately minimizes the human's actions, because in industrial work the interesting question isn't "can the AI write code" but "which actions have to stay with a person."

Step The agent does You must do
Write SCL Read the project, add a CPU, generate the block, import, compile, self-fix from compiler output, clean up source files, save State the requirement; approve the "report before change" step; spot-check the read-back table
Modify an example Update the tag table, insert a contact, compile, read back structure Confirm the boundary (only Main + tag table); confirm it's not a live project
Read a legacy project Inventory devices/blocks/tags/HMI, export, summarize per block and network, flag unreferenced tags Decide which findings escalate to safety items; review inferred vs unconfirmed
Change impact Scan references, classify read/write and symbolic/absolute, map to hardware channels, dryRun Decide whether to change it; confirm terminals and drawings; confirm saving
Virtual PLC Add device, insert modules, import, compile, register, power on, download, read/write, fill the table Click the TIA authorization prompt; check that "measured" values really came from reads
Cloud simulation Write scenario JSON, run assertions, locate the failing scan Verify the scenario table covers the requirement — especially the definition of "trigger"
Refactor + regression Save the baseline, write the parameterized FB, replace, rerun, diff Decide which blocks must not be "refactored while you're in there"; confirm replacing the original

Short version: actions can be delegated, judgment cannot. Your part boils down to three kinds of moves: state the requirement and the boundary, approve before a change is written and before it is saved, and verify the evidence instead of the claim. The checklists exist to turn "a human took a look" into "a human verified something."

6. Four questions people ask

Do I need TIA Portal installed?
Not for the whole course. One of the seven lessons (cloud PASS/FAIL) never touches TIA — browser, account, and any Streamable HTTP MCP client. The other six need Windows plus TIA Portal V19.

Can TiaLink change my project on its own?
It doesn't save silently. It exposes Openness operations as MCP tools, so writes are explicit: preflight before import, no overwrite of same-named objects by default, no auto-save afterwards. "Import ≠ save" appears more than once in the seven lessons.

If cloud simulation passes, is the program fine?
No. Cloud simulation runs SCL scan semantics only — no bus, HMI, drives, safety circuits, and it is not the TIA compiler. What it is very good at is exactly the "compiles clean, behavior broken" class of bug, like TON where the requirement said "trigger."

Can AI-written blocks go straight into production?
I wouldn't. The course's stance is consistent: AI assists, the compiler and read-back decide, every change gets human review, and safety functions, interlocks and e-stops are not delegated. What ships isn't "AI-written code" — it's code that passed the acceptance chain.

7. Two ways to start

Path A — Windows + TIA Portal V19

  1. Open a sandbox project, start TiaLink on the same machine; the page header auto-detects the connection and project name.
  2. Add a Streamable HTTP MCP server at http://localhost:8080/mcp with a Bearer token; on another machine, replace localhost with the TIA PC's LAN address.
  3. Start with "modify the first example" (20 minutes, smallest diff, fastest verification), then go back to "write SCL to 0/0" (30 minutes).
  4. If the health check flags the allow-list, the first write triggers a TIA authorization dialog that only a human can confirm; running TiaLink.Helper.exe once avoids it.

Path B — no TIA available yet

  1. Open the cloud simulator, sign in with a QR code, create an API key (shown once — don't commit it).
  2. Set the MCP endpoint to https://tia.hubwiz.com/sim/mcp.
  3. Do the "no TIA install" lesson with the same FB_Sorter that compiled clean in path A — and watch it fail twice before it passes.

8. What the course deliberately does not provide

  • No downloads. By design: no files land on disk; import goes through TiaLink with a preflight, and nothing is auto-saved.
  • Cloud simulation only accepts examples or desensitized code. Don't paste a customer project; lessons that touch TIA run entirely on your machine, and project data stays in-house.
  • Passing simulation is not commissioning. PLCSIM only simulates CPU behavior; bus, HMI, drives and safety circuits are out of scope. Cloud simulation is narrower still.
  • Every change needs human review. Safety functions, interlocks, e-stops are not delegated; AI output is "unverified" by default.
  • Version-specific. Everything was measured on TiaLink 1.1.0.0 + TIA Portal V19 + S7-PLCSIM Advanced 6.0. Different versions change error text and tool names — which is exactly why the course insists on structured results instead of matching error strings.

9. Who it's for, and who it isn't

For: engineers who know TIA Portal but don't trust AI-written blocks in a real project; people inheriting a legacy program who want a specification before touching it; anyone training juniors who needs a checkable practice path; technical leads deciding how far AI can go.

Not for: people who have never opened TIA Portal (start with the 60-example ladder library); anyone looking for "one-click ladder generation, download the project"; anyone who wants to skip human review and safety validation.

10. Takeaway

Read together, the seven lessons aren't about how much the AI can write. They describe an auditable delivery chain:

write → compile → read back → assess impact → simulate → PASS scenarios → regression shows behavior unchanged.

Every step has a criterion that doesn't depend on the AI claiming success. That's the part worth copying, even if you never use TiaLink or the cloud simulator: put evidence at every step, and AI actually gets to enter an engineering workflow.

Start with the 20-minute modify-an-example lesson, or the 35-minute cloud PASS/FAIL lesson.


Verification notes

  • Course structure, lesson steps and all measured numbers (2 errors / 2 warnings, 36 operands, 51 tags with 6 unreferenced, dryRun rewriting 27 tags, 5,735 scan cycles, 1513F-1 PN, 1516-3 PN/DP) come from the official course pages at tia.hubwiz.com/learn-lad/ai/ and its seven sub-pages, checked on 2026-10-20.
  • The stated test environment is TiaLink 1.1.0.0 + TIA Portal V19 + S7-PLCSIM Advanced 6.0; the cloud simulation report replays identically on scl-sim 0.1.0 and the hosted endpoint (2026-10-19).
  • This article does not independently reproduce the measurements; numbers and conclusions follow the official pages and may change with tool versions.

Further reading (all in Chinese)

This article was originally published in Chinese on hubwiz.com.

Top comments (0)